T-SQL How to change hierarchy levels to ascend instead of descend? - sql

I have very simple query that show all parents of all childs:
;with cte as (
select 1 as Level, Child, Parent from TABLE
union all
select t.Level + 1, t.Child, t.Parentfrom TABLE t
inner join cte on t.Child = cte.Parent
)
select distinct * from t
output:
Level|Child|Parent|
3 | A | |
2 | B | A |
1 | C | B |
1 | D | B |
How do I achieve the levels to be in ascending order, so that the top parent begins with 1, for example:
Level|Child|Parent|
1 | A | |
2 | B | A |
3 | C | B |
3 | D | B |
Thank you.

You can use DENSE_RANK in your final query. Something like:
SELECT DENSE_RANK() OVER (ORDER BY [Level] DECS) AS [Level]

Start with parents and work your way down:
with cte as (
select 1 as Level, Child, Parent
from TABLE
where parent is null
union all
select cte.Level + 1, t.Child, t.Parent
from TABLE t join
cte
on cte.Child = t.Parent
)
select *
from cte;
Here is a db<>fiddle.

Related

display oldest value with newest value at id level

I have a table that looks something like this where ids are displayed with old value with the new value (the one it just got changed into).
IF OBJECT_ID('tempdb.dbo.#TT','U')IS NOT NULL
DROP TABLE #TT;
SELECT *
INTO #TT
FROM (
SELECT 1 AS ID, 'A' AS OldValue, 'B' AS NewValue, CONVERT(DATE,'20190421') AS [Date]
UNION ALL
SELECT 1 AS ID, 'B' AS OldValue, 'C' AS NewValue, CONVERT(DATE,'20190423') AS [Date]
UNION ALL
SELECT 2 AS ID, 'D' AS OldValue, 'E' AS NewValue, CONVERT(DATE,'20190422') AS [Date]
UNION ALL
SELECT 3 AS ID, 'J' AS OldValue, 'K' AS NewValue, CONVERT(DATE,'20190422') AS [Date]
UNION ALL
SELECT 3 AS ID, 'K' AS OldValue, 'L' AS NewValue, CONVERT(DATE,'20190423') AS [Date]
UNION ALL
SELECT 3 AS ID, 'L' AS OldValue, 'M' AS NewValue, CONVERT(DATE,'20190424') AS [Date]
) T
;
I want to display each id and old value with newest value available for them. For example ID=1 and OldValue = A should display C or ID = 3 and OldValue = K should display M
I have tried to do recursive cte as follows:
WITH RecCTE AS (
SELECT ID, OldValue, NewValue
FROM #TT
UNION ALL
SELECT A.ID, B.OldValue, A.NewValue
FROM #TT A
INNER JOIN RecCTE B ON A.ID = B.ID AND B.NewValue = A.OldValue
)
SELECT *
FROM RecCTE
With that recursive query. I am getting some line correctly but I am also getting extra lines in between:
| ID | OldValue | NewValue |
|-----------|---------------|----------------|
| 1 | A | B |
| 1 | B | C |
| 2 | D | E |
| 3 | J | K |
| 3 | K | L |
| 3 | L | M |
| 3 | K | M |
| 3 | J | L |
| 3 | J | M |
| 1 | A | C |
I want to get results like this:
| ID | OldValue | NewValue |
|-----------|---------------|----------------|
| 1 | A | C |
| 1 | B | C |
| 2 | D | E |
| 3 | J | M |
| 3 | K | M |
| 3 | L | M |
I think taking the last row should work:
WITH RecCTE AS (
SELECT ID, OldValue, NewValue, 1 as lev
FROM #TT
UNION ALL
SELECT A.ID, B.OldValue, A.NewValue, lev + 1
FROM #TT A JOIN
RecCTE B
ON A.ID = B.ID AND B.NewValue = A.OldValue
)
SELECT *
FROM (SELECT r.*, ROW_NUMBER() OVER (PARTITION BY id, OldValue ORDER BY lev DESC) as seqnum
FROM RecCTE r
) r
WHERE seqnum = 1;
If the data works the way it looks to me like it works, the following will do it without using recursion:
;WITH cte as (
-- List of all, ordered by ID, with the most recent being the first listed
SELECT
ID
,NewValue
,row_number() over (partition by Id order by Date desc) Ranking
from #TT
)
select
tt.Id
,tt.OldValue
,cte.NewValue
from #TT tt
-- Join table to the "most recent" row for the ID
inner join cte
on cte.ID = tt.Id
and cte.Ranking = 1

get parents (and their parents,...) from table with self relationship in SQL Server

I have been breaking my head over this, but without any succes... I have following self-reverencing table (Table A):
ID |EmployeeID | ParentID | Level |
1 |11 | null | A |
2 |12 | 11 | B |
3 |13 | 12 | C |
4 |14 | 12 | C |
(it's not well build, but we can't change that anymore)
I need to create a view that gives following result:
ID | EmployeeID | Level | LevelA | LevelB | LevelC | LevelD
1 | 11 | A | 11 | null | null | null
2 | 12 | B | 11 | 12 | null | null
3 | 13 | C | 11 | 12 | 13 | null
4 | 14 | C | 11 | 12 | 14 | null
ID, EmployeeID and Level come directly from Table A.
Level A - D gives the parent of that EmployeeID and the next parents in hierarchy. If the Level of the Employee is C, you can say it is a C-level employee so his ID is in column LevelC. His Parent is a B-Level employee, so his ID comes in column LevelB. His patent is a A-level employee (which is the highest rank) and his ID comes in column LevelA.
The empty levels just stay null.
Any ideas/suggestions?
I think you need something like:
SELECT A.ID, A.EMPLOYEEID, A.LEVEL, PT.A, PT.B, PT.C, PT.D
FROM TABLEA A
INNER JOIN
(
SELECT * FROM
(SELECT ID, EMPLOYEEID, LEVEL FROM TABLEA) AS SOURCETABLE
PIVOT (
MAX([EMPLOYEEID])
FOR LEVEL IN ([A], [B], [C], [D])) AS PIVOTTABLE
) AS PT
ON A.ID = PT.ID
This code Works as long as you have four columns, but you'll see null values in the columns not matched by the pivot.
Hope it helps.
Use Common Table Expression (CTE)
With MyCTE
As (SELECT EmployeeID, ParentID, Level
FROM tableA
WHERE ParentID IS NULL
UNION ALL
SELECT EmployeeID, ParentID, Level
FROM tableA a join MyCTE c
ON c.EmployeeID = a.ParentID
WHERE a.ParentID IS NOT NULL )
SELECT * FROM MyCTE
You really don't even need the Level column in the table
With MyCTE
As (SELECT EmployeeID, ParentID, 1 Level
FROM tableA
WHERE ParentID IS NULL
UNION ALL
SELECT a.EmployeeID, a.ParentID, Level + 1 Level
FROM tableA a join MyCTE c
ON c.EmployeeID = a.ParentID
WHERE a.ParentID IS NOT NULL )
SELECT * FROM MyCTE
to just add the extra columns, you can use Pivot,

Repeat row based on the number in a column

SqlFiddle Demo
I need to repeat each barcode of the article based on the quantity of this article in the table Stock.
This is source data:
| BarCode | quantity |
|---------|----------|
| 5142589 | 7 |
| 123454 | 5 |
| 1111145 | 3 |
I want result that looks like this:
Barcode
-------
5142589
5142589
5142589
5142589
5142589
5142589
5142589
123454
123454
123454
123454
123454
1111145
1111145
1111145
How can I do this?
Thanks
You can use table of numbers. Either permanent, or generated on the fly.
Query below uses CTE to generate up to 1000 numbers. Here is SQL Fiddle.
WITH
e1(n) AS
(
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
) -- 10
,e2(n) AS (SELECT 1 FROM e1 CROSS JOIN e1 AS b) -- 10*10
,e3(n) AS (SELECT 1 FROM e1 CROSS JOIN e2) -- 10*100
,CTE_Numbers
AS
(
SELECT ROW_NUMBER() OVER (ORDER BY n) AS Number
FROM e3
)
SELECT b.BarCode, s.quantity
FROM
TABLE_BARCODE b
INNER JOIN TABLE_STOCK s ON b.IdArticle = s.IdArticle
CROSS APPLY
(
SELECT TOP(s.quantity) CTE_Numbers.Number
FROM CTE_Numbers
ORDER BY CTE_Numbers.Number
) AS CA
Results:
| BarCode | quantity |
|---------|----------|
| 5142589 | 7 |
| 5142589 | 7 |
| 5142589 | 7 |
| 5142589 | 7 |
| 5142589 | 7 |
| 5142589 | 7 |
| 5142589 | 7 |
| 123454 | 5 |
| 123454 | 5 |
| 123454 | 5 |
| 123454 | 5 |
| 123454 | 5 |
| 1111145 | 3 |
| 1111145 | 3 |
| 1111145 | 3 |
You can get this by a simple recursive CTE.
WITH cte
AS
(
SELECT IdArticle,1 AS rn FROM TABLE_STOCK
UNION ALL
SELECT t.IdArticle,rn+1 AS rn
FROM cte c
INNER JOIN TABLE_STOCK t ON t.IdArticle = c.IdArticle and rn<t.QUANTITY
)
SELECT t.BarCode,TS.QUANTITY
FROM cte c
INNER JOIN TABLE_BARCODE t ON t.IdArticle = c.IdArticle
INNER JOIN TABLE_STOCK TS ON TS.IdArticle = C.IdArticle
ORDER BY t.IdArticle
Here is SQL Fiddle
Simplified and improved version of Vladmir's answer:
DECLARE #t table(BarCode int, quantity int)
INSERT #t values(5142589, 7),(123454, 5),(1111145,3)
;WITH
e1(n) AS
(
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
) -- 10
,e2(n) AS (SELECT 1 FROM e1 CROSS JOIN e1 AS b) -- 10*10
,e3(n) AS (SELECT 1 FROM e2 CROSS JOIN e2 ex) -- 100*100
SELECT BarCode
FROM #t t
CROSS APPLY
(
SELECT top(t.quantity) null dummy
FROM e3
) x

Select rows with maximum value where values in two columns are same

I have a simple table like this
....................................
| hotelNo | roomType | totalBooking |
....................................
| 1 | single | 2 |
| 1 | family | 4 |
| 2 | single | 3 |
| 2 | family | 2 |
| 3 | single | 1 |
.....................................
Now I want to get the most commonly booked roomType for each hotels, i.e the following result
......................
| hotelNo | roomType |
......................
| 1 | family |
| 2 | single |
| 3 | single |
......................
P.S I use sub-query to get the first table
If you want the maximum, you can use window functions:
select hotelNo, roomType
from (select t.*, row_number() over (partition by hotelNo order by totalBooking desc) as seqnum
from table t
) t
where seqnum = 1;
Sample table
SELECT * INTO #TEMP
FROM
(
SELECT 1 HOTELNO ,'SINGLE' ROOMTYPE ,2 TOTALBOOKING
UNION ALL
SELECT 1,'FAMILY',4
UNION ALL
SELECT 2,'SINGLE',3
UNION ALL
SELECT 2,'FAMILY',2
UNION ALL
SELECT 3,'SINGLE',1
)TAB
Result query
;WITH CTE1 AS
(
SELECT HOTELNO,ROOMTYPE,TOTALBOOKING,
MAX(TOTALBOOKING) OVER (PARTITION BY HOTELNO) MAXX
FROM #TEMP
)
SELECT DISTINCT HOTELNO,ROOMTYPE,MAXX
FROM CTE1
WHERE TOTALBOOKING=MAXX
Another way of doing is by using Aggregate Function Max
SELECT A.hotelNo,
A.roomType,
A.totalBooking
FROM tablename A
JOIN (SELECT Max (totalBooking) totalBooking,
hotelNo
FROM tablename
group by hotelNo) B
ON a.totalBooking = b.totalBooking
AND a.hotelNo = b.hotelNo
SQL FIDDLE DEMO

How can I get linked list like (Parent - Child) mapped columns in a single Query?

Consider The following Table
+--------+-------+--+
| Parent | Child | |
+--------+-------+--+
| 1 | 2 | |
| 10 | 13 | |
| 2 | 3 | |
| 3 | 4 | |
| 13 | 14 | |
| 4 | 5 | |
| 15 | 16 | |
| 5 | 1 | |
+--------+-------+--+
In this table I'm following the hierarchy of parent child. From this table I want a result as the below table
+--------+-------+--+
| Parent | Child | |
+--------+-------+--+
| 1 | 2 | |
| 2 | 3 | |
| 3 | 4 | |
| 4 | 5 | |
| 5 | 1 | |
+--------+-------+--+
I want to get the hierarchy in my code (1-2-3-4-5-1). At present I'm querying for each child after getting its parent (Sometimes, Child can be any of previous Parents like 5-1). For a long hierarchy it will execute a number of queries. How can I make this more efficient?
;with cte(parent,child) as (
select parent, child
from sometable
where parent = 1 --- seed
UNION ALL
select t.parent, t.child
from sometable t
join cte on cte.child = t.parent
)
select *
from cte;
To avoid infinite loops, you will have to store the list of traversed ids:
;with cte(parent,child,traversed) as (
select parent, child, ',' + right(parent,10) + ','
from sometable
where parent = 1 --- seed
UNION ALL
select t.parent, t.child, cte.traversed + right(t.parent,10) + ','
from sometable t
join cte on cte.child = t.parent
where not cte.traversed like ',%' + t.parent + '%,'
)
select parent, child
from cte;
But it won't run anywhere near as fast since it's having to do the LIKE checks.
Please try:
DECLARE #table as TABLE(Parent int, Child int)
insert into #table values
(1, 2),
(10, 13),
(2, 3),
(3, 4),
(13, 14),
(4, 5),
(5, 1)
select * from #table
declare #ParentID int
set #ParentID=1
;WITH T(Parent, Child)AS
(
SELECT Parent, Child from #table where Parent=#ParentID
UNION ALL
SELECT T1.Parent, T1.Child FROM #table T1 INNER JOIN T ON T1.Parent=T.Child
WHERE T.Child<>#ParentID
)
select * from T
order by Parent
The manual covers that: http://msdn.microsoft.com/en-us/library/ms186243(v=sql.105).aspx
SO really shouldn't be used to for asking questions that already have good answers in the manuals.