TSQL - Mapping Parent/Child Hierarchy - sql

Sample Data:
DECLARE #Hierarchy TABLE
(
[ParentId] INT
, [ChildId] INT
) ;
INSERT INTO #Hierarchy
VALUES
( 1, 2 )
, ( 1, 3 )
, ( 2, 4 )
, ( 3, 5 )
, ( 4, 3 )
, ( 4, 6 )
, ( 5, 6 )
, ( 7, 3 ) ;
Current Query:
; WITH CTE AS
(
SELECT [ParentId]
, [ChildId]
, 1 AS [Level]
, CONCAT ( CAST ( [ParentId] AS VARCHAR(MAX) ), '.', CAST ( [ChildId] AS VARCHAR(MAX) ) ) AS [Path]
FROM #Hierarchy
UNION ALL
SELECT [C].[ParentId]
, [T].[ChildId]
, [C].[Level] + 1
, CAST ( [C].[Path] + '.' + CAST([T].[ChildId] AS VARCHAR(MAX) ) AS VARCHAR(MAX) )
FROM CTE AS [C]
JOIN #Hierarchy AS [T]
ON [C].[ChildId] = [T].[ParentId]
)
SELECT *
FROM CTE
ORDER BY [ParentId]
, [Level]
, [ChildId] ;
Goal:
distinctly group levels of shared "path" together
find the shallowest and the deepest levels of the shared "path"
Expected Output:
NOTICE: the records with Orange highlight at the end are manually inserted to show what I'm expecting, but haven't figure out yet.
Group: Basically a "dense rank" of each "groups" of nodes that follow the same path. I think if you look at the values of Group in the above image and relate it to Level and Path field's data, it'll make more sense.
IsShallowest: 1st level (I can see that now that someone brought it up). Just need to figure out how to derive those missing (repeating) records
IsDeepest: max level within the group.
Think IsShallowest and IsDeepest is easy to figure out once "Group" logic is figured out and adding missing (repeated) records.

Please check this solution. It provide the requested solution except adding the extra row which more information is needed
;WITH CTE AS (
SELECT
[ParentId]
, [ChildId]
, 1 AS [Level]
, CONCAT ( CAST ( [ParentId] AS VARCHAR(MAX) ), '.', CAST ( [ChildId] AS VARCHAR(MAX) ) ) AS [Path]
, MyGroup1 = ROW_NUMBER() OVER(ORDER BY [ParentId])
, MyGroup2 = ROW_NUMBER() OVER(ORDER BY [ParentId])
FROM Hierarchy
UNION ALL
SELECT
[C].[ParentId]
, [T].[ChildId]
, [C].[Level] + 1
, CAST ( [C].[Path] + '.' + CAST([T].[ChildId] AS VARCHAR(MAX) ) AS VARCHAR(MAX) )
, MyGroup1 = C.MyGroup1
, MyGroup2 = [C].[MyGroup1] + ROW_NUMBER() OVER(ORDER BY [T].[ParentId]) - 1
FROM CTE AS [C]
JOIN Hierarchy AS [T] ON [C].[ChildId] = [T].[ParentId]
)
, MyCTE2 as (
SELECT
[ParentId]
, [ChildId]
, [Level]
, [Path]
-- un-comment bellow 2 rows to see the logic
--, MyGroup1
--, MyGroup2
, MyGroup = DENSE_RANK() OVER (ORDER BY MyGroup1, MyGroup2)
FROM CTE
),
MyCTE3 as (
SELECT
[ParentId]
, [ChildId]
, [Level]
, [Path]
, MyGroup
, shallowest = ROW_NUMBER() OVER(PARTITION BY MyGroup ORDER BY [Path])
, deepest = ROW_NUMBER() OVER(PARTITION BY MyGroup ORDER BY [Path] DESC)
FROM MyCTE2
)
SELECT
[ParentId]
, [ChildId]
, [Level]
, [Path]
, MyGroup
, ISshallowest = CASE WHEN shallowest = 1 then 1 else 0 END
, Isdeepest = CASE WHEN deepest = 1 then 1 else 0 END
FROM MyCTE3
ORDER BY
--[Path]
[ParentId]
, [Level]
, [ChildId];

Related

Return random value for each row from different table

I'm trying to get random name for each row, but it just shows same random value for every row. What am I missing?
SELECT TOP (1000) v.id,v.[Name], RandomName
FROM [V3_Priva].[dbo].[Vehicle] v
cross join
(Select top 1 ISNULL([Description_cs-CZ], [Description]) RandomName
from crm.Enumeration e
join crm.EnumerationType et on e.EnumerationType_FK = et.Id
where EnumerationType_FK = 12
order by NEWID()) RandomName
Result table
Try using something like the following to drive your lookup.
DECLARE #Rows AS TABLE ( I INT NOT NULL )
DECLARE #Vals AS TABLE ( ID INT IDENTITY, Val NVARCHAR(255) NOT NULL )
INSERT INTO #Rows
VALUES ( 0 )
, ( 1 )
, ( 2 )
, ( 3 )
, ( 4 )
, ( 5 )
, ( 6 )
, ( 7 )
, ( 8 )
, ( 9 )
INSERT INTO #Vals
VALUES ( 'Apple' )
, ( 'Banana' )
, ( 'Peach' )
, ( 'Plum' )
, ( 'Pear' )
WITH cte AS ( SELECT *, ABS(CHECKSUM(NEWID())) % 5 ID FROM #Rows )
SELECT cte.I
, cte.ID
, V.ID
, V.Val
FROM cte
JOIN #Vals V ON V.ID = cte.ID + 1
ORDER BY I
This way new ID is generated for each row, rather than once for the lookup.

SQL - Prepend a value if missing

Code/data:
DECLARE #T TABLE
(
[Col1] VARCHAR(20)
, [RowNum] INT
) ;
INSERT INTO #T
VALUES
( N'second', 1 )
, ( N'fifth', 4 )
, ( N'fourth', 3 )
--, ( N'zzz', 1 )
, ( N'third', 2 )
---- OR when "zzz" is part of this list
--VALUES
-- ( N'second', 2 )
-- , ( N'fifth', 5 )
-- , ( N'fourth', 4 )
-- , ( N'zzz', 1 )
-- , ( N'third', 3 )
SELECT STUFF ((
SELECT ',' + [SQ].[Col1]
FROM
(
SELECT N'zzz' AS [Col1]
, 1 AS [RowNum]
UNION
SELECT [Col1]
, [RowNum]
FROM #T
) AS [SQ]
FOR XML PATH ( '' ), TYPE
).[value] ( '.', 'varchar(MAX)' ), 1, 1, ''
) ;
Current output:
fifth,fourth,second,third,zzz
Goal:
Prepend "zzz," in the output string if missing in the 2nd part of the union AND the values should be in ASC ordered based on the values specified in [rownum] field defined in the 2nd part of the union. If "zzz" exists in the 2nd part of the input already (it will always be RowNum 1 in that case), it should return it only once as the first value.
Expected output:
zzz,second,third,fourth,fifth
UPDATED the requirement due to an error on my part when creating this post. Updated code/data represents more accurate scenario. Please note the RowNum seq in the 2nd part of the UNION, it also starts with 1, but this time, it might or might not be associated to "zzz" Basically, I want to prepend "zzz" in the comma-delimited & ordered output if it doesn't exist.
Hope the below one will help you.
SELECT ',' + [SQ].[Col1]
FROM
(
SELECT N'first' AS [Col1],1 AS [RowNum]
UNION
SELECT [ABC].[Col1],[ABC].[RowNum]
FROM
(
VALUES
( N'second', 2 )
, ( N'fifth', 5 )
, ( N'fourth', 4 )
--, ( N'first', 1 )
, ( N'third', 3 )
) AS [ABC] ( [Col1], [RowNum] )
) AS [SQ]
ORDER BY [RowNum]
FOR XML PATH ( '' ), TYPE
).[value] ( '.', 'varchar(MAX)' ), 1, 1, ''
) ;
Returns an output
first,second,third,fourth,fifth
Attached the Answer for the updated Scenario-
DECLARE #T TABLE
(
[Col1] VARCHAR(20)
, [RowNum] INT
) ;
INSERT INTO #T
VALUES
( N'second', 1 )
, ( N'fifth', 4 )
, ( N'fourth', 3 )
--, ( N'zzz', 1 )
, ( N'third', 2 )
---- OR when "zzz" is part of this list
--VALUES
-- ( N'second', 2 )
-- , ( N'fifth', 5 )
-- , ( N'fourth', 4 )
-- , ( N'zzz', 1 )
-- , ( N'third', 3 )
SELECT STUFF ((
SELECT ',' + [SQ].[Col1]
FROM
(
SELECT N'zzz' AS [Col1]
, 0 AS [RowNum]
UNION
SELECT [Col1]
, [RowNum]
FROM #T
) AS [SQ]
ORDER BY [RowNum]
FOR XML PATH ( '' ), TYPE
).[value] ( '.', 'varchar(MAX)' ), 1, 1, ''
) ;
Returns
zzz,second,third,fourth,fifth
Common Table Expressions (CTEs) provide a handy way of breaking queries down into simpler steps. Note that you can view the results of each step by switching out the last select statement.
with
Assortment as (
-- Start with the "input" rows.
select Col1, RowNum
from ( values ( N'second', 2 ), ( N'fifth', 5 ), ( N'fourth', 4 ),
-- ( N'first', 1 ),
( N'third', 3 ) ) ABC ( Col1, RowNum ) ),
ExtendedAssortment as (
-- Conditionally add "first".
select Col1, RowNum
from Assortment
union all -- Do not remove duplicate rows.
select N'first', 1
where not exists ( select 42 from Assortment where Col1 = N'first' ) )
-- Output the result.
-- Intermediate results may be seen by uncommenting one of the alternate select statements.
-- select * from Assortment;
-- select * from ExtendedAssortment;
select Stuff(
( select N',' + Col1 from ExtendedAssortment order by RowNum for XML path(N''), type).value( N'.[1]', 'NVarChar(max)' ),
1, 1, N'' ) as List;
The same logic can be performed using tables for input:
-- Rows to be included in the comma delimited string.
declare #Input as Table ( Col1 NVarChar(20), RowNum Int );
insert into #Input ( Col1, RowNum ) values
( N'second', 2 ), ( N'fifth', 5 ),
--( N'ZZZ', 17 ), -- Test row.
( N'fourth', 4 ), ( N'third', 3 );
select * from #Input;
-- Mandatory value that must appear in the result. One row only.
declare #Mandatory as Table ( Col1 NVarChar(20), RowNum Int );
-- By using the maximum negative value for an Int this value will be prepended
-- (unless other rows happen to have the same RowNum value).
insert into #Mandatory ( Col1, RowNum ) values ( N'ZZZ', -2147483648 );
select * from #Mandatory;
-- Process the data.
with
AllRows as (
select Col1, RowNum
from #Input
union all
select Col1, RowNum
from #Mandatory
where not exists ( select 42 from #Mandatory as M inner join #Input as I on M.Col1 = I.Col1 ) )
-- Output the result.
-- Intermediate results may be seen by uncommenting the alternate select statement.
--select * from AllRows;
select Stuff(
( select N',' + Col1 from AllRows order by RowNum for XML path(N''), type).value( N'.[1]', 'NVarChar(max)' ),
1, 1, N'' ) as List;

SQL Server : concatenate the primary key ID within a grouped statement

I am trying to select a grouped set of rows and concatenate those rows primary key values into the select statement and count the rows also and select that value.
Tables:
JobTable - JobID, ExpressJob, ItemID
ItemTable - ItemID, Colour, Size
Values in Jobs:
10001, true, 3
10002, true, 3
10003, false, 4
Values in Items:
3, Blue, 1-2
4, Pink, 5-6
Result set:
3,Blue,1-2,10001|10002
3,Pink,5-6,10003
I've explored the following within the select statement:
SELECT
i.ItemID, i.Colour, i.Size,
COUNT(i.ItemID) AS Quantity,
j.ExpressJob,
JobIDArray = STUFF((SELECT CONVERT(VARCHAR(10), jb.JOBID)
FROM Jobs jb
WHERE jb.JobID = j.JobID
FOR XML PATH(''), TYPE).value('.', 'VARCHAR(MAX)'), 1, 1, ''))
FROM
Jobs j
INNER JOIN
Items i ON i.ItemID = j.ItemID
GROUP BY
i.ItemID, i.Colour, i.Size, j.ExpressJob
But I keep getting an aggregate group error on JobID. From what I have researched online FROM XML is the way to go but for some reason not effective when selecting the ID column.
Small tweak to what you already have will get you there.
Give this a try:
DECLARE #Jobs TABLE
(
[JobID] INT
, [ExpressJob] NVARCHAR(100)
, [ItemID] INT
);
DECLARE #Items TABLE
(
[ItemID] INT
, [Colour] NVARCHAR(100)
, [Size] NVARCHAR(100)
);
INSERT INTO #Jobs (
[JobID]
, [ExpressJob]
, [ItemID]
)
VALUES ( 10001, 'true', 3 )
, ( 10002, 'true', 3 )
, ( 10003, 'false', 4 );
INSERT INTO #Items (
[ItemID]
, [Colour]
, [Size]
)
VALUES ( 3, 'Blue', '1-2' )
, ( 4, 'Pink', '5-6' );
SELECT [i].[ItemID]
, [i].[Colour]
, [i].[Size]
, [j].[ExpressJob]
, COUNT([i].[ItemID]) AS [Quantity]
--Added '|' as that was how you wanted the results delimited
, STUFF((
SELECT '|' + CONVERT(VARCHAR(10), [jb].[JobID])
FROM #Jobs [jb]
WHERE [jb].[ItemID] = [i].[ItemID] --Change here as you're looking for JobID associated to the Item.
FOR XML PATH('') --No need to set TYPE or use '.value'
)
, 1
, 1
, ''
) AS JobIDArray
FROM #Jobs [j]
INNER JOIN #Items [i]
ON [i].[ItemID] = [j].[ItemID]
GROUP BY [i].[ItemID]
, [i].[Colour]
, [i].[Size]
, [j].[ExpressJob];

SQL Recursive CTE 'where-used' / BOM explosion part 2

Hi this carries on from another post,
SQL Recursive CTE 'where-used' / BOM explosion
which the original requirement was answered however I have realised I now have another final requirement. The data that I have after '1' i.e. description etc., want to repeat for each level so the description correctly identifies with the correct parent item. I tried just adding the columns in again in the final select however it just repeated items from level 1. How can this be done?
* UPDATE * I'm really struggling to get the test data/query to look how i want. I want to get the items to relate to each other through bomid. Output to look like img.
The relationship between BOM and BOMVERSION is one item has many BOMID's in BOM table each bomID has a corresponding record in BOMVERSION which through BOMID you get a different ITEMID off of BOMVERSION. That itemID can exist itself in the BOM table with MULTIPLE BOMIDs. I know this is confusing and is very difficult to demonstrate with test data. That's why i'm happy to put on bounty.
* UPDATE * through what i've learnt i've redone the query/test data. I haven't been able to get it to do EXACTLY what i want so query/data may need to be tweaked or added. I'll output what i'm expecting. Basically when BOM.ItemID is linked to BV.ItemID through BOMID i would expect BV.ItemID to be moved into next level and then if THAT BOM.ItemID links to another BV.ItemID move that item to the next level etc. etc. along with all other info RELATED to item in level.
IF OBJECT_ID('tempdb..#BOM') IS NOT NULL
DROP TABLE #BOM;
CREATE TABLE #BOM
(
ItemID NVARCHAR(10) ,
BOMID NVARCHAR(10) ,
BOMQTY INT ,
UnitID NVARCHAR(10) ,
BOMQTYSERIE INT
);
INSERT INTO #BOM
( ItemID ,
BOMID ,
BOMQTY ,
UnitID ,
BOMQTYSERIE
)
VALUES ( N'100001' , -- ItemID - nvarchar(10)
N'1A' , -- BOMID - nvarchar(10)
10 , -- BOMQTY - int
N'g' , -- UnitID - nvarchar(10)
5 -- Bomqtyserie - int
);
INSERT INTO #BOM
( ItemID ,
BOMID ,
BOMQTY ,
UnitID ,
BOMQTYSERIE
)
VALUES ( N'100001' , -- ItemID - nvarchar(10)
N'2A' , -- BOMID - nvarchar(10)
15 , -- BOMQTY - int
N'kg' , -- UnitID - nvarchar(10)
13 -- Bomqtyserie - int
);
INSERT INTO #BOM
( ItemID ,
BOMID ,
BOMQTY ,
UnitID ,
BOMQTYSERIE
)
VALUES ( N'100001' , -- ItemID - nvarchar(10)
N'3A' , -- BOMID - nvarchar(10)
16 , -- BOMQTY - int
N'l' , -- UnitID - nvarchar(10)
16 -- Bomqtyserie - int
);
INSERT INTO #BOM
( ItemID ,
BOMID ,
BOMQTY ,
UnitID ,
BOMQTYSERIE
)
VALUES ( N'100002' , -- ItemID - nvarchar(10)
N'1A' , -- BOMID - nvarchar(10)
18 , -- BOMQTY - int
N'g' , -- UnitID - nvarchar(10)
17 -- Bomqtyserie - int
);
INSERT INTO #BOM
( ItemID ,
BOMID ,
BOMQTY ,
UnitID ,
BOMQTYSERIE
)
VALUES ( N'100004' , -- ItemID - nvarchar(10)
N'2A' , -- BOMID - nvarchar(10)
20 , -- BOMQTY - int
N'kg' , -- UnitID - nvarchar(10)
11 -- Bomqtyserie - int
);
INSERT INTO #BOM
( ItemID ,
BOMID ,
BOMQTY ,
UnitID ,
BOMQTYSERIE
)
VALUES ( N'100002' , -- ItemID - nvarchar(10)
N'2A' , -- BOMID - nvarchar(10)
23 , -- BOMQTY - int
N'kg' , -- UnitID - nvarchar(10)
19 -- Bomqtyserie - int
);
INSERT INTO #BOM
( ItemID ,
BOMID ,
BOMQTY ,
UnitID ,
BOMQTYSERIE
)
VALUES ( N'100003' , -- ItemID - nvarchar(10)
N'2A' , -- BOMID - nvarchar(10)
25 , -- BOMQTY - int
N'kg' , -- UnitID - nvarchar(10)
21 -- Bomqtyserie - int
);
IF OBJECT_ID('tempdb..#BOMVERSION') IS NOT NULL
DROP TABLE #BOMVERSION;
CREATE TABLE #BOMVERSION
(
ItemID NVARCHAR(10) ,
BOMID NVARCHAR(10) ,
Name NVARCHAR(20) ,
Active BIT
);
INSERT INTO #BOMVERSION
( ItemID ,
BOMID ,
Name ,
Active
)
VALUES ( N'100002' , -- ItemID - nvarchar(10)
N'1A' , -- BOMID - nvarchar(10)
N'100002 Version' , -- Name - nvarchar(10)
1 -- Active - bit
);
INSERT INTO #BOMVERSION
( ItemID ,
BOMID ,
Name ,
Active
)
VALUES ( N'100002' , -- ItemID - nvarchar(10)
N'2A' , -- BOMID - nvarchar(10)
N'100002.1 Version' , -- Name - nvarchar(10)
1 -- Active - bit
);
INSERT INTO #BOMVERSION
( ItemID ,
BOMID ,
Name ,
Active
)
VALUES ( N'100003' , -- ItemID - nvarchar(10)
N'3A' , -- BOMID - nvarchar(10)
N'100003 Version' , -- Name - nvarchar(10)
1 -- Active - bit
);
INSERT INTO #BOMVERSION
( ItemID ,
BOMID ,
Name ,
Active
)
VALUES ( N'100004' , -- ItemID - nvarchar(10)
N'4A' , -- BOMID - nvarchar(10)
N'100004 Version' , -- Name - nvarchar(10)
1 -- Active - bit
);
INSERT INTO #BOMVERSION
( ItemID ,
BOMID ,
Name ,
Active
)
VALUES ( N'100005' , -- ItemID - nvarchar(10)
N'5A' , -- BOMID - nvarchar(10)
N'100005 Version' , -- Name - nvarchar(10)
1 -- Active - bit
);
IF OBJECT_ID('tempdb..#INVENTTABLE') IS NOT NULL
DROP TABLE #INVENTTABLE;
CREATE TABLE #INVENTTABLE
(
ItemID NVARCHAR(10) ,
Name NVARCHAR(20) ,
Product INT
);
INSERT INTO #INVENTTABLE
( ItemID, Name, Product )
VALUES ( N'100001', -- ItemID - nvarchar(10)
N'100001 Name', -- Name - nvarchar(10)
1 -- Product - int
);
INSERT INTO #INVENTTABLE
( ItemID, Name, Product )
VALUES ( N'100002', -- ItemID - nvarchar(10)
N'100002 Name', -- Name - nvarchar(10)
2 -- Product - int
);
INSERT INTO #INVENTTABLE
( ItemID, Name, Product )
VALUES ( N'100003', -- ItemID - nvarchar(10)
N'100003 Name', -- Name - nvarchar(10)
3 -- Product - int
);
INSERT INTO #INVENTTABLE
( ItemID, Name, Product )
VALUES ( N'100004', -- ItemID - nvarchar(10)
N'100004 Name', -- Name - nvarchar(10)
4 -- Product - int
);
INSERT INTO #INVENTTABLE
( ItemID, Name, Product )
VALUES ( N'100005', -- ItemID - nvarchar(10)
N'100005 Name', -- Name - nvarchar(10)
5 -- Product - int
);
IF OBJECT_ID('tempdb..#ECORESPRODUCTTRANSLATION') IS NOT NULL
DROP TABLE #ECORESPRODUCTTRANSLATION;
CREATE TABLE #ECORESPRODUCTTRANSLATION
(
Product INT ,
Name NVARCHAR(20)
);
INSERT INTO #ECORESPRODUCTTRANSLATION
( Product, Name )
VALUES ( 1, -- Product - int
N'100001 Description' -- Name - nvarchar(10)
);
INSERT INTO #ECORESPRODUCTTRANSLATION
( Product, Name )
VALUES ( 2, -- Product - int
N'100002 Description' -- Name - nvarchar(10)
);
INSERT INTO #ECORESPRODUCTTRANSLATION
( Product, Name )
VALUES ( 3, -- Product - int
N'100003 Description' -- Name - nvarchar(10)
);
INSERT INTO #ECORESPRODUCTTRANSLATION
( Product, Name )
VALUES ( 4, -- Product - int
N'100004 Description' -- Name - nvarchar(10)
);
INSERT INTO #ECORESPRODUCTTRANSLATION
( Product, Name )
VALUES ( 5, -- Product - int
N'100005 Description' -- Name - nvarchar(10)
);
WITH CTE
AS ( SELECT B.ItemID AS MainItem ,
BV.Name AS BVName ,
B.BOMID ,
BV.ItemID AS ParentItem ,
ECPT.Name AS ParentItemName ,
B.BOMQTY ,
B.UnitID ,
B.BOMQTYSERIE ,
1 AS [Level]
FROM #BOM AS B
JOIN #BOMVERSION AS BV ON BV.BOMID = B.BOMID
JOIN #INVENTTABLE AS IT ON IT.ItemID = BV.ItemID
JOIN #ECORESPRODUCTTRANSLATION AS ECPT ON ECPT.Product = IT.Product
WHERE B.ItemID = '100001'
AND BV.Active = 1
UNION ALL
SELECT C.MainItem ,
C.BVName ,
C.BOMID ,
BV.ItemID ,
C.ParentItemName ,
C.BOMQTY ,
C.UnitID ,
C.BOMQTYSERIE ,
C.[Level] + 1
FROM CTE AS C
JOIN #BOM AS B ON C.ParentItem = B.ItemID
JOIN #BOMVERSION AS BV ON BV.BOMID = B.BOMID
WHERE C.[Level] <= 7
)
SELECT MainItem ,
[1] AS Level1 ,
BVName ,
ParentItemName ,
BOMQTY ,
UnitID ,
BOMQTYSERIE ,
[2] AS Level2 ,
BVName ,
ParentItemName ,
BOMQTY ,
UnitID ,
BOMQTYSERIE ,
[3] AS Level3 ,
BVName ,
ParentItemName ,
BOMQTY ,
UnitID ,
BOMQTYSERIE ,
[4] AS Level4 ,
BVName ,
ParentItemName ,
BOMQTY ,
UnitID ,
BOMQTYSERIE ,
[5] AS Level5 ,
BVName ,
ParentItemName ,
BOMQTY ,
UnitID ,
BOMQTYSERIE ,
[6] AS Level6 ,
BVName ,
ParentItemName ,
BOMQTY ,
UnitID ,
BOMQTYSERIE ,
[7] AS Level7 ,
BVName ,
ParentItemName ,
BOMQTY ,
UnitID ,
BOMQTYSERIE
FROM CTE PIVOT ( MAX(ParentItem) FOR [Level] IN ( [1], [2], [3], [4],
[5], [6], [7] ) ) AS pvt;
Here is a version that is easy to understand and maintain:
;
WITH CTE
AS ( SELECT B.RECID AS MainID ,
B.ITEMID AS MainItem ,
BV.NAME ,
BV.ITEMID AS ParentItem ,
ECPT.NAME AS ParentItemName ,
B.BOMQTY ,
B.UNITID ,
B.BOMQTYSERIE ,
0 AS [level]
FROM #BOM B
JOIN #BOMVERSION BV ON BV.BOMID = B.BOMID
JOIN #INVENTTABLE AS IT ON IT.ITEMID = BV.ITEMID
JOIN #ECORESPRODUCTTRANSLATION AS ECPT ON ECPT.PRODUCT = IT.PRODUCT
WHERE B.ITEMID = '113621'
AND BV.ACTIVE = '1'
UNION ALL
SELECT C.MainID ,
C.MainItem ,
C.NAME ,
BV.ITEMID ,
C.ParentItemName ,
C.BOMQTY ,
C.UNITID ,
C.BOMQTYSERIE ,
C.[level] + 1
FROM CTE C
JOIN #BOM B ON C.ParentItem = B.ITEMID
JOIN #BOMVERSION BV ON BV.BOMID = B.BOMID
WHERE C.[level] <= 6
)
SELECT B.ITEMID ,
C1.ParentItem AS ParentItem1 ,
C1.NAME AS BVName1 ,
C1.ParentItemName AS ParentItemName1 ,
C1.BOMQTY AS BomQty1 ,
C1.UNITID AS UnitID1 ,
C1.BOMQTYSERIE AS BomQtySerie1 ,
C2.ParentItem AS ParentItem2 ,
C2.NAME AS BVName2 ,
C2.ParentItemName AS ParentItemName2 ,
C2.BOMQTY AS BomQty2 ,
C2.UNITID AS UnitID2 ,
C2.BOMQTYSERIE AS BomQtySerie2 ,
C3.ParentItem AS ParentItem3 ,
C3.NAME AS BVName3 ,
C3.ParentItemName AS ParentItemName3 ,
C3.BOMQTY AS BomQty3 ,
C3.UNITID AS UnitID3 ,
C3.BOMQTYSERIE AS BomQtySerie3 ,
C4.ParentItem AS ParentItem2 ,
C4.NAME AS BVName2 ,
C4.ParentItemName AS ParentItemName4 ,
C4.BOMQTY AS BomQty4 ,
C4.UNITID AS UnitID4 ,
C4.BOMQTYSERIE AS BomQtySerie4 ,
C5.ParentItem AS ParentItem5 ,
C5.NAME AS BVName5 ,
C5.ParentItemName AS ParentItemName5 ,
C5.BOMQTY AS BomQty5 ,
C5.UNITID AS UnitID5 ,
C5.BOMQTYSERIE AS BomQtySerie5 ,
C6.ParentItem AS ParentItem6 ,
C6.NAME AS BVName6 ,
C6.ParentItemName AS ParentItemName6 ,
C6.BOMQTY AS BomQty6 ,
C6.UNITID AS UnitID6 ,
C6.BOMQTYSERIE AS BomQtySerie6 ,
C7.ParentItem AS ParentItem7 ,
C7.NAME AS BVName7 ,
C7.ParentItemName AS ParentItemName7 ,
C7.BOMQTY AS BomQty7 ,
C7.UNITID AS UnitID7 ,
C7.BOMQTYSERIE AS BomQtySerie7
FROM #BOM B
LEFT JOIN CTE C1 ON B.RECID = C1.MainID
AND C1.[level] = 0
LEFT JOIN CTE C2 ON B.RECID = C2.MainID
AND C2.[level] = 1
LEFT JOIN CTE C3 ON B.RECID = C3.MainID
AND C3.[level] = 2
LEFT JOIN CTE C4 ON B.RECID = C4.MainID
AND C4.[level] = 3
LEFT JOIN CTE C5 ON B.RECID = C5.MainID
AND C5.[level] = 4
LEFT JOIN CTE C6 ON B.RECID = C6.MainID
AND C6.[level] = 5
LEFT JOIN CTE C7 ON B.RECID = C7.MainID
AND C7.[level] = 6
WHERE B.ITEMID = '113621'
AND C1.ParentItem IS NOT NULL;
EDIT:
Input:
INSERT INTO #BOM VALUES
( 1, N'10', N'1A' ),
( 2, N'20', N'2A' ),
( 3, N'30', N'3A' ),
( 4, N'40', N'4A' ),
( 5, N'50', N'5A' ),
( 6, N'60', N'6A' ),
( 7, N'70', N'7A' ),
( 8, N'80', N'8A' ),
( 9, N'90', N'9A' ),
( 10, N'100', N'10A' ),
( 11, N'110', N'11A' )
INSERT INTO #BOMVERSION VALUES
( 1, N'20', N'10 PRE', N'1A' ),
( 2, N'30', N'20 PRE', N'2A' ),
( 3, N'40', N'30 PRE', N'3A' ),
( 4, N'50', N'40 PRE', N'4A' ),
( 5, N'60', N'50 PRE', N'5A' ),
( 6, N'70', N'60 PRE', N'6A' ),
( 7, N'80', N'70 PRE', N'7A' ),
( 8, N'100', N'90 PRE', N'9A' ),
( 9, N'110', N'100 PRE', N'10A' ),
( 9, N'120', N'110 PRE', N'11A' )
INSERT INTO #Item VALUES
( 1, N'10', N'10 DESC' ),
( 2, N'20', N'20 DESC' ),
( 3, N'30', N'30 DESC' ),
( 4, N'40', N'40 DESC' ),
( 5, N'50', N'50 DESC' ),
( 6, N'60', N'60 DESC' ),
( 7, N'70', N'70 DESC' ),
( 8, N'80', N'80 DESC' ),
( 9, N'90', N'90 DESC' ),
( 10, N'100', N'100 DESC' ),
( 11, N'110', N'110 DESC' )
Output:
I think You need this type of pivoting:
select p.MainItem, p.BVName
, left(p.[0],charindex('|',p.[0])-1) Level0_ID, REPLACE(p.[0], left(p.[0],charindex('|',p.[0])),'') Level0_Decription
, left(p.[1],charindex('|',p.[1])-1) Level1_ID, REPLACE(p.[1], left(p.[1],charindex('|',p.[1])),'') Level1_Decription
, left(p.[2],charindex('|',p.[2])-1) Level2_ID, REPLACE(p.[2], left(p.[2],charindex('|',p.[2])),'') Level2_Decription
, left(p.[3],charindex('|',p.[3])-1) Level3_ID, REPLACE(p.[3], left(p.[3],charindex('|',p.[3])),'') Level3_Decription
, left(p.[4],charindex('|',p.[4])-1) Level4_ID, REPLACE(p.[4], left(p.[4],charindex('|',p.[4])),'') Level4_Decription
, left(p.[5],charindex('|',p.[5])-1) Level5_ID, REPLACE(p.[5], left(p.[5],charindex('|',p.[5])),'') Level5_Decription
, left(p.[6],charindex('|',p.[6])-1) Level6_ID, REPLACE(p.[6], left(p.[6],charindex('|',p.[6])),'') Level6_Decription
, left(p.[7],charindex('|',p.[7])-1) Level7_ID, REPLACE(p.[7], left(p.[7],charindex('|',p.[7])),'') Level7_Decription
from (select t.MainItem, t.BVName, t.level, convert(varchar(20),t.ParentItem)+'|'+t.ParentItemName item from tree t) t
pivot (max(item) for t.level in ([0], [1], [2], [3], [4], [5], [6], [7])) p
If You want use more column to pivot the try this:
select p.MainItem, p.BVName
, p.Level0_ParentItem, p.Level0_ParentItemName
, p.Level1_ParentItem, p.Level1_ParentItemName
, p.Level2_ParentItem, p.Level2_ParentItemName
, p.Level3_ParentItem, p.Level3_ParentItemName
, p.Level4_ParentItem, p.Level4_ParentItemName
, p.Level5_ParentItem, p.Level5_ParentItemName
, p.Level6_ParentItem, p.Level6_ParentItemName
, p.Level7_ParentItem, p.Level7_ParentItemName
from (
select u.MainItem, u.BVName, 'Level'+convert(varchar(30), u.level)+'_'+u.col col, u.value
from (select t.MainItem, t.BVName, t.level, convert(nvarchar(max),t.ParentItem) ParentItem, convert(nvarchar(max),t.ParentItemName) ParentItemName from tree t) t /*Convert all columns to the desired, but the same type*/
unpivot (value for col in (ParentItem, ParentItemName /*and the other columns You want use*/)) u) src
pivot (max(src.value)
for src.col in (
Level0_ParentItem, Level0_ParentItemName
, Level1_ParentItem, Level1_ParentItemName
, Level2_ParentItem, Level2_ParentItemName
, Level3_ParentItem, Level3_ParentItemName
, Level4_ParentItem, Level4_ParentItemName
, Level5_ParentItem, Level5_ParentItemName
, Level6_ParentItem, Level6_ParentItemName
, Level7_ParentItem, Level7_ParentItemName
)) p
In the unpivot statement I make the new columns for the pivot statement.
More from this solution: SQL Server Pivot Table with multiple column aggregates
*** UPDATED WITH FINAL REQUIRED FORMAT ***
WITH CTE
AS ( SELECT B.ITEMID AS MainItem ,
BV.NAME AS BVName ,
B.BOMID ,
BV.ITEMID AS ParentItem ,
ECPT.NAME AS ParentItemName ,
B.BOMQTY ,
B.UNITID ,
B.BOMQTYSERIE ,
1 AS [Level]
FROM BOM AS B
JOIN BOMVERSION AS BV ON BV.BOMID = B.BOMID
JOIN dbo.INVENTTABLE AS IT ON IT.ITEMID = BV.ITEMID
JOIN dbo.ECORESPRODUCTTRANSLATION AS ECPT ON ECPT.PRODUCT = IT.PRODUCT
WHERE B.ITEMID = '113621' /*is this really needed*/
AND BV.ACTIVE = 1
UNION ALL
SELECT C.MainItem ,
C.BVName ,
C.BOMID ,
BV.ITEMID ,
C.ParentItemName ,
C.BOMQTY ,
C.UNITID ,
C.BOMQTYSERIE ,
C.[Level] + 1
FROM CTE AS C
JOIN BOM AS B ON C.ParentItem = B.ITEMID
JOIN BOMVERSION AS BV ON BV.BOMID = B.BOMID
WHERE C.[Level] <= 7
),
TREE
AS ( SELECT CTE.MainItem ,
CTE.BVName ,
CTE.ParentItem ,
CTE.ParentItemName ,
CTE.BOMQTY ,
CTE.UNITID ,
CTE.BOMQTYSERIE ,
CTE.[Level]
FROM CTE
)
SELECT p.MainItem ,
p.Level1_ParentItem ,
p.Level1_BVName ,
p.Level1_ParentItemName ,
p.Level1_BOMQTY ,
p.Level1_UnitID ,
p.Level2_ParentItem ,
p.Level2_BVName ,
p.Level2_ParentItemName ,
p.Level2_BOMQTY ,
p.Level2_UnitID ,
p.Level3_ParentItem ,
p.Level3_BVName ,
p.Level3_ParentItemName ,
p.Level3_BOMQTY ,
p.Level3_UnitID ,
p.Level4_ParentItem ,
p.Level4_BVName ,
p.Level4_ParentItemName ,
p.Level4_BOMQTY ,
p.Level4_UnitID ,
p.Level5_ParentItem ,
p.Level5_BVName ,
p.Level5_ParentItemName ,
p.Level5_BOMQTY ,
p.Level5_UnitID ,
p.Level6_ParentItem ,
p.Level6_BVName ,
p.Level6_ParentItemName ,
p.Level6_BOMQTY ,
p.Level6_UnitID ,
p.Level7_ParentItem ,
p.Level7_BVName ,
p.Level7_ParentItemName ,
p.Level7_BOMQTY ,
p.Level7_UnitID
FROM ( SELECT u.MainItem ,
'Level' + CAST(u.Level AS VARCHAR(20)) + '_' + u.col col ,
u.value
FROM ( SELECT t.MainItem ,
CAST(t.ParentItem AS VARCHAR(30)) AS ParentItem ,
CAST(t.BVName AS VARCHAR(30)) AS BVName ,
CAST(t.ParentItemName AS VARCHAR(30)) AS ParentItemName ,
CAST(t.BOMQTY AS VARCHAR(30)) AS BOMQTY ,
CAST(t.UNITID AS VARCHAR(30)) AS UnitID ,
CAST(t.BOMQTYSERIE AS VARCHAR(30)) AS BOMQTYSERIE ,
t.Level
FROM TREE t
) t /*Convert all columns to the desired, but the same type*/
UNPIVOT ( value FOR col IN ( ParentItem,
ParentItemName, BVName,
BOMQTY, UnitID,
BOMQTYSERIE
/*and the other columns You want use*/ ) ) u
) src PIVOT ( MAX(src.value) FOR src.col IN ( Level1_ParentItem,
Level1_BVName,
Level1_ParentItemName,
Level1_BOMQTY,
Level1_UnitID,
Level2_ParentItem,
Level2_BVName,
Level2_ParentItemName,
Level2_BOMQTY,
Level2_UnitID,
Level3_ParentItem,
Level3_BVName,
Level3_ParentItemName,
Level3_BOMQTY,
Level3_UnitID,
Level4_ParentItem,
Level4_BVName,
Level4_ParentItemName,
Level4_BOMQTY,
Level4_UnitID,
Level5_ParentItem,
Level5_BVName,
Level5_ParentItemName,
Level5_BOMQTY,
Level5_UnitID,
Level6_ParentItem,
Level6_BVName,
Level6_ParentItemName,
Level6_BOMQTY,
Level6_UnitID,
Level7_ParentItem,
Level7_BVName,
Level7_ParentItemName,
Level7_BOMQTY,
Level7_UnitID ) ) p;

Sorting comments with nested replies

I am trying to come up with a query that will return comments following their replies and its replies.
Something like
comment 1
reply 1.1
reply 1.1.1
reply 1.2
comment 2
comment 3
comment 3.1
etc
I have this so far
SELECT [CommentID]
,[ParentID]
,[Message]
, ROW_NUMBER() over(partition by ParentID order by CommentID ) as rn
,[CreatedBy]
,[CreatedDate]
FROM [DBNAME].[dbo].[Commenttable]
GROUP BY [CommentID],[ParentID],[CreatedDate],[Message],[CreatedBy]
but what I get is
comment 1
comment 2
comment 3
reply 1.1
reply 1.2
reply 3.1
reply 1.1.1
Basic Structure is just a table with Comment ID, Parent ID, and the message.
The comments and replies are just to help explain what I am trying to achieve
Give this a try:
declare #CommentTable as Table ( CommentId Int Identity, ParentId Int Null, Message VarChar(16) )
insert into #CommentTable ( ParentId, Message ) values
( null, '1' ),
( null, '2' ), ( 1, '1.1' ),
( null, '3' ), ( 4, '3.1' ), ( 3, '1.1.1' ), ( 1, '1.2' )
select * from #CommentTable
; with Cindy as (
-- Start with the base comments.
select CommentId, ParentId, Message, Row_Number() over ( order by CommentId ) as Number,
Cast( Row_Number() over ( order by CommentId ) as VarChar(1000) ) as Path,
Cast( Right( '0000' + Cast( Row_Number() over ( order by CommentId ) as VarChar(4) ), 5 ) as VarChar(1000) ) as OrderPath
from #CommentTable
where ParentId is NULL
union all
-- Add replies on layer at a time.
select CT.CommentId, CT.ParentId, CT.Message, Row_Number() over ( order by CT.CommentId ),
Cast( C.Path + '.' + Cast( Row_Number() over ( order by CT.CommentId ) as VarChar(4) ) as VarChar(1000) ),
Cast( C.OrderPath + Right( '0000' + Cast( Row_Number() over ( order by CT.CommentId ) as VarChar(4) ), 5 ) as VarChar(1000) )
from #CommentTable as CT inner join
Cindy as C on C.CommentId = CT.ParentId
)
select *
from Cindy
order by OrderPath