How to group names which are dependent on other columns - sql-server

I have a table like below.
CREATE TABLE #Test
(
CustId INT ,
CustName VARCHAR(100) ,
CustHeading VARCHAR(100)
)
INSERT INTO #Test
SELECT '1','john carroll','Heading 1'
UNION ALL
SELECT '1','john carroll','Heading 2'
UNION ALL
SELECT '2','john c','Heading 1'
UNION ALL
SELECT '2','john c','Heading 2'
UNION ALL
SELECT '2','john c','Heading 3'
UNION ALL
SELECT '3','john lynch','Heading 1'
UNION ALL
SELECT '4','john carroll lynch','Heading 1'
UNION ALL
SELECT '4','john carroll lynch','Heading 4'
UNION ALL
SELECT '4','john carroll lynch','Heading 5'
UNION ALL
SELECT '5','john c lynch','Heading 1'
UNION ALL
SELECT '5','john c lynch','Heading 3'
UNION ALL
SELECT '6','john c l','Heading 11'
UNION ALL
SELECT '6','john c l','Heading 12'
UNION ALL
SELECT '7','john c ln','Heading 1'
UNION ALL
SELECT '7','john c ln','Heading 2'
UNION ALL
SELECT '2','john c','Heading 11'
UNION ALL
SELECT '2','john c','Heading 12'
In this, we need to group the customers who are having atleast two matching headings among them.
For example,custID :: 1,2 and 7 are having two matching CustHeading :: Header 1 and Header 2, so they are grouped.CustID :: 2 and 5 having two matching CustHeading :: Header 1 and Header 3,they also can be grouped. Please let me know how to achieve this
without using WHILE loop
Thanks in advance.

SELECT
DISTINCT
CASE WHEN c.num = 1 THEN a.CustId
ELSE a.[_CustId]
END ,
CASE WHEN c.num = 1 THEN a.CustName
ELSE a.[_CustName]
END ,
'M' + CAST(DENSE_RANK() OVER ( ORDER BY mx, mn ) AS VARCHAR(100)) AS gr_nbr
FROM
(
SELECT
a.CustId ,
a.CustName ,
c.CustId _CustId ,
c.CustName _CustName,
MAX(a.CustHeading) mx,
MIN(a.CustHeading) mn
FROM
#Test a
JOIN #Test c ON c.CustHeading = a.CustHeading
AND c.CustId > a.CustId
GROUP BY
a.CustId ,
a.CustName ,
c.CustId ,
c.CustName
HAVING
MAX(a.CustHeading) <> MIN(a.CustHeading)
) a
JOIN #Test b ON b.CustId = a.[_CustId]
CROSS JOIN (
SELECT
1 num
UNION ALL
SELECT
2 num
) AS c
ORDER BY
3 ,
1

Sorry my brain is not working today. I'm sure there is a simpler way to do this but I can think right now. This seems to work fine for me. Let me know if it needs any changes.
CREATE TABLE #Test
(
CustId INT ,
CustName VARCHAR(100) ,
CustHeading VARCHAR(100)
)
INSERT INTO #Test
SELECT '1','john carroll','Heading 1'
UNION ALL
SELECT '1','john carroll','Heading 2'
UNION ALL
SELECT '2','john c','Heading 1'
UNION ALL
SELECT '2','john c','Heading 2'
UNION ALL
SELECT '2','john c','Heading 3'
UNION ALL
SELECT '3','john lynch','Heading 1'
UNION ALL
SELECT '4','john carroll lynch','Heading 1'
UNION ALL
SELECT '4','john carroll lynch','Heading 4'
UNION ALL
SELECT '4','john carroll lynch','Heading 5'
UNION ALL
SELECT '5','john c lynch','Heading 1'
UNION ALL
SELECT '5','john c lynch','Heading 3'
UNION ALL
SELECT '6','john c l','Heading 11'
UNION ALL
SELECT '6','john c l','Heading 12'
UNION ALL
SELECT '7','john c ln','Heading 1'
UNION ALL
SELECT '7','john c ln','Heading 2'
UNION ALL
SELECT '2','john c','Heading 11'
UNION ALL
SELECT '2','john c','Heading 12';
WITH CTE_Heading
AS
(
SELECT DISTINCT custHeading
FROM #Test
),
CTE_Paired_Headings
AS
(
SELECT A.custHeading AS Head1,
B.CustHeading AS Head2
FROM CTE_Heading A
INNER JOIN CTE_Heading B
ON A.custHeading < B.custHeading
),
CTE_Matching_Cust
AS
(
SELECT A.Head1,
A.Head2,
B.CustId,
B.CustName
FROM CTE_Paired_Headings A
INNER JOIN #Test B
ON A.Head1 = B.CustHeading
OR A.Head2 = B.CustHeading
GROUP BY A.Head1,A.Head2,B.CustId,B.CustName
HAVING COUNT(*) >= 2
),
CTE_HeadingGroups
AS
(
SELECT 'M' + CAST(ROW_NUMBER() OVER (ORDER BY Head1,Head2) AS VARCHAR(5)) MatchingID,
Head1,
Head2
FROM CTE_Matching_Cust
GROUP BY Head1,Head2
HAVING COUNT(*) >= 2
)
SELECT B.CustId,
B.CustName,
A.MatchingID
FROM CTE_HeadingGroups A
INNER JOIN CTE_Matching_Cust B
ON A.Head1 = B.Head1
AND A.Head2 = B.Head2
ORDER BY 3,1
DROP TABLE #Test
Results:
CustId CustName MatchingID
-----------------------------------------------
1 john carroll M1
2 john c M1
7 john c ln M1
2 john c M2
5 john c lynch M2
2 john c M3
6 john c l M3

Related

Best way to write TSQL query

I'm curious if there is a better way to write this query, either syntactically to make simpler/easier to follow, or for processing speed.
I've been writing a lot of similar queries recently and hoping to streamline this. I'm not a developer by trade, but I have to dive into sql
quintiles table
costcenter
quintile
quintilevalue
A
Max
50
A
Q1
8
A
Q2
12
A
Q3
14
A
Q4
18
B
Max
45
B
Q1
5
B
Q2
10
B
Q3
12
B
Q4
16
employees table
costcenter
employee
hiredate
A
W
2021-01-01
A
X
2021-02-08
B
Y
2020-12-16
B
Z
2021-01-15
workcomplete table
employee
workdate
widgetsassembled
W
2021-02-26
4
W
2021-03-05
5
X
2021-05-24
6
X
2021-05-31
3
Y
2021-04-07
2
Y
2021-04-14
8
Z
2021-02-07
4
Z
2021-02-14
1
My goal: for each record in the workcomplete table, find out what the tenure was when the employee did the work and what quintile it falls in.
employee
workdate
widgetsassembled
costcenter
tenure
smallestquintile
quintile
W
2021-02-26
4
A
8
8
Q1
W
2021-03-05
5
A
9
12
Q2
X
2021-05-24
6
A
15
18
Q4
X
2021-05-31
3
A
16
18
Q4
Y
2021-04-07
2
B
16
16
Q4
Y
2021-04-14
8
B
17
45
Max
Z
2021-02-07
4
B
4
5
Q1
Z
2021-02-14
1
B
5
5
Q1
This is what I did, it works fine:
WITH quintiles AS (
SELECT 'A' as costcenter
,'Q1' as quintile
,8 as quintilevalue
UNION SELECT 'A','Q2',12
UNION SELECT 'A','Q3',14
UNION SELECT 'A','Q4',18
UNION SELECT 'A','Max',50
UNION SELECT 'B','Q1',5
UNION SELECT 'B','Q2',10
UNION SELECT 'B','Q3',12
UNION SELECT 'B','Q4',16
UNION SELECT 'B','Max',45
),
employees AS
(
SELECT 'A' as costcenter
,'W' as employee
,'2021-01-01' as hiredate
UNION SELECT 'A','X','2021-02-08'
UNION SELECT 'B','Y','2020-12-16'
UNION SELECT 'B','Z','2021-01-15'
),
workcomplete AS
(
SELECT 'W' as employee
,'2021-02-26' as workdate
,4 as widgetsassembled
UNION SELECT 'W','2021-03-05',5
UNION SELECT 'X','2021-05-24',6
UNION SELECT 'X','2021-05-31',3
UNION SELECT 'Y','2021-04-07',2
UNION SELECT 'Y','2021-04-14',8
UNION SELECT 'Z','2021-02-07',4
UNION SELECT 'Z','2021-02-14',1
)
SELECT t.*
,q.quintile
FROM (
SELECT wc.employee
,wc.workdate
,wc.widgetsassembled
,e.costcenter
,DATEDIFF(week,e.hiredate,wc.workdate) AS tenure
,MIN(q.quintilevalue) as smallestquintile
FROM workcomplete wc
LEFT JOIN employees e
ON wc.employee = e.employee
LEFT JOIN quintiles q
ON q.costcenter = e.costcenter and DATEDIFF(week,e.hiredate,wc.workdate) <= q.quintilevalue
GROUP BY wc.employee
,wc.workdate
,wc.widgetsassembled
,e.costcenter
,DATEDIFF(week,e.hiredate,wc.workdate)
)t
LEFT JOIN quintiles q
ON t.smallestquintile = q.quintilevalue and t.costcenter = q.costcenter
This also works.
WITH quintiles AS (
SELECT 'A' as costcenter
,'Q1' as quintile
,8 as quintilevalue
UNION SELECT 'A','Q2',12
UNION SELECT 'A','Q3',14
UNION SELECT 'A','Q4',18
UNION SELECT 'A','Max',50
UNION SELECT 'B','Q1',5
UNION SELECT 'B','Q2',10
UNION SELECT 'B','Q3',12
UNION SELECT 'B','Q4',16
UNION SELECT 'B','Max',45
),
employees AS
(
SELECT 'A' as costcenter
,'W' as employee
,'2021-01-01' as hiredate
UNION SELECT 'A','X','2021-02-08'
UNION SELECT 'B','Y','2020-12-16'
UNION SELECT 'B','Z','2021-01-15'
),
workcomplete AS
(
SELECT 'W' as employee
,'2021-02-26' as workdate
,4 as widgetsassembled
UNION SELECT 'W','2021-03-05',5
UNION SELECT 'X','2021-05-24',6
UNION SELECT 'X','2021-05-31',3
UNION SELECT 'Y','2021-04-07',2
UNION SELECT 'Y','2021-04-14',8
UNION SELECT 'Z','2021-02-07',4
UNION SELECT 'Z','2021-02-14',1
)
SELECT t.*
,q.quintile
FROM (
SELECT DISTINCT wc.employee
,wc.workdate
,wc.widgetsassembled
,e.costcenter
,DATEDIFF(week,e.hiredate,wc.workdate) AS tenure
,MIN(q.quintilevalue) OVER (PARTITION BY wc.employee, wc.workdate) as smallestquintile
FROM workcomplete wc
LEFT JOIN employees e
ON wc.employee = e.employee
LEFT JOIN quintiles q
ON q.costcenter = e.costcenter and DATEDIFF(week,e.hiredate,wc.workdate) <= q.quintilevalue
)t
LEFT JOIN quintiles q
ON t.smallestquintile = q.quintilevalue and t.costcenter = q.costcenter
Is there a simpler way to do this, without nesting selects?
It sounds like you just need a top-1-per-group query.
The standard solution for that is to use ROW_NUMBER
WITH quintiles AS (
SELECT 'A' as costcenter
,'Q1' as quintile
,8 as quintilevalue
UNION SELECT 'A','Q2',12
UNION SELECT 'A','Q3',14
UNION SELECT 'A','Q4',18
UNION SELECT 'A','Max',50
UNION SELECT 'B','Q1',5
UNION SELECT 'B','Q2',10
UNION SELECT 'B','Q3',12
UNION SELECT 'B','Q4',16
UNION SELECT 'B','Max',45
),
employees AS
(
SELECT 'A' as costcenter
,'W' as employee
,'2021-01-01' as hiredate
UNION SELECT 'A','X','2021-02-08'
UNION SELECT 'B','Y','2020-12-16'
UNION SELECT 'B','Z','2021-01-15'
),
workcomplete AS
(
SELECT 'W' as employee
,'2021-02-26' as workdate
,4 as widgetsassembled
UNION SELECT 'W','2021-03-05',5
UNION SELECT 'X','2021-05-24',6
UNION SELECT 'X','2021-05-31',3
UNION SELECT 'Y','2021-04-07',2
UNION SELECT 'Y','2021-04-14',8
UNION SELECT 'Z','2021-02-07',4
UNION SELECT 'Z','2021-02-14',1
)
SELECT t.*
FROM (
SELECT wc.employee
,wc.workdate
,wc.widgetsassembled
,e.costcenter
,DATEDIFF(week,e.hiredate,wc.workdate) AS tenure
,q.quintile
,ROW_NUMBER() OVER (PARTITION BY wc.employee, wc.workdate ORDER BY q.quintilevalue) as rn
FROM workcomplete wc
LEFT JOIN employees e
ON wc.employee = e.employee
LEFT JOIN quintiles q
ON q.costcenter = e.costcenter and DATEDIFF(week,e.hiredate,wc.workdate) <= q.quintilevalue
)t
WHERE rn = 1;
db<>fiddle

Count dynamic items

I have a dynamic data Eg:
Name Date Class
______________
John 15/06/2019 AB
John 14/06/2019 B
John 13/06/2019 C
Tom 14/06/2019 C
Tom 13/06/2019 D
I want the result:
Name ClassA ClassB ClassC ClassD ClassCount
_____________________
John 1 2 1 0 4
Tom 0 0 1 1 2
I try someway,but it's not work.
thks.
You need some dynamic SQL to execute to get your desired result. Check this following script.
Note: Considering class name A to Z
Check this Demo for more details.
DECLARE #ColumnList VARCHAR(MAX)
DECLARE #sqlCommand VARCHAR(MAX)
;WITH CTE(Class,Name,Date)
AS
(
SELECT A.Cl Class,B.Name,B.Date
FROM (
SELECT 'A' Cl UNION ALL SELECT 'B' UNION ALL SELECT 'C' UNION ALL SELECT 'D' UNION ALL SELECT 'E' UNION ALL
SELECT 'F' UNION ALL SELECT 'G' UNION ALL SELECT 'H' UNION ALL SELECT 'I' UNION ALL SELECT 'J' UNION ALL
SELECT 'K' UNION ALL SELECT 'L' UNION ALL SELECT 'M' UNION ALL SELECT 'N' UNION ALL SELECT 'O' UNION ALL
SELECT 'P' UNION ALL SELECT 'Q' UNION ALL SELECT 'R' UNION ALL SELECT 'S' UNION ALL SELECT 'T' UNION ALL
SELECT 'U' UNION ALL SELECT 'V' UNION ALL SELECT 'W' UNION ALL SELECT 'X' UNION ALL SELECT 'Y' UNION ALL SELECT 'Z'
)A
INNER JOIN <your_table> B
ON B.Class LIKE '%' + A.Cl + '%'
)
SELECT #ColumnList =
STUFF((SELECT DISTINCT ('],[' + A.Class)
FROM CTE A
--ORDER BY (A.Class)
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')+']'
FROM CTE
SET #sqlCommand=
N'
SELECT
Name,A ClassA, B ClassB,C ClassC,D ClassD,
A+B+C+D ClassCount
FROM
(
SELECT Name,'+SUBSTRING(#ColumnList,2,LEN(#ColumnList))+'
FROM
(
SELECT A.Cl Class,B.Name,B.Date
FROM (
SELECT ''A'' Cl UNION ALL SELECT ''B'' UNION ALL SELECT ''C'' UNION ALL SELECT ''D'' UNION ALL SELECT ''E'' UNION ALL
SELECT ''F'' UNION ALL SELECT ''G'' UNION ALL SELECT ''H'' UNION ALL SELECT ''I'' UNION ALL SELECT ''J'' UNION ALL
SELECT ''K'' UNION ALL SELECT ''L'' UNION ALL SELECT ''M'' UNION ALL SELECT ''N'' UNION ALL SELECT ''O'' UNION ALL
SELECT ''P'' UNION ALL SELECT ''Q'' UNION ALL SELECT ''R'' UNION ALL SELECT ''S'' UNION ALL SELECT ''T'' UNION ALL
SELECT ''U'' UNION ALL SELECT ''V'' UNION ALL SELECT ''W'' UNION ALL SELECT ''X'' UNION ALL SELECT ''Y'' UNION ALL SELECT ''Z''
)A
INNER JOIN <your_table> B
ON B.Class LIKE ''%'' + A.Cl + ''%''
)A
PIVOT(
COUNT(Date)
FOR Class IN('+SUBSTRING(#ColumnList,2,LEN(#ColumnList))+')
)PVT
)AA'
--PRINT #sqlCommand
EXEC (#sqlCommand)

. It’s working fine when I pass 10-15 input but taking more than 5 minutes for 30 input. I need to make it work for 100 inputs

Here is my ask:
Go through the code and understand it.
As first solution, query should complete within 10 secs for 30 input
It should be working with good performance for 100 input as well.
My code:
/**************************************************
Populating the Array values in table variable
**************************************************/
DECLARE #PUZZLE table(
ID int IDENTITY(1,1) NOT NULL,
Value int NOT NULL)
/****Sample 1*****/
INSERT INTO #PUZZLE (value)
--SELECT 0 UNION ALL
--SELECT -22 UNION ALL
--SELECT -33 UNION ALL
--SELECT -44 UNION ALL
--SELECT 55 UNION ALL
--SELECT -100 UNION ALL
--SELECT 100 UNION ALL
--SELECT 10 UNION ALL
--SELECT -30 UNION ALL
--SELECT -60 UNION ALL
--SELECT -60 UNION ALL
SELECT -60 UNION ALL
SELECT -10 UNION ALL
SELECT 10 UNION ALL
SELECT 10 UNION ALL
SELECT -10 UNION ALL
SELECT 0 UNION ALL
SELECT -22 UNION ALL
SELECT -33 UNION ALL
SELECT -44 UNION ALL
SELECT 55 UNION ALL
SELECT -100 UNION ALL
SELECT 100 UNION ALL
SELECT 10 UNION ALL
SELECT -30 UNION ALL
SELECT -60 UNION ALL
SELECT -60 UNION ALL
SELECT -60 UNION ALL
SELECT -10 UNION ALL
SELECT 10 UNION ALL
SELECT 10
/**************************************************
Populating possible hierarchy/path
**************************************************/
DECLARE #puzHierarchy table (parentid int, childid int,value int)
INSERT #puzHierarchy (parentid,childid,value)
SELECT *-- INTO #puzHierarchy
FROM (
SELECT NULL AS ParentId,ID AS ChildId, Value
FROM #PUZZLE
WHERE ID = (SELECT MIN(ID) FROM #PUZZLE)
UNION ALL
SELECT B.Id,C.ID,C.Value
FROM #PUZZLE B
JOIN #PUZZLE C
ON C.ID > B.ID AND C.ID < (B.ID + 7)
) A
--SELECT * FROM #puzHierarchy order by parentid
/*******************************************************
Logic using recursive CTE to get the path with max value
*******************************************************/
;WITH children AS
(
SELECT ParentId
,CAST(ISNULL(CAST(ParentId AS NVARCHAR) + '->' ,'') + CAST(ChildId AS NVARCHAR) AS NVARCHAR(Max)) AS Path
,value As PathValue
FROM #puzHierarchy
WHERE ChildId = (SELECT MAX(ChildId) FROM #puzHierarchy)
UNION ALL
SELECT t.ParentId
,list= CAST(ISNULL(CAST(t.ParentId AS NVARCHAR) + '->' ,'') + d.Path AS NVARCHAR(Max))
,(t.value+d.PathValue) As PathValue
FROM #puzHierarchy t
INNER JOIN children AS d
ON t.ChildId = d.ParentId
)
SELECT [Path],PathValue
FROM children c
WHERE ParentId IS NULL
AND c.PathValue = (SELECT max(PathValue) FROM children WHERE ParentId IS NULL)
A. Your code goes through too many cycles/data unrelated to result you want.
B. After running your sample data, the results are not accurate.
Parentid Path PathValue
NULL 1->3->4->6->10->12->13->19->20 145
NULL 1->3->4->10->12->13->19->20 145
The first result is wrong.
Basically you just want starting from ParentId IS NULL and ChildId = 1, among ParentId = 1 finding which ChildId has the MAX value, this ChildId becomes ParentID to find next MAX value, and so on.
;WITH cte_base AS (SELECT Parentid
, Childid
, Value
, ROW_NUMBER() OVER(PARTITION BY Parentid ORDER BY Value DESC) AS Rownum
FROM #puzHierarchy
), cte_re AS (SELECT ParentId
, Childid
, CAST(CAST(ChildId AS NVARCHAR) AS NVARCHAR(Max)) AS Path
, Value As PathValue
, Rownum
FROM cte_base
WHERE Parentid IS NULL
UNION ALL
SELECT b.parentid, b.childid
, CAST(Path + '->' + ISNULL(CAST(b.parentid AS NVARCHAR) ,'') AS NVARCHAR(Max))
,(b.value + r.PathValue) As PathValue
, b.Rownum
FROM cte_base AS b
INNER JOIN cte_re AS r
ON b.Parentid = r.childid
where b.Rownum = 1
)
SELECT *
FROM cte_re
(I changed your sample table variable to a temporary table.)

Count New Response and Cumulative Percent Per Month

If I have several customers responding month after month, I want to count them only in the first month they responded. I can achieve this by creating temp tables for each month and comparing month over month, but it looks ugly with several temp tables. I'm pretty sure that there's a better way to do this (I don't know if Rank() will work). Can someone show me code please?
declare #Something table
(
CustID Char(10),
MthId char(2),
ResponseDate datetime
)
insert #Something
select 'Cust1', '1', '5/6/13' union all
select 'Cust1', '2', '6/13/13' union all
select 'Cust1', '3', '7/13/13' union all
select 'Cust2', '1', '5/20/13' union all
select 'Cust2', '2', '6/22/13' union all
select 'Cust3', '2', '6/20/13' union all
select 'Cust4', '2', '6/24/13' union all
select 'Cust4', '3', '7/24/13' union all
select 'Cust5', '4', '8/28/13' union all
select 'Cust6', '3', '7/24/13'
This is the output I'm expecting in 3 columns (I don't really need the 2nd col - it's there to explain further)
Month, How many "cumulative" new customers responded that month, What's the cumulative percent of total customers contacted new every month.
MthId NewCustomerResponse CumulativeNewCustomerResponse Cumulative%Responded
1 2 2 33.3
2 2 4 66.7
3 1 5 83.3
4 1 6 100.0
Including your new column:
SQLFiddle to the solution
;with cte as(
select
ROW_NUMBER() over (partition by CustID order by responseDate) as seq
,* from #Something
)
,cte2 as(
select
MthId
,(select Count(*) from cte t2 where t1.MthId=t2.MthId and t2.seq=1) NewCustomerResponse
,(select COUNT(*) from cte t2 where t1.MthId>=t2.MthId and t2.seq=1) CumulativeNewCustomerResponse
,(select COUNT(CustID) from cte where seq=1) total
from cte t1
group by MthId
)
select
MthID
,NewCustomerResponse
,CumulativeNewCustomerResponse
,(cast(CumulativeNewCustomerResponse as decimal(3,1))/CAST(total as decimal(3,1)))*100
from cte2 t1
check this,
declare #Something table
(
CustID Char(10),
MthId char(2),
ResponseDate datetime
)
insert into #Something
select 'Cust1', '1', '5/6/13'
union all
select 'Cust1', '2', '6/13/13'
union all
select 'Cust1', '3', '7/13/13'
union all
select 'Cust2', '1', '5/20/13'
union all
select 'Cust2', '2', '6/22/13'
union all
select 'Cust3', '2', '6/20/13' union all
select 'Cust4', '2', '6/24/13' union all
select 'Cust4', '3', '7/24/13' union all
select 'Cust5', '4', '8/28/13' union all
select 'Cust6', '3', '7/24/13'
;with CTE as
(select *,dense_rank()over(partition by custid order by mthid)rn from #Something)
,CTE1 as
(select a.MthId,count(*) NewCustomerResponse from cte a where rn=1 group by a.MthId )
,cte2 as
(select sum(NewCustomerResponse) totalresponse from cte1)
,cte4 as
(
select a.MthId
,(Select sum(NewCustomerResponse) from CTE1 c where c.mthid<=a.mthid) CumulativeNewCustomerResponse
from cte1 a cross apply cte2 b
)
select a.MthId,a.NewCustomerResponse
,(Select sum(NewCustomerResponse)from CTE1 c where c.mthid<=a.mthid)CumulativeNewCustomerResponse
,case when b.totalresponse>0 then cast((d.CumulativeNewCustomerResponse /cast(b.totalresponse as float))*100 as decimal(10,2)) else 0 end [Cumulative%Responded]
from cte1 a
inner join cte4 d on a.MthId=d.MthId
cross apply cte2 b

Group a set of chain values

I have the following table in SQL Server 2008
DECLARE #UnitConvert table
(
ID int identity(1,1),
ConvertUnitOne nvarchar(50),
ConvertUnitTwo nvarchar(50)
)
INSERT INTO #UnitConvert
SELECT 100,500
UNION ALL SELECT 200,100
UNION ALL SELECT 500,300
UNION ALL SELECT 2000,1000
UNION ALL SELECT 3000,9000
UNION ALL SELECT 2000,700
UNION ALL SELECT 820,3000
SELECT * FROM #UnitConvert
Here value in UnitConvertOne is equivalent to UnitConvertTwo
So it has a chain of value linking
So i want to display the result like
Group unit
1 100
200
300
500
2 700
1000
2000
3 820
3000
9000
Group value will be autoincrement based on the number of groups can be created
Unit value can be sorted from small to large value
Thanks to Eugene Elutin from sqlservercentral.com
DECLARE #UnitConvert table
(
ID int identity(1,1),
ConvertUnitOne nvarchar(50),
ConvertUnitTwo nvarchar(50)
)
INSERT INTO #UnitConvert
SELECT 100,500
UNION ALL SELECT 200,100
UNION ALL SELECT 500,300
UNION ALL SELECT 2000,1000
UNION ALL SELECT 3000,9000
UNION ALL SELECT 2000,700
UNION ALL SELECT 820,3000
;WITH cteUP AS
(
SELECT ConvertUnitTwo AS childUP, ConvertUnitOne AS unitUP, 0 AS Lvl
FROM #UnitConvert
UNION ALL
SELECT cte.childUP, u.ConvertUnitOne AS unitUP, Lvl = Lvl + 1
FROM #UnitConvert u
INNER JOIN cteUP cte ON cte.unitUP = u.ConvertUnitTwo
)
--select * from cteUP
SELECT c.ConvertUnit
,DENSE_RANK() OVER (ORDER BY ISNULL(cm.unitUP, c.ConvertUnit)) AS GrpNO
FROM (SELECT ConvertUnitOne AS ConvertUnit FROM #UnitConvert
UNION
SELECT ConvertUnitTwo AS ConvertUnit FROM #UnitConvert) c
OUTER APPLY (SELECT TOP 1 unitUP FROM cteUP m WHERE
m.childUP = c.ConvertUnit ORDER BY Lvl DESC) cm

Resources