SQL, questions about join - sql

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];

Related

SQL - Multiple records in one row

I've trying to create a script that allows me to display multiples records in a one-row, group by a specific column. Below is the code that I worked on. I've done it in Power BI but now I need it on SQL Someone could help me how can I fix this, I appreciate:
MY CODE:
B.WorkOrderCode,
STUFF((SELECT '; ' + A.UserName
FROM [PanatrackerGP].[dbo].[User] AS A
WHERE A.ProfileOid = B.ProfileOid
FOR XML PATH('')), 1, 1, '') [USERS]
FROM [PanatrackerGP].[dbo].[TrxIssueInventory] AS B
WHERE B.WorkOrderCode = 'S12119'
GROUP BY B.ProfileOid, B.WorkOrderCode
ORDER BY 1
------------------------------ OUTPUT ----------------------------------------
WorkOrderCode | USERS
S12119 | GM; FM; FO; GR; RG; TI
S12119 | NC; BS; DNA; CS; JMAGGI; mj; fa; LR; lgm; MS; JPU
-----------------------------------------------------------------------
Only these users should be shown
------------------------------TrxIssueInventory[TABLE]-----------------
WorkOrderCode | CreateUserName
S12119 | FO
S12119 | lgm
-----------------------------------------------------------------------
-----------------------------GOAL OUTPUT---------------------------------
WorkOrderCode | CreateUserName
S12119 | FO ; lgm
-----------------------------------------------------------------------
Thank You
You have to do an additional join within your correlated subquery:
SELECT b.WorkOrderCode,
CreateUserName = STUFF((SELECT CONCAT(';', u.UserName)
FROM [dbo].[User] AS u
INNER JOIN [dbo].[TrxIssueInventory] AS i
ON i.ProfileOid = u.ProfileOid
WHERE i.WorkOrderCode = b.WorkOrderCode
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(1000)'), 1, 1, '')
FROM [dbo].[TrxIssueInventory] AS b
GROUP BY b.WorkOrderCode;
I've also amended your concatenation method slightly to use FOR XML PATH(), TYPE rather than just for XML path as this properly handles special XML Characters. If you had a username that was LGM > this would end up as LGM >, if you don't use TYPE and '.value`.
WORKING DEMO
IF OBJECT_ID(N'tempdb..#TrxIssueInventory', 'U') IS NOT NULL
DROP TABLE #TrxIssueInventory;
IF OBJECT_ID(N'tempdb..#User', 'U') IS NOT NULL
DROP TABLE #User;
CREATE TABLE #TrxIssueInventory (WorkOrderCode INT, ProfileOid INT);
INSERT #TrxIssueInventory VALUES (12119, 1), (12119, 2);
CREATE TABLE #User (ProfileOid INT, UserName VARCHAR(10));
INSERT #User VALUES (1, 'FO'), (2, 'LGM >');
SELECT b.WorkOrderCode,
CreateUserName = STUFF((SELECT CONCAT(';', u.UserName)
FROM #User AS u
INNER JOIN #TrxIssueInventory AS i
ON i.ProfileOid = u.ProfileOid
WHERE i.WorkOrderCode = b.WorkOrderCode
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(1000)'), 1, 1, '')
FROM #TrxIssueInventory AS B
GROUP BY b.WorkOrderCode;
-- DON'T USE, JUST HERE TO DEMONSTRATE THE ISSUE OF NOT USING 'TYPE'
SELECT b.WorkOrderCode,
CreateUserName = STUFF((SELECT CONCAT(';', u.UserName)
FROM #User AS u
INNER JOIN #TrxIssueInventory AS i
ON i.ProfileOid = u.ProfileOid
WHERE i.WorkOrderCode = b.WorkOrderCode
FOR XML PATH('')
), 1, 1, '')
FROM #TrxIssueInventory AS B
GROUP BY b.WorkOrderCode
STUFF(
(
SELECT DISTINCT '; ' + CAST(CreateUserName AS VARCHAR (MAX))
FROM [PanatrackerGP].[dbo].[TrxIssueInventory]
WHERE (WorkOrderCode = A.WorkOrderCode)
FOR XML PATH ('')
),1,2,''
) AS [USERS]
FROM [PanatrackerGP].[dbo].[TrxIssueInventory] AS A
WHERE WorkOrderCode = 'S12119'
GROUP BY WorkOrderCode
------------------OUTPUT-----------------------
WorkOrderCode | USERS
S12119 | FO; lgm
Thank you all!

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.

using recursive function to simulate group_concat

I have a table like
ID NAME
1 STALIN
2 MERWIN
1 AMALA
1 RAYON
i want the out as
ID NAME
1 STALIN,AMALA,RAYON
2 MERWIN
How can do this using recursive function
You can do this using XML Path
SELECT ID, NAME =
STUFF((SELECT ', ' + NAME
FROM your_table b
WHERE b.ID = a.ID
FOR XML PATH('')), 1, 2, '')
FROM your_table a
GROUP BY ID
Live Demo
Follow this link and see the case 3. it explains why to use XML path over recursive cursor.
You can use FOR XML PATH to get the result:
for example:
DECLARE #EXAMPLE TABLE (ID int, NAME nvarchar(10))
INSERT #EXAMPLE VALUES (1,'STALIN'),(2,'MERWIN'),(1,'AMALA'),(1,'RAYON')
SELECT
ID,
SUBSTRING(NAME,0,LEN(NAME)) AS NAME
FROM ( SELECT DISTINCT
T1.ID,
STUFF(( SELECT DISTINCT '' + t2.NAME + ','
FROM #EXAMPLE T2
WHERE T1.ID = T2.ID
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)')
,1,0,'') NAME
FROM #EXAMPLE t1) AS info

Join tables with rows

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

Duplicates without using While or Cursor in T-SQL

ID Name
1 A
1 B
1 C
2 X
2 Y
3 P
3 Q
3 R
These are the columns in a table. I want to get output like
ID Company
1 A,B,C
2 X, Y
3 P,Q,R
Restriction is that I cannot use WHILE or CURSOR. Please write a query for the same.
This query should do it - uses FOR XML PATH which is new in SQL Server 2005 - hope you are on 2005 or higher, you didn't clearly specify.....
SELECT
ID,
STUFF(CAST((SELECT ','+Name FROM dbo.YourTable t2
WHERE t2.ID = dbo.YourTable.ID
FOR XML PATH(''), TYPE) AS VARCHAR(MAX)), 1, 1, '') AS 'Company'
FROM
dbo.YourTable
GROUP BY
ID
Here's a solution using the CROSS APPLY method:
select id, sub.names
from (
select distinct id from YourTable
) a
cross apply (
select name + ', ' as [text()]
from YourTable b
where b.id = a.id
for xml path('')
) sub(names)
For 2005 version:
CREATE TABLE dbo.TEST([Type] INTEGER, [Name] NVARCHAR(100), [Qty] INTEGER)
GO
INSERT dbo.TEST VALUES(1, N'a', 5)
INSERT dbo.TEST VALUES(1, N'b', 6)
INSERT dbo.TEST VALUES(2, N'c', 44)
INSERT dbo.TEST VALUES(3, N'd', 1)
GO
select [Type],
[Description] = replace((select [Name] + ':' + cast([Qty] as varchar) as 'data()'
from TEST where [Type] = t.[Type] for xml path('')), ' ', ',')
from dbo.TEST t
group by [Type]
go
drop table dbo.TEST
You can group on the ID to get the unique values, then get the comma separated string for each using a for xml query:
select
a.ID,
substring((
select ', ' + Name
from Test1
where Test1.ID = a.ID
for xml path('')
), 3, 1000) as Company
from
TheTable a
group by
a.ID