Join tables with rows - sql

I am trying to join three tables in SQL Server 2008 R2, where I want the items in the second table to be added as new column.
To explain in detail - I have 3 tables:
First table contains User Name and User ID
UserID UserName
1 Mike
2 John
3 George
Second Table is position ID's with Position Names
PositionID PositionName
1 RW
2 LW
3 DF
4 MDF
5 SS
6 CF
etc
Third table table contains their preferred positions where one user can have more than one
UserID PositionId
1 1
1 3
2 2
2 3
2 5
3 2
3 7
When I join these tables I want to get single row for every user with all the preferred positions like
UserID UserName PreferedPosition PreferedPosition2 PreferedPosition3
1 Mike RW LW
2 John CMF SS CF
3 George LW MDF
I don't know how to achieve this, any help would be appreciated.

If you have only a few numbers of positions, you can do it with PIVOT keyword
select
UserID,
UserName,
[1] as Position1,
[2] as Position2,
[3] as Position3
from
(
select
U.UserID, U.UserName, P.PositionName,
row_number() over (partition by U.UserID order by P.PositionName) as RowNum
from Positions_Users as PU
inner join Positions as P on P.PositionID = PU.PositionID
inner join Users as U on U.UserID = PU.UserID
) as P
pivot
(
min(P.PositionName)
for P.RowNum in ([1], [2], [3])
) as PIV
SQL FIDDLE
If, however, you want to have a dynamic number of columns, you have to use dynamic SQL, like this
declare #stmt nvarchar(max), #stmt_columns1 nvarchar(max), #stmt_columns2 nvarchar(max)
declare #Temp_Data table (RowNum nvarchar(max))
insert into #Temp_Data
select distinct row_number() over (partition by U.UserID order by P.PositionName) as RowNum
from Positions_Users as PU
inner join Positions as P on P.PositionID = PU.PositionID
inner join Users as U on U.UserID = PU.UserID
select #stmt_columns1 = stuff((select ', [' + RowNum + ']' from #Temp_Data for xml path(''), type).value('.', 'nvarchar(max)'), 1, 2, '')
select #stmt_columns2 = stuff((select ', [' + RowNum + '] as Position' + RowNum from #Temp_Data for xml path(''), type).value('.', 'nvarchar(max)'), 1, 2, '')
select #stmt = '
select
UserID,
UserName,' + #stmt_columns2 + '
from
(
select
U.UserID, U.UserName, P.PositionName,
row_number() over (partition by U.UserID order by P.PositionName) as RowNum
from Positions_Users as PU
inner join Positions as P on P.PositionID = PU.PositionID
inner join Users as U on U.UserID = PU.UserID
) as P
pivot
(
min(P.PositionName)
for P.RowNum in (' + #stmt_columns1 + ')
) as PIV'
exec sp_executesql #stmt = #stmt
SQL FIDDLE

Related

How to join rows in some table in SQL Server?

i want join some table then concatenation columns
MyTable
-------------------------------
ID RowId LangId Caption
-------------------------------
1 1 1 ڕۆشتن
2 1 2 Go
3 1 3 اذهب
4 2 1 ئاو
5 2 2 water
6 2 3 ماء
I want join concatenation Caption column ex: for RowId 1 'ڕۆشتن - Go - اذهب'
Desired output
--------------------
RowId Caption
--------------------
1 ڕۆشتن - Go - اذهب
2 ئاو- water - ماء
I seen link but can't help me
You can use string_agg():
select rowid,
string_agg(caption, ' ') within group (order by langid) as caption
from t
group by rowid;
You can use for xml for older vresion :
select r.rowid,
stuff( (select ' - '+t.caption
from table t
where t.rowid = r.rowid
order by t.LangId
for xml path('')
), 1, 1, ''
) as Caption
from (select distinct rowid from table ) r;
You can use string_agg() for newer version SQL Server 17+ :
select t.rowid,
string_agg(t.caption, ' ') within group (order by t.langid) as caption
from table t
group by t.rowid;
You can try the following query using for xml as shown below.
SELECT DISTINCT t.RowId,
STUFF((SELECT distinct ' - ' + t1.[Caption]
FROM
(
SELECT t.[RowId],
t2.Caption
FROM [TestTable] AS t
INNER JOIN [TestTable] AS t2 ON t2.[RowId] = t.[RowId]
)
t1
WHERE t.RowId = t1.RowId
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'') CaptionList
FROM
(
SELECT t.[RowId],
por.Caption
FROM [TestTable] AS t
INNER JOIN [TestTable] AS por ON por.RowId = t.RowId
)t;
Here is the live db<>fiddle demo.
Try this:
SELECT DISTINCT RowID,
STUFF((SELECT DISTINCT Sub.caption +' - '
FROM #tab Sub
WHERE Sub.rowid = Main.rowid
FOR XML PATH('')), 1, 1, '') AS Caption
FROM #tab Main
I think this Query can perfectly fit for your case.

SQL Server 2012 Dynamic Pivot with a Join [duplicate]

I have a table of Customers
Customer ID Name
1 John
2 Lewis
3 Mary
I have another table CustomerRewards
TypeID Description
1 Bronze
2 Silver
3 Gold
4 Platinum
5 AnotherOne
And the final table
RewardID TypeID CustomerID
1 1 1
2 1 1
3 2 1
4 2 2
The customerTypes table is dynamic, many of these types can be added and removed. Basically all I want is the columns to be generated dynamically and a count in each, something like
CustomerName Bronze Silver Gold Platinum AnotherOne total
John 2 1 0 0 0 3
Lewis 0 1 0 0 0 1
Grand TOTAL 2 2 0 0 0 4
The problem like I said it that the types are dynamic and the customers are dynamic so I need the columns to be dynamic depending on the types in the system
I have tagged c# as I need this in a DataGridView
Thanks in advance
You will want to use a PIVOT function for this. If you have a known number of columns, then you can hard-code the values:
select name, [Bronze], [Silver], [Gold], [Platinum], [AnotherOne]
from
(
select c.name,
cr.description,
r.typeid
from customers c
left join rewards r
on c.id = r.customerid
left join customerrewards cr
on r.typeid = cr.typeid
) x
pivot
(
count(typeid)
for description in ([Bronze], [Silver], [Gold], [Platinum], [AnotherOne])
) p;
See SQL Fiddle with Demo.
Now if you have an unknown number of columns, then you can use dynamic SQL to PIVOT:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(description)
from customerrewards
group by description, typeid
order by typeid
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT name,' + #cols + ' from
(
select c.name,
cr.description,
r.typeid
from customers c
left join rewards r
on c.id = r.customerid
left join customerrewards cr
on r.typeid = cr.typeid
) x
pivot
(
count(typeid)
for description in (' + #cols + ')
) p '
execute(#query)
See SQL Fiddle With Demo
If you need to include the Total column, then you can use ROLLUP (Static Version Demo):
select name, sum([Bronze]) Bronze, sum([Silver]) Silver,
sum([Gold]) Gold, sum([Platinum]) Platinum, sum([AnotherOne]) AnotherOne
from
(
select name, [Bronze], [Silver], [Gold], [Platinum], [AnotherOne]
from
(
select c.name,
cr.description,
r.typeid
from customers c
left join rewards r
on c.id = r.customerid
left join customerrewards cr
on r.typeid = cr.typeid
) x
pivot
(
count(typeid)
for description in ([Bronze], [Silver], [Gold], [Platinum], [AnotherOne])
) p
) x
group by name with rollup
Dynamic version (Demo):
DECLARE #cols AS NVARCHAR(MAX),
#colsRollup AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(description)
from customerrewards
group by description, typeid
order by typeid
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
select #colsRollup
= STUFF((SELECT ', Sum(' + QUOTENAME(description) + ') as '+ QUOTENAME(description)
from customerrewards
group by description, typeid
order by typeid
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query
= 'SELECT name, '+ #colsRollup + '
FROM
(
SELECT name,' + #cols + ' from
(
select c.name,
cr.description,
r.typeid
from customers c
left join rewards r
on c.id = r.customerid
left join customerrewards cr
on r.typeid = cr.typeid
) x
pivot
(
count(typeid)
for description in (' + #cols + ')
) p
) x1
GROUP BY name with ROLLUP'
execute(#query)

SQL, questions about join

I have two tables in sql 2012: name and prod with structure:
name: id int increment, name1 nvarchar(50)
prod: id int increment, products nvarchar(50), id_name int
Values for table are:
name table:
Id name1
1 pop
2 andi
prod table:
Id products id_name
1 coke 1
2 pizza 1
3 orange 2
I have done this query:
select name.name1, prod.product, prod.id_name
from name
join prod on name.id=prod.id_name
How can I obtain this result:
pop ->coke, pizza
andi->orange
unfortunately, there's no easy way to do it in SQL Server. Known solutions are:
xml trick (see below);
using variable to accumulate data (don't work for multiple group rows, only with cursor);
custom CLR aggregate;
here's xml:
select
n.name1,
stuff(
(
select ', ' + p.product
from prod as p
where p.id_name = n.id
for xml path(''), type).value('.', 'nvarchar(max)')
, 1, 2, '') as products
from name as n
sql fiddle demo
here's variable:
declare #product nvarchar(max), #id int
select #id = 1
select #product = isnull(#product + ', ', '') + product
from prod
where id_name = #id
select name1, #product as products
from name
where id = #id
sql fiddle demo
try this:
SELECT
G.id,
G.name1,
stuff(
(select cast(',' as varchar(10)) + U.product
from prod U
WHERE U.id_name = G.id
order by U.product
for xml path('')
), 1, 1, '') AS prod
FROM name G
ORDER BY G.name1 ASC
sqlfiddle
select
n.nameid [id],
n.name [name],
count(*)[count],
stuff(
(
select ', ' + p.prod
from prodtbl as p
where p.nameid = n.nameid
for xml path(''), type).value('.', 'nvarchar(max)'), 1, 1, '') as products
from nametbl n, prodtbl p
where p.nameid = n.nameid
group by n.nameid, n.name
order by [id];

T:SQL: select values from rows as columns

I have a table for Profiles stores profile properties values in row style, ex:
[ProfileID] [PropertyDefinitionID] [PropertyValue]
1 6 Jone
1 7 Smith
1 8 Mr
1 3 50000
and another table for property definitions :
[PropertyDefinitionID] [PropertyName]
6 FirstName
7 LastName
8 Prefix
3 Salary
How to use PIVOT or any other way to show it in this way:
[ProfileID] [FirstName] [LastName] [Salary]
1 Jone Smith 5000
It's easy to do this without PIVOT keyword, just by grouping
select
P.ProfileID,
min(case when PD.PropertyName = 'FirstName' then P.PropertyValue else null end) as FirstName,
min(case when PD.PropertyName = 'LastName' then P.PropertyValue else null end) as LastName,
min(case when PD.PropertyName = 'Salary' then P.PropertyValue else null end) as Salary
from Profiles as P
left outer join PropertyDefinitions as PD on PD.PropertyDefinitionID = P.PropertyDefinitionID
group by P.ProfileID
you can also do this with PIVOT keyword
select
*
from
(
select P.ProfileID, P.PropertyValue, PD.PropertyName
from Profiles as P
left outer join PropertyDefinitions as PD on PD.PropertyDefinitionID = P.PropertyDefinitionID
) as P
pivot
(
min(P.PropertyValue)
for P.PropertyName in ([FirstName], [LastName], [Salary])
) as PIV
UPDATE: For dynamic number of properties - take a look at Increment value in SQL SELECT statement
It looks like you might have an unknown number of PropertyName's that you need to turn into columns. If that is the case, then you can use dynamic sql to generate the result:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(PropertyName)
from propertydefinitions
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT profileid, ' + #cols + ' from
(
select p.profileid,
p.propertyvalue,
d.propertyname
from profiles p
left join propertydefinitions d
on p.PropertyDefinitionID = d.PropertyDefinitionID
) x
pivot
(
max(propertyvalue)
for propertyname in (' + #cols + ')
) p '
execute(#query)
See SQL Fiddle with Demo.

Dynamically create columns sql

I have a table of Customers
Customer ID Name
1 John
2 Lewis
3 Mary
I have another table CustomerRewards
TypeID Description
1 Bronze
2 Silver
3 Gold
4 Platinum
5 AnotherOne
And the final table
RewardID TypeID CustomerID
1 1 1
2 1 1
3 2 1
4 2 2
The customerTypes table is dynamic, many of these types can be added and removed. Basically all I want is the columns to be generated dynamically and a count in each, something like
CustomerName Bronze Silver Gold Platinum AnotherOne total
John 2 1 0 0 0 3
Lewis 0 1 0 0 0 1
Grand TOTAL 2 2 0 0 0 4
The problem like I said it that the types are dynamic and the customers are dynamic so I need the columns to be dynamic depending on the types in the system
I have tagged c# as I need this in a DataGridView
Thanks in advance
You will want to use a PIVOT function for this. If you have a known number of columns, then you can hard-code the values:
select name, [Bronze], [Silver], [Gold], [Platinum], [AnotherOne]
from
(
select c.name,
cr.description,
r.typeid
from customers c
left join rewards r
on c.id = r.customerid
left join customerrewards cr
on r.typeid = cr.typeid
) x
pivot
(
count(typeid)
for description in ([Bronze], [Silver], [Gold], [Platinum], [AnotherOne])
) p;
See SQL Fiddle with Demo.
Now if you have an unknown number of columns, then you can use dynamic SQL to PIVOT:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(description)
from customerrewards
group by description, typeid
order by typeid
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT name,' + #cols + ' from
(
select c.name,
cr.description,
r.typeid
from customers c
left join rewards r
on c.id = r.customerid
left join customerrewards cr
on r.typeid = cr.typeid
) x
pivot
(
count(typeid)
for description in (' + #cols + ')
) p '
execute(#query)
See SQL Fiddle With Demo
If you need to include the Total column, then you can use ROLLUP (Static Version Demo):
select name, sum([Bronze]) Bronze, sum([Silver]) Silver,
sum([Gold]) Gold, sum([Platinum]) Platinum, sum([AnotherOne]) AnotherOne
from
(
select name, [Bronze], [Silver], [Gold], [Platinum], [AnotherOne]
from
(
select c.name,
cr.description,
r.typeid
from customers c
left join rewards r
on c.id = r.customerid
left join customerrewards cr
on r.typeid = cr.typeid
) x
pivot
(
count(typeid)
for description in ([Bronze], [Silver], [Gold], [Platinum], [AnotherOne])
) p
) x
group by name with rollup
Dynamic version (Demo):
DECLARE #cols AS NVARCHAR(MAX),
#colsRollup AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(description)
from customerrewards
group by description, typeid
order by typeid
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
select #colsRollup
= STUFF((SELECT ', Sum(' + QUOTENAME(description) + ') as '+ QUOTENAME(description)
from customerrewards
group by description, typeid
order by typeid
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query
= 'SELECT name, '+ #colsRollup + '
FROM
(
SELECT name,' + #cols + ' from
(
select c.name,
cr.description,
r.typeid
from customers c
left join rewards r
on c.id = r.customerid
left join customerrewards cr
on r.typeid = cr.typeid
) x
pivot
(
count(typeid)
for description in (' + #cols + ')
) p
) x1
GROUP BY name with ROLLUP'
execute(#query)