Displaying nested parent-child relationship - sql-server

I have a table as below with some data:
ID Name ParentID
-----------------------------
1 A NULL
2 B NULL
3 C 1
4 D 2
5 E 3
As you can see E is a child of C which is a child of A. I am looking for a simple SQL query which can return a string like below given a child ID, for e.g. ID = 5, which is E should have something as below:
Data
-----
A -> C -> E
I have tried below query but stuck on way forward
SELECT a.ID,
a.Name,
COALESCE(b.Name, '->') AS 'ParentName'
FROM MyTable AS a
LEFT JOIN MyTable AS b ON a.ID = b.ID
WHERE a.ID = 5
Any assistance would be appreciated.

Try this:
with cte as(
select * from t where id = 5
union all
select t.id, t.name, t.pid from cte c
join t on c.pid = t.id)
select replace(stuff((select '-' + name from cte
order by id
for xml path('')), 1, 1, ''), '-', '->')
Fiddle http://sqlfiddle.com/#!3/6fdde1/19

Related

Create a stored procedure to aggregate rows

Having a transaction table with the following rows:
Id UserId PlatformId TransactionTypeId
-------------------------------------------------
0 1 3 1
1 1 1 2
2 2 3 2
3 3 2 1
4 2 3 1
How do I write a stored procedure that can aggregate the rows into a new table with the following format?
Id UserId Platforms TransactionTypeId
-------------------------------------------------
0 1 {"p3":1,"p1":1} {"t1":1,"t2":1}
1 2 {"p3":2} {"t2":1,"t1":1}
3 3 {"p2":1} {"t1":1}
So the rows are gouped by User, count each platform/transactionType and store as key/value json string.
Ref: My previous related question
You could use GROUP BY and FOR JSON:
SELECT MIN(ID) AS ID, UserId, MIN(sub.x) AS Platforms, MIN(sub2.x) AS Transactions
FROM tab t
OUTER APPLY (SELECT CONCAT('p', platformId) AS platform, cnt
FROM (SELECT PlatformId, COUNT(*) AS cnt
FROM tab t2 WHERE t2.UserId = t.UserId
GROUP BY PlatformId) s
FOR JSON AUTO) sub(x)
OUTER APPLY (SELECT CONCAT('t', TransactiontypeId) AS Transactions, cnt
FROM (SELECT TransactiontypeId, COUNT(*) AS cnt
FROM tab t2 WHERE t2.UserId = t.UserId
GROUP BY TransactiontypeId) s
FOR JSON AUTO) sub2(x)
GROUP BY UserId;
DBFiddle Demo
Result is a bit different(array of key-value) but please treat it as starting point.
Your sample JSON is not really a json, but since you want it that way:
SELECT u.UserId, plt.pValue, ttyp.ttValue
FROM Users AS [u]
CROSS APPLY (
SELECT '{'+STUFF( (SELECT ',"'+pn.pName+'":'+LTRIM(STR(pn.pCount))
FROM (SELECT p.Name AS pName, COUNT(*) AS pCount
FROM transactions t
left JOIN Platforms p ON p.PlatformID = t.PlatformId
WHERE t.UserId = u.UserId
GROUP BY p.PlatformId, p.Name
) pn
FOR XML PATH('')),1,1,'')+'}'
) plt(pValue)
CROSS APPLY (
SELECT '{'+STUFF( (SELECT ',"'+tty.ttName+'":'+LTRIM(STR(tty.ttCount))
FROM (SELECT tt.Name AS ttName, COUNT(*) AS ttCount
FROM transactions t
left JOIN dbo.TransactionType tt ON tt.TransactionTypeId = t.TransactionTypeID
WHERE t.UserId = u.UserId
GROUP BY tt.TransactionTypeId, tt.Name
) tty
FOR XML PATH('')),1,1,'')+'}'
) ttyp(ttValue)
WHERE EXISTS (SELECT * FROM transactions t WHERE u.UserId = t.UserId)
ORDER BY UserId;
DBFiddle Sample

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

inner join and group by

I have two tables with identical definition.
T1:
Name VARCHAR(50)
Qty INT
T2:
Name VARCHAR(50)
Qty INT
This is the data each table has:
T1:
Name Qty
a 1
b 2
c 3
d 4
T2:
Name Qty
a 1
b 3
e 5
f 10
I want to have result which can sum the Qty from both the tables based on Name.
Expected resultset:
Name TotalQty
a 2
b 5
c 3
d 4
e 5
f 10
If am do Left Join or Right Join, it is not going to return me the Name from either of the tables.
What i am thinking is to create a temp table and add these records and just do a SUM aggregate on Qty column but i think there should be a better way to do this.
This is how my query looks like which does not return the expected resultset:
SELECT t1.Name, ISNULL(SUM(t1.Qty + t2.Qty),0) TotalQty
FROM t1
LEFT JOIN t2
ON t1.Name = T2.Name
GROUP BY t1.Name
Can someone please tell me if creating a temp table is OK here or there is a better way to do this?
You can use a full outer join:
SELECT
ISNULL(t1.Name, t2.Name) AS Name,
ISNULL(t1.Qty, 0) + ISNULL(t2.Qty, 0) AS TotalQty
FROM t1
FULL JOIN t2 ON t1.Name = T2.Name
See it working online: sqlfiddle
You can use a UNION ALL to select both tables as one, since they have the same definition. From there, you can nest them as a derived table, and then SUM on that:
SELECT [Name], SUM(Qty) AS TotalQty
FROM (
SELECT [Name], Qty
FROM t1
UNION ALL
SELECT [Name], Qty
FROM t2
) YourDerivedTable
GROUP BY [Name]

SQL query to find out some of three fields from three different table

I have three tables, tableA (id, A), tableB (id,B) and tableC (id,C). id is unique and primary key. Now I want to run a query on these three tables to find out sum of values of A,B and C for every id. (i.e. if id 1 is present in tableA but not in tableB then value B should be considered as 0 for id 1).example: tableA:
id A
1 5
2 6
3 2
5 7
tableB:
id B
2 5
3 8
4 1
tableC:
id C
5 2
the output should be:
id Sum
1 (5 + 0 + 0 =)5
2 (6 + 5 + 0 =)11
3 (2 + 8 + 0 =)10
4 (0 + 1 + 0 =)1
5 (7 + 0 + 2 =)9
First get a distinct list ( UNION ) of the IDs so that you include all, then LEFT JOIN to add the values together.
Something like
SELECT IDs.ID,
IFNULL(tableA.A,0) + IFNULL(tableB.B,0) + IFNULL(tableC.C,0) SumVal
FROM (
SELECT ID
FROM tableA
UNION
SELECT ID
FROM tableB
UNION
SELECT ID
FROM tableC
) IDs LEFT JOIN
tableA ON IDs.ID = tableA.ID LEFT JOIN
tableB ON IDs.ID = tableB.ID LEFT JOIN
tableC ON IDs.ID = tableC.ID
Something like this should work:
select id, sum(val) from
( select id, "A" as val from "tableA"
union all
select id, "B" as val from "tableB"
union all
select id, "C" as val from "tableC" ) as joined
group by id
order by id
I could not test it with MySql but this works my databases (HSQLDB, Oracle):
select ID, sum(X) from
(SELECT ID, A as X FROM tableA
UNION
SELECT ID, B as X FROM tableB
UNION
SELECT ID, C as X FROM tableC)
group by ID
Not sure about the exact MySQL syntax, but this works in SQL Server:
SELECT ID, SUM(ColToSum) As SumValue FROM
(
SELECT ID, A As ColToSum FROM TableA
UNION ALL
SELECT ID, B As ColToSum FROM TableB
UNION ALL
SELECT ID, C As ColToSum FROM TableC
) Combined
GROUP BY ID
Remember to use "UNION ALL", not just "UNION" which strips out duplicate rows as it combines (see http://dev.mysql.com/doc/refman/5.0/en/union.html)

Resources