I must say I'm quite disappointed I could not solve this problem in a simple way.
Maybe It's something beyond my skills.
Anyway this is the best I could put together:
IF OBJECT_ID('Tempdb..#TmpTbl') IS NOT NULL DROP TABLE #TmpTbl
CREATE TABLE #TmpTbl (TblID INT PRIMARY KEY, MultiDelimStr VARCHAR(255))
INSERT #TmpTbl ( TblID, MultiDelimStr )
SELECT 101, 'Orange,Apple;Orange,Grape,Banana;Apple,Kiwi,Grape'
UNION
SELECT 202, 'Grape,Apple;Orange,Grape,Banana;Peach,Kiwi,Grape'
UNION
SELECT 303, 'Orange;Peach,Grape,Banana;Apple,Kiwi;Tangerine,Grapefruit'
;WITH Splits AS (
SELECT TblId, GRP_ID = B.Id, C.Value AS Fruit, C.Id AS Fruit_id, NumGroups = MAX(B.Id) OVER(PARTITION BY TblId)
FROM #TmpTbl AS A
CROSS APPLY dbo.fSplit(MultiDelimStr, ';') AS B
CROSS APPLY dbo.fSplit(B.Value, ',') AS C
),
ThreeRows AS (
SELECT N
FROM Tally
WHERE N <= 3
),
FourRows AS (
SELECT N
FROM Tally
WHERE N <= 4
),
CrossThree AS (
SELECT A.N AS [1], B.N AS [2], C.N AS [3]
FROM ThreeRows AS A
CROSS JOIN ThreeRows AS B
CROSS JOIN ThreeRows AS C
),
CrossFour AS (
SELECT A.N AS [1], B.N AS [2], C.N AS [3], D.N AS [4]
FROM FourRows AS A
CROSS JOIN ThreeRows AS B
CROSS JOIN ThreeRows AS C
CROSS JOIN ThreeRows AS D
)
SELECT DISTINCT B.TblId, B.Fruit + ',' + C.Fruit + ',' + D.Fruit
FROM CrossThree AS A
INNER JOIN Splits AS B
ON B.GRP_ID = 1
AND B.Fruit_ID = [1]
INNER JOIN Splits AS C
ON C.GRP_ID = 2
AND C.TblId = B.TblId
AND C.Fruit_ID = [2]
AND C.Fruit <> B.Fruit
INNER JOIN Splits AS D
ON D.GRP_ID = 3
AND D.TblId = C.TblId
AND D.Fruit_ID = [3]
AND D.Fruit <> B.Fruit
AND D.Fruit <> C.Fruit
WHERE B.NumGroups = 3
UNION ALL
SELECT DISTINCT B.TblId, B.Fruit + ',' + C.Fruit + ',' + D.Fruit + ',' + E.Fruit
FROM CrossFour AS A
INNER JOIN Splits AS B
ON B.GRP_ID = 1
AND B.Fruit_ID = [1]
INNER JOIN Splits AS C
ON C.GRP_ID = 2
AND C.TblId = B.TblId
AND C.Fruit_ID = [2]
AND C.Fruit <> B.Fruit
INNER JOIN Splits AS D
ON D.GRP_ID = 3
AND D.TblId = C.TblId
AND D.Fruit_ID = [3]
AND D.Fruit <> B.Fruit
AND D.Fruit <> C.Fruit
INNER JOIN Splits AS E
ON E.GRP_ID = 4
AND E.TblId = D.TblId
AND E.Fruit_ID = [4]
AND E.Fruit <> B.Fruit
AND E.Fruit <> C.Fruit
AND E.Fruit <> D.Fruit
WHERE B.NumGroups = 4
Basically the code builds two work tables (CrossThree and CrossFour) with "N" columns, holding all N^N possible combinations of values between 1 and "N".
Once the work tables are in place, I join them back to the splitted strings, choosing the right worktable looking at the number of groups.
The main thing I don't like about this code is that it relies on fixed numbers of groups, so that it should be generated dynamically after a first pass on the input data to gather the minimum/maximum number of groups.
I think this problem could be solved much better with a CLR aggregate, but it's a technique I don't master.
I hope someone can help with something better than this.
Gianluca
-- Gianluca Sartori