T-SQL | 字符串"操纵"和聚合

007*_*007 5 sql t-sql sql-server sql-server-2012

我有以下场景.

消息来源表1

CREATE TABLE #Table1 
(
     Div varchar(10), 
     Dept varchar(10), 
     States varchar(10)
)

INSERT INTO #Table1
   SELECT 'Div1','Dept1','CA,NV,TX'
   UNION ALL
   SELECT 'Div2','Dept2','MI,OH,IN'
   UNION ALL
   SELECT 'Div3','Dept2','NY,NJ,PA'
   UNION ALL
   SELECT 'Div4','Dept1',NULL
Run Code Online (Sandbox Code Playgroud)

消息来源表2

CREATE TABLE #Table2 
(
    Div varchar(10), 
    Dept varchar(10), 
    States varchar(10)
)

INSERT INTO #Table2
   SELECT 'Div1','Dept1','CA'
   UNION ALL
   SELECT 'Div1','Dept1','NV, TX'
   UNION ALL
   SELECT 'Div1','Dept1','TX, CA'
   UNION ALL
   SELECT 'Div1','Dept1','CA, NV'
   UNION ALL
   SELECT 'Div2','Dept2','MI, OH'
   UNION ALL
   SELECT 'Div2','Dept2','MI, IN'
   UNION ALL
   SELECT 'Div2','Dept2','OH'
   UNION ALL
   SELECT 'Div3','Dept2','NY, NJ, PA'
Run Code Online (Sandbox Code Playgroud)

期望的输出

CREATE TABLE #Table3 
(
    Div varchar(10), 
    Dept varchar(10), 
    States varchar(50)
)

INSERT INTO #Table3
SELECT 'Div1','Dept1','CA - (3), NV - (2), TX - (2)'
UNION ALL
SELECT 'Div2','Dept2','MI - (2), OH - (2), IN - (1)'
UNION ALL
SELECT 'Div3','Dept2','NY - (1), NJ - (1), PA - (1)'
UNION ALL
SELECT 'Div4','Dept1',NULL

SELECT * FROM #Table1
SELECT * FROM #Table2
SELECT * FROM #Table3

DROP TABLE #Table1
DROP TABLE #Table2
DROP TABLE #Table3
Run Code Online (Sandbox Code Playgroud)

SQLFIDDLE

目标:基于#Table1#Table2,加入这两个表上DivDept字段,然后汇总为不同状态的计数States领域,创造,你有一个输出Div,DeptStates旁边打印到状态的那些国家中的每一个独特的计数.

我不知道如何实现这一目标.我正在尝试,LIKE但无法弄清楚如何让它充满活力.我会继续试着看看能不能搞清楚.以为我会在这里发布这个问题,看看能否得到一些帮助.

谢谢

更新:

期望的输出

Div     Dept    States
Div1    Dept1   CA - (3), NV - (2), TX - (2)
Div2    Dept2   MI - (2), OH - (2), IN - (1)
Div3    Dept2   NY - (1), NJ - (1), PA - (1)
Div4    Dept1   NULL
Run Code Online (Sandbox Code Playgroud)

Lam*_*mak 6

好的,首先,您需要在#Temp1和中拆分连接值#Temp2.这样做有多种方法,我将使用Aaron Bertrand 这篇精彩博文中描述的数字表.所以,我们需要一个数字表,可以这样做:

;WITH n AS
(
    SELECT  x = ROW_NUMBER() OVER (ORDER BY s1.[object_id])
    FROM sys.all_objects AS s1
    CROSS JOIN sys.all_objects AS s2
)
SELECT Number = x
INTO #Numbers
FROM n
WHERE x BETWEEN 1 AND 8000;
Run Code Online (Sandbox Code Playgroud)

然后,您需要实际执行拆分,然后为结果执行组连接方法:

;WITH T1 AS
(
    SELECT *
    FROM #Table1 T
    OUTER APPLY (SELECT Item = SUBSTRING(T.States, Number,
                                         CHARINDEX(',',T.States + ',', Number) - 
                                         Number)
                 FROM #Numbers
                 WHERE Number <= CONVERT(INT, LEN(T.States))
                 AND SUBSTRING(',' + T.States, Number, LEN(',')) = ',') N
), T2 AS
(
    SELECT *
    FROM #Table2 T
    OUTER APPLY (SELECT Item = SUBSTRING(T.States, Number,
                                         CHARINDEX(', ',T.States + ', ', Number) - 
                                         Number)
                 FROM #Numbers
                 WHERE Number <= CONVERT(INT, LEN(T.States))
                 AND SUBSTRING(', ' + T.States, Number, LEN(', ')) = ', ') N
), T3 AS
(
    SELECT T1.Div, T1.Dept, T1.Item, COUNT(*) N
    FROM T1 
    LEFT JOIN T2
        ON T1.Div = T2.Div
        AND T1.Dept = T2.Dept
        AND T1.Item = T2.Item
    GROUP BY T1.Div, T1.Dept, T1.Item
)
SELECT  A.Div, 
        A.Dept, 
        States = STUFF((SELECT  ',' + CONVERT(VARCHAR(20), Item) + 
                                ' - (' + CAST(N AS VARCHAR(4)) + ')'
                        FROM T3 
                        WHERE Div = A.Div
                        AND Dept = A.Dept
                    FOR XML PATH(''), TYPE).value('.[1]','nvarchar(max)'),1,1,'')
FROM T3 A
ORDER BY Div, Dept, Item
Run Code Online (Sandbox Code Playgroud)

结果是:

?????????????????????????????????????????????
? Div  ? Dept  ?           States           ?
?????????????????????????????????????????????
? Div1 ? Dept1 ? CA - (3),NV - (2),TX - (2) ?
? Div1 ? Dept1 ? CA - (3),NV - (2),TX - (2) ?
? Div1 ? Dept1 ? CA - (3),NV - (2),TX - (2) ?
? Div2 ? Dept2 ? IN - (1),MI - (2),OH - (2) ?
? Div2 ? Dept2 ? IN - (1),MI - (2),OH - (2) ?
? Div2 ? Dept2 ? IN - (1),MI - (2),OH - (2) ?
? Div3 ? Dept2 ? NJ - (1),NY - (1),PA - (1) ?
? Div3 ? Dept2 ? NJ - (1),NY - (1),PA - (1) ?
? Div3 ? Dept2 ? NJ - (1),NY - (1),PA - (1) ?
? Div4 ? Dept1 ? NULL                       ?
?????????????????????????????????????????????
Run Code Online (Sandbox Code Playgroud)


Cod*_*ent 6

您的要求非常令人讨厌,但作为开发人员,我们必须使用我们所拥有的.这是一个广泛使用Common Table Expression(CTE)的解决方案:

;WITH
    CTE1 AS
    (
        SELECT      Div, Dept,
                    REPLACE(States,' ','') + ',' AS States
        FROM        Table2
    ),
    CTE2 AS
    (
        SELECT      c1.Div, c1.Dept,
                    LEFT(c1.States,CHARINDEX(',', c1.States)-1)                 AS IndividualState,
                    RIGHT(c1.States,LEN(c1.States)-CHARINDEX(',', c1.States))   AS RemainingStates
        FROM        CTE1    c1
        UNION ALL
        SELECT      c2.Div, c2.Dept,
                    LEFT(c2.RemainingStates,CHARINDEX(',', c2.RemainingStates)-1),
                    RIGHT(c2.RemainingStates,LEN(c2.RemainingStates) - CHARINDEX(',', c2.RemainingStates))
        FROM        CTE2    c2
        WHERE       LEN(c2.RemainingStates) > 0
    ),
    CTE3 AS
    (
        SELECT      Div, Dept,
                    IndividualState,
                    COUNT(*)            AS StateCount
        FROM        CTE2
        GROUP BY    Div, Dept, IndividualState
    ),
    CTE4 AS
    (
        SELECT      t1.Div, t1.Dept,
                    (
                        SELECT  c3.IndividualState + ' - (' + CONVERT(varchar(10),c3.StateCount) + '), ' 
                        FROM    CTE3 c3
                        WHERE   c3.Div = t1.Div AND c3.Dept = t1.Dept
                        FOR XML PATH('')
                    )       AS States
        FROM        Table1  t1
    )

SELECT  Div, Dept,
        LEFT(States, LEN(States) - 1) AS States
FROM    CTE4
Run Code Online (Sandbox Code Playgroud)

说明

  1. CTE1清理数据Table2:删除空格,添加逗号到最后
  2. CTE2 正常化
  3. CTE3 计数
  4. CTE4做最后的组装,把CA | 3CA - (3), ...

最后SELECT删除整数输出的尾随逗号.

为了更好地理解每一个步骤,可以替换最终SELECTSELECT * FROM CTE1,SELECT * FROM CTE2等等.