SQL Using PIVOT for multi valued attributes - sql-server

I designed an EAV table that looks like this:
SID AID VID
1 1 1
1 2 1
1 3 2
1 4 3
1 1 2
SID stand for Subject ID, AID stands for Attribute ID and VID stands for ValuedID
also a table to map the attributes:
AttributeID AttributeName
1 Hobbies
2 Name
3 Gender
4 IrisColor
After using pivot on the first table, linked to the attribute table:
SELECT
SubjectID,
Hobbies,
Name,
Gender,
IrisColor
FROM
(
SELECT SubjectID, attr.AttributeName as attribute, ValueID from SubjectDetails, SubjectAttributes as attr WHERE SubjectDetails.AttributeID=attr.ID
) as t
PIVOT(
MAX(ValueID)
FOR attribute IN (Hobbies,Name,Gender,IrisColor)) AS t1
WHERE SubjectID=1
I get this:
SubjectID Hobbies Name Gender IrisColor
1 1 1 2 3
Which is almost correct, but SubjectAttribute 1 (which is hobbies) appears one more time in the first table (SubjectDetails), so what I want to achieve is this:
SubjectID Hobbies Name Gender IrisColor
1 **1,2** 1 2 3
I have to mention that I don't care about what separator is used and that I tried doing that with the STUFF function but it is a pain to combine PIVOT and STUFF (or I just don't know how).. Any suggestions?

This should work, I did the following:
Stored the information from your EAV table (table1) as single row per SID into a temporary table (you can create a view instead).
Then pivoted that resultset as below (using your pivot query):
SELECT *
FROM
(
SELECT * from #temptbl
) as t
PIVOT( MAX(vid) FOR attrname IN (Hobbies,Name,Gender,IrisColor)) AS t1
WHERE sid=1
I got this result:
Please check the full working version here.

This would work without using PIVOT, I think here no need to use PIVOT
SELECT SubjectID
,+STUFF((SELECT ', '+CAST(ValueID AS NVARCHAR)
FROM #T_SubjectAttributes A
INNER JOIN #T_SubjectDetails B ON A.AttributeID = B.AttributeId
WHERE AttributeName = 'Hobbies'
AND B.SubjectID = H.SubjectID FOR XML PATH(''))
,1,2,'') AS Hobbies
,STUFF((SELECT ', '+CAST(ValueID AS NVARCHAR)
FROM #T_SubjectAttributes A
INNER JOIN #T_SubjectDetails B ON A.AttributeID = B.AttributeId
WHERE AttributeName = 'Name'
AND B.SubjectID = H.SubjectID FOR XML PATH(''))
,1,2,'') AS Name
,STUFF((SELECT ', '+CAST(ValueID AS NVARCHAR)
FROM #T_SubjectAttributes A
INNER JOIN #T_SubjectDetails B ON A.AttributeID = B.AttributeId
WHERE AttributeName = 'Gender'
AND B.SubjectID = H.SubjectID FOR XML PATH(''))
,1,2,'') AS Gender
,STUFF((SELECT ', '+CAST(ValueID AS NVARCHAR)
FROM #T_SubjectAttributes A
INNER JOIN #T_SubjectDetails B ON A.AttributeID = B.AttributeId
WHERE AttributeName = 'IrisColor'
AND B.SubjectID = H.SubjectID FOR XML PATH(''))
,1,2,'') AS IrisColor
FROM #T_SubjectDetails H
GROUP BY SubjectID

Related

SQL Server - Concatenate 3 different tables matching correspondent ID's of those columns

I've been on this for hours trying to figure this one out. So I need to concatenate values in a single column based on its ID's.
I have 3 tables named Type, DeliverType and Platform. I need to get all the matching ID's in the table Platform and concatenate all of its values in another column of table Platform.
Example :
Table Type Table DeliverType Table Platform
----------------------------------------------------------------------------------------
ID | Type ID | DeliverType ID | TypeID | DeliverType
----------------------------------------------------------------------------------------
1 | TestType1 1 | DeliverType1 9 | 3 | 1
2 | TestType2 2 | DeliverType2 9 | 4 | 2
3 | TestType3 3 | DeliverType3 23 | 2 | 4
4 | TestType4 4 | DeliverType4 23 | 1 | 3
These are my tables , I want to return the name of Type, the name of DeliverType and the ID of Platform
Expected Result
Table Platform
ID | NamesConcat
9 | TestType1,TestType2,TestType3,DeliverType1,DeliverType2,9
23 | TestType2,TesType3,TestType4,DeliverType3,DeliverType4,23
So it has only 1 ID which is 9 and all of the testTypes and DeliverTypes correspondant to that ID in table platform needs to be concat.
Here's my code so far:
SELECT DISTINCT p.ID,
SUBSTRING(
(
SELECT ','+ d.DeliverType,a.Type AS [text()]
FROM dbo.DeliverType d
INNER JOIN dbo.Type a ON a.ID = p.TypeID
ORDER BY d.ID
FOR XML PATH ('')
), 2, 1000) [NamesConcat]
FROM dbo.Platform p
Would appreciate some help on this one fellow programmers.
Thank you very much in advance.
From the sample data you show, I don't get the same expected result. I would expect the result:
Table Platform
ID
NamesConcat
9
TestType3,TestType4,DeliverType1,DeliverType2,9
23
TestType1,TestType2,DeliverType3,DeliverType4,23
If that is indeed the expected result and you want the strings concatenated by the Type first and then by DeliverType you can do that with this query:
SELECT DISTINCT pmain.ID,
(
SELECT t.[Type] + ','
FROM Platform p
INNER JOIN Type t ON t.ID = p.TypeID
WHERE p.ID = pmain.ID
ORDER BY t.ID
FOR XML PATH('')
) +
(
SELECT d.DeliverType + ','
FROM Platform p
INNER JOIN DeliverType d ON d.ID = p.DeliverTypeID
WHERE p.ID = pmain.ID
ORDER BY p.ID
FOR XML PATH('')
) +
CAST(pmain.ID AS VARCHAR(10)) AS NamesConcat
FROM Platform pmain
If you don't need the concatenated values in that order, you could do it with a shorter query:
SELECT DISTINCT pmain.ID,
(
SELECT d.DeliverType + ',' + t.[Type] + ','
FROM Platform p
INNER JOIN DeliverType d ON d.ID = p.DeliverTypeID
INNER JOIN Type t ON t.ID = p.TypeID
WHERE p.ID = pmain.ID
FOR XML PATH('')
) + CAST(pmain.ID AS VARCHAR(10)) AS NamesConcat
FROM Platform pmain
So that you can check the work, here is a simple script that I wrote to mimic your demo data and the result:
DECLARE #Type AS TABLE (
ID INT IDENTITY(1,1) NOT NULL,
[Type] VARCHAR(20) NOT NULL
);
INSERT INTO #Type([Type]) VALUES('TestType1'), ('TestType2'), ('TestType3'), ('TestType4');
DECLARE #DeliverType AS TABLE (
ID INT IDENTITY(1, 1) NOT NULL,
[DeliverType] VARCHAR(25) NOT NULL
);
INSERT INTO #DeliverType([DeliverType]) VALUES ('DeliverType1'), ('DeliverType2'), ('DeliverType3'), ('DeliverType4');
DECLARE #Platform AS TABLE (
ID INT NOT NULL,
TypeID INT NOT NULL,
DeliverTypeID INT NOT NULL
);
INSERT INTO #Platform(ID, TypeID, DeliverTypeID) VALUES (9, 3, 1), (9, 4, 2), (23, 2, 4), (23, 1, 3);
SELECT DISTINCT pmain.ID,
(
SELECT d.DeliverType + ',' + t.[Type] + ','
FROM #Platform p
INNER JOIN #DeliverType d ON d.ID = p.DeliverTypeID
INNER JOIN #Type t ON t.ID = p.TypeID
WHERE p.ID = pmain.ID
FOR XML PATH('')
) + CAST(pmain.ID AS VARCHAR(10)) AS NamesConcat
FROM #Platform pmain
SELECT DISTINCT pmain.ID,
(
SELECT t.[Type] + ','
FROM #Platform p
INNER JOIN #Type t ON t.ID = p.TypeID
WHERE p.ID = pmain.ID
ORDER BY t.ID
FOR XML PATH('')
) +
(
SELECT d.DeliverType + ','
FROM #Platform p
INNER JOIN #DeliverType d ON d.ID = p.DeliverTypeID
WHERE p.ID = pmain.ID
ORDER BY p.ID
FOR XML PATH('')
) +
CAST(pmain.ID AS VARCHAR(10)) AS NamesConcat
FROM #Platform pmain
I hope this is what you were looking for!

I want to display records for one ID if there is more NUMBER for the same ID

For the first ID which is 1265 I want only one record like this(look below), and for now I have three different records for the same ID and that isn't good. You can see that in the first record with ID = 1265 I would like to have concated strings into one separated with ',':
1265 MARK, LISA, MAY
Here is my code sample:
select
vup.UgovorId as ID,
concat(p.Naziv, p.Naziv) as NUMBER
from
[TEST_Ugovori_Prod].dbo.VezaUgovorPartner as vup
inner join
[TEST_MaticniPodaci2].[dbo].[Partner] as p on vup.PartnerId = p.PartnerID
group by
vup.UgovorId, p.Naziv
order by
vup.UgovorId desc
Here are my results:
1265 MARK
1265 LISA
1265 MAY
1264 LINDA
1263 MARTINA
1262 MARKO
1261 VIDAKS
1260 PEEKS
1259 MARCUS
1258 MARKO
1257 MATIA
1256 POPOVIC
SELECT ID, NUMBER =
STUFF((SELECT DISTINCT ', ' + NUMBER
FROM example t2
WHERE t2.ID = t1.ID
FOR XML PATH('')), 1, 2, '')
FROM example t1
GROUP BY ID
ORDER BY ID DESC
[DEMO HERE]
If you are having trouble blending with your query above, this should help:
SELECT vup.UgovorId as ID,
STUFF((SELECT DISTINCT ', ' + p2.Naziv
FROM [TEST_Ugovori_Prod].dbo.VezaUgovorPartner vup2
INNER JOIN [TEST_MaticniPodaci2].[dbo].[Partner] p2 ON vup2.PartnerId = p2.PartnerID
WHERE vup2.UgovorId = vup.UgovorId
FOR XML PATH('')), 1, 2, '') NUMBER
FROM [TEST_Ugovori_Prod].dbo.VezaUgovorPartner vup
INNER JOIN [TEST_MaticniPodaci2].[dbo].[Partner] p on vup.PartnerId = p.PartnerID
GROUP BY vup.UgovorId
ORDER BY vup.UgovorId DESC
[Test blend]

MS SQL Server - Join two tables where ID is equivalent to new ID

I am trying to join two tables but did not able to success
Test Supplier Table
SID NAME
1 Test
2 Test2
Test Stock Table
ID NewID SupID Qty
1 101 1 2
2 102 1 5
3 103 2 6
101 1 4
101 1 7
101 2 5
103 2 10
The output I am looking for
ID NAME Qty
2 Test 5
101 Test 13
101 Test2 5
103 Test2 16
My code is -
Select S.NAME, ST.ID, SUM(ST.Qty)
From Stock ST
Inner Join ST.SupID = S.SID
I need to combine those ID's which are matching with the new ID's with another ID's. If you see the results, I need to combine ID 1 qty with ID 101 because ID 1 has new ID 101 and no need to display ID 1. I have tried inner join but did not work.
First, you find those with NEW ID and those without NEW ID. For those with NewID, use NEWID, for those without use ID (old ID). then you use UNION ALL to combine both result and join to the Supplier table to obtain the NAME.
; with
cte as
(
-- with NewID
select ID = NewID, SupID, Qty = sum(Qty)
from Stock ST
where exists
(
select *
from Stock x
where x.ID = ST.NewID
)
group by NewID, SupID
union all
-- without NewID
select ID, SupID, Qty = sum(Qty)
from Stock ST
where not exists
(
select *
from Stock x
where x.ID = ST.NewID
)
group by ID, SupID
)
select c.ID, SP.NAME, Qty = sum(Qty)
from cte c
inner join Supplier SP on c.SupID = SP.SID
group by c.ID, SP.NAME
Start with the Stock table and join to the Supplier table (remembering to name the table in the join) and then self-left join to the IDs in the Stock table to determine if they exist or not. Then group by on whichever ID you want to keep.
SELECT
COALESCE(ST2.ID, ST.ID) ID
, S.NAME NAME
, SUM(ST.Qty) Qty
FROM Stock ST
INNER JOIN Supplier S
ON ST.SupID = S.SID
LEFT JOIN (
SELECT DISTINCT ID
FROM Stock
) ST2
ON ST.NewID = ST2.ID
GROUP BY
COALESCE(ST2.ID, ST.ID)
, S.NAME

SQL Server - CSV values against CSV id

I have 2 tables, TableA in which I have ID and Name columns
id name
1 Turret Punch Press
2 Laser Machine
3 Press Brake
4 Other machines
and Table B in which I am passing Id of Table A as CSV value like this
id TableACSV
1 1,2
2 1,3
My query will be something like
Select
id, TableACSV
from tableB
where (--here i am having problem with query--)
The output I want should be like this
id TableACSV
1 Turret Punch Press,Laser Machine
2 Turret Punch Press,Press Brake
I know this might be a bad practice to do .. but currently i need this ..
Using a CSV splitter DelimitedSplit8K
; WITH CTE AS
(
SELECT b.id, a.name
FROM TableB b
CROSS APPLY dbo.DelimitedSplit8K(b.TableACSV, ',') c
INNER JOIN TableA a on c.Item = a.id
)
SELECT DISTINCT c.id, STUFF(d.name, 1, 1, '') AS TableACSV
FROM CTE c
CROSS APPLY
(
SELECT ',' + x.name
FROM CTE x
WHERE x.id = c.id
FOR XML PATH ('')
) d (name)

Concatenate column values against another column group

May be this is odd but i need to concatenate values of ActionId to corresponding group of roleId and Order by ActionID is must., some thing like
ActionID RoleId
"1357" 1
"2468" 2
Here is what i have currently, I am looking for GROUP_CONCAT equivalent in MS SQL.
select av.ActionId, ra.RoleId from RoleAction ra join ActionValue av
on ra.ActionId = av.ActionId order by av.ActionId
ActionID RoleId
1 1
3 1
5 1
7 1
4 2
2 2
6 2
8 2
Is there way to do that? Thanks in advance.
You can make it work using FOR XML PATH('') and an inner query:
SELECT DISTINCT T1.RoleID,
(SELECT '' + ActionID
FROM RoleAction T2
WHERE T1.RoleID = T2.RoleID
ORDER BY ActionID
FOR XML PATH(''))
FROM RoleAction T1
This should work:
WITH CTE_A AS
(
select av.ActionId, ra.RoleId from RoleAction ra join ActionValue av
on ra.ActionId = av.ActionId
)
SELECT DISTINCT A.RoleId,
(SELECT '' +
CAST(B.ActionId AS varchar(10))
FROM CTE_A B
WHERE B.RoleID = A.RoleID
FOR XML PATH('')) AS ActionID
FROM CTE_A A
GROUP BY A.RoleID

Resources