add
remove
split
language
show hidden
hide
db<>fiddle
Db2
DocumentDB
DuckDB
Firebird
MariaDB
MySQL
Oracle
Postgres
SQL Server
SQLite
TimescaleDB
YugabyteDB
11.1
11.5
12.1
0.114 (MongoDB 7.0)
1.4 LTS
3.0
4.0
5.0
10.2
10.3
10.4
10.5
10.6
10.7
10.8
10.9
10.11
11.4
11.8
12.3
5.5
5.6
5.7
8.0
8.4
9.7
11g Release 2
18c
21c
23ai
23c
26ai
8.4
9.3
9.4
9.5
9.6
10
11
12
13
14
15
16
17
18
19 beta 3
2012
2014
2016
2017
2017 (Linux)
2019
2019 (Linux)
2022
2025
3.8
3.16
3.27
3.39
3.45
3.53
2.11
2.14
2.28
2.6
2.8
2.18
2024.2 LTS
2025.2 LTS
no sample DB
no sample DB
no sample DB
no sample DB
no sample DB
no sample DB
no sample DB
no sample DB
no sample DB
no sample DB
no sample DB
no sample DB
no sample DB
no sample DB
no sample DB
no sample DB
no sample DB
no sample DB
no sample DB
no sample DB
no sample DB
no sample DB
no sample DB
no sample DB
Sakila
no sample DB
no sample DB
no sample DB
no sample DB
no sample DB
no sample DB
no sample DB
no sample DB
no sample DB
no sample DB
no sample DB
no sample DB
no sample DB
no sample DB
no sample DB
no sample DB
no sample DB
no sample DB
no sample DB
no sample DB
no sample DB
no sample DB
no sample DB
no sample DB
no sample DB
no sample DB
no sample DB
no sample DB
no sample DB
AdventureWorks
no sample DB
no sample DB
AdventureWorks
no sample DB
AdventureWorks
no sample DB
no sample DB
no sample DB
no sample DB
no sample DB
no sample DB
no sample DB
no sample DB
no sample DB
no sample DB
no sample DB
no sample DB
no sample DB
no sample DB
run
abort
markdown
clear
donate
feedback
about
By using db<>fiddle, you agree to license everything you submit by
Creative Commons CC0
.
CREATE TABLE Data (Key1 INT, Key2 VARCHAR(100)) INSERT Data VALUES (1, 'A'), (1, 'B'), (2, 'A'), (2, 'B'), (3, 'A'), (3, 'C'), -- Additiona Data (4, 'I'), (4, 'J'), (5, 'I'), (5, 'K'), (5, 'L'), (6, 'K'), (7, 'I'), (7, 'M'), (8, 'I'), (8, 'J'), (8, 'K'), (8, 'L'), (8, 'M'), (9, 'Z'), -- Comment this out for no possible solution (9, 'L')
21 rows affected
DECLARE @Delim1 CHAR = '|' -- Choses character must not be present in the Data DECLARE @Delim2 CHAR = '=' ;WITH CTE_Key1List AS ( -- Get a distinct list of keys. Order the keys by number of options descending, -- so that those keys with the fewest options will be processed first. (Later -- processing starts with the highest RowNum and words its way down.) This -- will hopefully reduce backtracking in a tight scenario. SELECT Key1, ROW_NUMBER() OVER(ORDER BY COUNT(*) DESC) AS RowNum FROM Data GROUP BY Key1 ), CTE_Solver AS ( -- CTE Bootstrap: -- Working from MAX(RowNum) backwards, select the first set of -- candidate mappings for the selected Key1 value -- Build a map list of the form "|Key1=Key2|Key1=Key2|...|". SELECT K.RowNum, CAST(CONCAT(@Delim1, D.Key1, @Delim2, D.Key2, @Delim1) AS VARCHAR(MAX)) AS Mapping FROM CTE_Key1List K JOIN Data D ON D.Key1 = K.Key1 WHERE K.RowNum = (SELECT MAX(RowNum) FROM CTE_Key1List) -- CTE recursion: -- For each lower RowNum value down to 1, include candidate mappings for -- the next Key1 value. Exclude any cases that attempt to reuse an -- already-assigned Key2 value. UNION ALL SELECT K.RowNum, CONCAT(S.Mapping, D.Key1, @Delim2, D.Key2, @Delim1) AS Mapping FROM CTE_Solver S JOIN CTE_Key1List K ON K.RowNum = S.RowNum - 1 JOIN Data D ON D.Key1 = K.Key1 AND CHARINDEX(CONCAT(@Delim2, D.Key2, @Delim1), S.Mapping) = 0 -- Unused WHERE S.RowNum > 1 ), CTE_Result AS ( SELECT R.Key1, R.Key2 FROM ( -- Get just one sucessful mapping out of possibly many SELECT TOP 1 TRIM(@Delim1 FROM S.Mapping) AS TrimmedMapping FROM CTE_Solver S WHERE S.RowNum = 1 ) S -- Split the mapping list into individual Key1=Key2 pairs CROSS APPLY STRING_SPLIT(S.TrimmedMapping, @Delim1) M -- Locate the = delimiter and separate the two values. CROSS APPLY (SELECT CHARINDEX(@Delim2, M.value) SplitPos) P CROSS APPLY ( SELECT Key1 = CAST(LEFT(M.value, P.SplitPos - 1) AS INT), Key2 = STUFF(M.value, 1, P.SplitPos, '') ) R ) --SELECT * FROM CTE_Solver S -- All working steps (unordered) --SELECT * FROM CTE_Solver S ORDER BY MapList -- All working steps --SELECT * FROM CTE_Solver S WHERE S.RowNum = 1 -- All solutions --SELECT TOP 1 * FROM CTE_Solver S WHERE S.RowNum = 1 -- One solution SELECT * FROM CTE_Result ORDER BY Key1 -- Solution details
Key1
Key2
1
A
2
B
3
C
4
J
5
L
6
K
7
M
8
I
9
Z