Create table test123 (
CustId int,
[Level] int,
RowNum int,
USAFlag bit
)
insert into test123(CustId,[Level],RowNum,USAFlag)values
(101,1,1,0),
(102,2,1,0),
(102,2,2,1),
(103,3,1,0),
(103,3,2,1),
(103,3,3,0),
(104,4,1,1),
(104,4,2,0),
(104,4,3,0),
(104,4,4,1),
(105,2,1,1),
(105,2,2,0),
(106,2,1,0),
(106,2,2,0),
(107,3,1,0),
(107,3,2,0),
(107,3,3,1),
(108,1,1,1)
OUtput
CustID USARootLeaf
101 ONlyONeLevel_NonUSA
102 Leaf_USA
103 Root_Leaf_NonUSA
104 Root_Leaf_USA
105 Root_USA
106 Root_Leaf_NonUSA
107 Leaf_USA
108 OnlyOneLvel_USA
Logic: If Level is 1 then USARootLeaf value should be OnlyOneLvel_USA or OnlyOneLvel_NonUSA based on USAFlag value
If Level is >1, then USARootLeaf value should be Root_Leaf_USA,Root_Leaf_NonUSA,Root_USA,Leaf_USA based on min(level) and max(level) value of USAFlag is true/false
I have never used so many CTE's in a query before and I am sure there is a more efficient way, but this works.
--Add Row Numbers
WITH preselect AS (SELECT ROW_NUMBER() OVER(PARTITION BY CustId ORDER BY
CustId)'RowNum',CustId
FROM test123),
--Get the first and last Row
preselect2 AS (SELECT MIN(RowNum)'MinRow',MAX(RowNum)'MaxRow',CustId
FROM preselect
GROUP BY CustId),
--Join to table with data
preselect3 AS (SELECT ROW_NUMBER() OVER(PARTITION BY t.CustId ORDER BY
t.CustId)'RowNumber',t.CustId,t.Level,
t.RowNum,t.USAFlag,p2.MinRow,p2.MaxRow
FROM test123 t
JOIN preselect2 p2 ON t.CustId = p2.CustId),
--Filter out any row that isn't the first or last in the group
preselect4 AS (SELECT *
FROM preselect3
WHERE RowNumber = MinRow
OR
RowNumber = MaxRow),
--Get count of each CustId
preselect5 AS (SELECT CustId,COUNT(*)'Cnt'
FROM test123
GROUP BY CustId),
--Determine the properties for each CustId
preselect6 AS (
SELECT p4.CustId,
SUM(CASE WHEN Cnt > 1 THEN 1 ELSE 0 END) AS 'Multi',
SUM(CASE WHEN RowNumber = 1 AND USAFlag = 1 THEN 1 ELSE 0 END)AS 'RootUSA',
SUM(CASE WHEN RowNumber > 1 AND USAFlag = 1 THEN 1 ELSE 0 END)AS 'LeafUSA'
FROM preselect4 p4
JOIN preselect5 p5 ON p4.CustId = p5.CustId
GROUP BY p4.CustId)
--Apply values based on their properties
SELECT CustId,
CASE WHEN Multi = 0 AND RootUSA = 0 THEN 'OnlyOneLevel_NonUSA'
WHEN Multi = 0 AND RootUSA = 1 THEN 'OnlyOneLevel_USA'
WHEN Multi > 1 AND RootUSA = 0 AND LeafUSA = 1 THEN 'Leaf_USA'
WHEN Multi > 1 AND RootUSA = 0 AND LeafUSA = 0 THEN 'Root_Leaf_NonUSA'
WHEN Multi > 1 AND RootUSA = 1 AND LeafUSA = 1 THEN 'Root_Leaf_USA'
WHEN Multi > 1 AND RootUSA = 1 AND LeafUSA = 0 THEN 'Root_USA'
ELSE 'N/A' END AS 'USARootLeaf'
FROM preselect6
This could be refactored to be smaller, but here is the a guess at the implementation based on the requirements above. Note that I changed [Level] to thisLevel in my DDL.
mysql> select q.custid, concat(q.type,concat('_',q.starsandstripes)) as type from (
-> select
-> z.custid,
-> case when minlevel = 1 and maxlevel = 1 then 'OnlyOneLevel'
-> else
-> case when lastflag = 1 and firstflag = 0 then 'Leaf'
-> when lastflag = 0 and firstflag = 1 then 'Root'
-> when lastflag = firstflag then 'Root_Leaf'
-> end
-> end as type,
-> starsandstripes
-> from
-> (
-> select
-> za.custid,
-> case when max(za.usaflag) = 1
-> then 'USA'
-> else 'NonUSA'
-> end
-> as starsAndStripes
-> from test123 za join
-> (
-> select
-> custid,
-> max(rownum) as maxrow,
-> min(rownum) as minrow
-> from test123 zc
-> group by custid
-> ) zb
-> on za.custid = zb.custid
-> and (za.rownum = zb.maxrow
-> or za.rownum = zb.minrow)
-> group by za.custid
-> ) z,
-> (
-> select
-> a.custid,
-> (select usaflag from test123 d where d.custid = a.custid and d.rownum = b.maxrow) as lastFlag,
-> (select usaflag from test123 d where d.custid = a.custid and d.rownum = b.minrow) as firstFlag,
-> min(thisLevel) as minLevel,
-> max(thisLevel) as maxLevel
-> from test123 a join
-> (
-> select custid, max(rownum) as maxrow, min(rownum) as minrow
-> from test123 c
-> group by custid
-> ) b
-> on a.custid = b.custid
-> group by custid,
-> (select usaflag from test123 d where d.custid = a.custid and d.rownum = b.maxrow),
-> (select usaflag from test123 d where d.custid = a.custid and d.rownum = b.minrow)
-> ) x
-> where z.custid = x.custid
-> ) q;
Output:
+--------+---------------------+
| custid | type |
+--------+---------------------+
| 101 | OnlyOneLevel_NonUSA |
| 102 | Leaf_USA |
| 103 | Root_Leaf_NonUSA |
| 104 | Root_Leaf_USA |
| 105 | Root_USA |
| 106 | Root_Leaf_NonUSA |
| 107 | Leaf_USA |
| 108 | OnlyOneLevel_USA |
+--------+---------------------+
8 rows in set (0.00 sec)
Thanks Bryan and Jason for your quick inputs. I found a better solution myself.
with one as
(
Select CustId,[Level],
STUFF((SELECT ',' + cast(USAFlag as char(1)) FROM test123 WHERE (
CustId=Result.CustId) FOR XML PATH ('')),1,1,'') AS USAList
From test123 AS Result
GROUP BY CustId,[Level]
)
Select CustId,[level],USAList,
case
when Level=1 and substring(USAList,1,1)='1' then 'ONlyONeLevel_USA'
when Level=1 and substring(USAList,1,1)='0' then 'ONlyONeLevel_NonUSA'
when Level>1 and substring(USAList,1,1)='1' and substring(USAList,level*2-
1,1)='1' then 'Root_Leaf_USA'
when Level>1 and substring(USAList,1,1)='0' and substring(USAList,level*2-
1,1)='0' then 'Root_Leaf_NonUSA'
when Level>1 and substring(USAList,1,1)='1' then 'Root_USA'
when Level>1 and substring(USAList,level*2-1,1)='1' then 'Leaf_USA'
else 'InvalidLevel'
end as USARootLeaf
from one
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.