首先,您需要一个包含 ItemNumber 的 拆分器,为此您可以使用 delimitedSplit8k。那,APPLY 运营商 window aggregate function 和一些 bitwise 逻辑,你可以这样做:
-- Sample Data
DECLARE @t1 TABLE (stringID INT IDENTITY, String VARCHAR(100));
INSERT @t1 VALUES('1A,2B,2A'),('2B,1A'),('1B,2A,2B,1A'),('1B,1A');
-- Solution
WITH f AS
(
SELECT
t.stringID,
Item = part.One+Part.Two,
Chk = COUNT(part.One) OVER (PARTITION BY t.StringID, part.One ORDER BY s.ItemNumber) &
COUNT(part.Two) OVER (PARTITION BY t.StringID, part.Two ORDER BY s.ItemNumber)
FROM @t1 AS t
CROSS APPLY dbo.delimitedSplit8k(t.String,',') AS s
CROSS APPLY (VALUES(SUBSTRING(s.Item,1,1),SUBSTRING(s.Item,2,1))) part(One,Two)
)
SELECT ID = f.stringID, item.Combo
FROM f
CROSS APPLY
(
SELECT STUFF((
SELECT ','+f2.Item
FROM f AS f2
WHERE f2.Chk = 1 AND f2.stringID = f.stringID
ORDER BY f2.Item
FOR XML PATH('')),1,1,'')
) AS item(Combo)
WHERE CHARINDEX(f.item, item.Combo) & f.Chk = 1;
返回:
ID Combo
----------- ------------
1 1A,2B
2 1A,2B
3 1B,2A
4 1B
更新 2019114 基于 OP cmets:
如果您无法创建函数或在 PDW 中不允许使用有关 DelimitedSplit8K 的内容,您可以使用 XML 创建一个 内联拆分器(不是我的第一选择,但它并不可怕,并且可以满足您的需求'正在做。)
-- Sample Data
DECLARE @t1 TABLE (stringID INT IDENTITY, String VARCHAR(100));
INSERT @t1 VALUES('1A,2B,2A'),('2B,1A'),('1B,2A,2B,1A'),('1B,1A');
WITH f AS
(
SELECT
t.stringID,
Item = part.One+Part.Two,
Chk = COUNT(part.One) OVER (PARTITION BY t.StringID, part.One ORDER BY s.ItemNumber) &
COUNT(part.Two) OVER (PARTITION BY t.StringID, part.Two ORDER BY s.ItemNumber)
FROM @t1 AS t
--CROSS APPLY dbo.delimitedSplit8k(t.String,',') AS s
CROSS APPLY
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)), x.xxx.value('(text())[1]','varchar(8000)')
FROM (VALUES(CAST(CONCAT('<Z><x>',REPLACE(t.string, ',','</x><x>'),'</x></Z>') AS XML))) AS f(NS)
CROSS APPLY f.NS.nodes('Z/x') AS x(xxx)
) AS s(ItemNumber,Item)
CROSS APPLY (VALUES(SUBSTRING(s.Item,1,1),SUBSTRING(s.Item,2,1))) part(One,Two)
)
SELECT ID = f.stringID, item.Combo
FROM f
CROSS APPLY
(
SELECT STUFF((
SELECT ','+f2.Item
FROM f AS f2
WHERE f2.Chk = 1 AND f2.stringID = f.stringID
ORDER BY f2.Item
FOR XML PATH('')),1,1,'')
) AS item(Combo)
WHERE CHARINDEX(f.item, item.Combo) & f.Chk = 1;
另外,我记得曾经在 PDW 中遇到过类似的问题,我们可以使用递归 CTE 解决它。如果您可以创建函数,则可以将其用作拆分器:
ALTER FUNCTION dbo.rCteSplitter(@string VARCHAR(8000), @delim CHAR(1))
RETURNS TABLE AS RETURN
WITH a(N,X,XX) AS
(
SELECT 1, f.CI, SUBSTRING(@string,1,f.CI-1)
FROM (VALUES(ISNULL(NULLIF(CHARINDEX(@delim,@string),0),LEN(@string)+1))) AS f(CI)
UNION ALL
SELECT N+1, f.CI, SUBSTRING(@string,X+1,f.CI-X-1)
FROM a
CROSS APPLY (VALUES(ISNULL(NULLIF(CHARINDEX(@delim,@string,X+1),0),LEN(@string)+1))) AS f(CI)
WHERE N <= LEN(@string)-LEN(REPLACE(@string,@delim,''))
)
SELECT
ItemNumber = a.N,
ItemIndex = a.X,
Item = a.XX
FROM a;
GO
那么解决方案将如下所示:
-- Sample Data
DECLARE @t1 TABLE (stringID INT IDENTITY, String VARCHAR(100));
INSERT @t1 VALUES('1A,2B,2A'),('2B,1A'),('1B,2A,2B,1A'),('1B,1A');
WITH f AS
(
SELECT
t.stringID,
Item = part.One+Part.Two,
Chk = COUNT(part.One) OVER (PARTITION BY t.StringID, part.One ORDER BY s.ItemNumber) &
COUNT(part.Two) OVER (PARTITION BY t.StringID, part.Two ORDER BY s.ItemNumber)
FROM @t1 AS t
CROSS APPLY dbo.rCteSplitter(t.String,',') AS s
CROSS APPLY (VALUES(SUBSTRING(s.Item,1,1),SUBSTRING(s.Item,2,1))) part(One,Two)
)
SELECT ID = f.stringID, item.Combo
FROM f
CROSS APPLY
(
SELECT STUFF((
SELECT ','+f2.Item
FROM f AS f2
WHERE f2.Chk = 1 AND f2.stringID = f.stringID
ORDER BY f2.Item
FOR XML PATH('')),1,1,'')
) AS item(Combo)
WHERE CHARINDEX(f.item, item.Combo) & f.Chk = 1;