Nachdem ich einige vielleicht bissige Kommentare abgegeben hatte, blieb dieses Problem den ganzen Abend in meinem Kopf hängen, und ich kam schließlich auf den folgenden satzbasierten Ansatz. Ich glaube, es ist definitiv "elegant", aber ich denke auch, dass es "irgendwie dumm" ist. Sie tätigen den Anruf.
Richten Sie zuerst einige Tabellen ein:
-- For testing purposes
DROP TABLE Source
DROP TABLE Numbers
DROP TABLE Results
-- Add as many rows as need be processed--though note that you get N! (number of rows, factorial) results,
-- and that gets big fast. The Identity column must start at 1, or the algorithm will have to be adjusted.
-- Element could be more than char(1), though the algorithm would have to be adjusted again, and each element
-- must be the same length.
CREATE TABLE Source
(
SourceId int not null identity(1,1)
,Element char(1) not null
)
INSERT Source (Element) values ('A')
INSERT Source (Element) values ('B')
INSERT Source (Element) values ('C')
INSERT Source (Element) values ('D')
--INSERT Source (Element) values ('E')
--INSERT Source (Element) values ('F')
-- This is a standard Tally table (or "table of numbers")
-- It only needs to be as long as there are elements in table Source
CREATE TABLE Numbers (Number int not null)
INSERT Numbers (Number) values (1)
INSERT Numbers (Number) values (2)
INSERT Numbers (Number) values (3)
INSERT Numbers (Number) values (4)
INSERT Numbers (Number) values (5)
INSERT Numbers (Number) values (6)
INSERT Numbers (Number) values (7)
INSERT Numbers (Number) values (8)
INSERT Numbers (Number) values (9)
INSERT Numbers (Number) values (10)
-- Results are iteratively built here. This could be a temp table. An index on "Length" might make runs
-- faster for large sets. Combo must be at least as long as there are characters to be permuted.
CREATE TABLE Results
(
Combo varchar(10) not null
,Length int not null
)
Hier ist die Routine:
SET NOCOUNT on
DECLARE
@Loop int
,@MaxLoop int
-- How many elements there are to process
SELECT @MaxLoop = max(SourceId)
from Source
-- Initialize first value
TRUNCATE TABLE Results
INSERT Results (Combo, Length)
select Element, 1
from Source
where SourceId = 1
SET @Loop = 2
-- Iterate to add each element after the first
WHILE @Loop <= @MaxLoop
BEGIN
-- See comments below. Note that the "distinct" remove duplicates, if a given value
-- is to be included more than once
INSERT Results (Combo, Length)
select distinct
left(re.Combo, @Loop - nm.Number)
+ so.Element
+ right(re.Combo, nm.Number - 1)
,@Loop
from Results re
inner join Numbers nm
on nm.Number <= @Loop
inner join Source so
on so.SourceId = @Loop
where re.Length = @Loop - 1
-- For performance, add this in if sets will be large
--DELETE Results
-- where Length <> @Loop
SET @Loop = @Loop + 1
END
-- Show results
SELECT *
from Results
where Length = @MaxLoop
order by Combo
Die allgemeine Idee ist:Wenn Sie ein neues Element (z. B. „B“) zu einer beliebigen Zeichenfolge (z. B. „A“) hinzufügen, würden Sie B an allen möglichen Positionen (Ba, aB) hinzufügen, um alle Permutationen zu erfassen, was zu einem neuen Satz von führt Saiten. Iterieren Sie dann:Fügen Sie an jeder Position in einer Zeichenfolge (AB wird zu Cab, aCb, abC) ein neues Element (C) für alle Zeichenfolgen (Cba, bCa, baC) hinzu, und Sie haben den Satz von Permutationen. Wiederholen Sie jede Ergebnismenge mit dem nächsten Zeichen, bis Ihnen die Zeichen oder Ressourcen ausgehen. 10 Elemente sind 3,6 Millionen Permutationen, ungefähr 48 MB mit dem obigen Algorithmus, und 14 (eindeutige) Elemente würden 87 Milliarden Permutationen und 1,163 Terabyte erreichen.
Ich bin mir sicher, dass es schließlich in einen CTE eingeklemmt werden könnte, aber am Ende wäre alles, was es wäre, eine verherrlichte Schleife. Die Logik ist auf diese Weise klarer, und ich komme nicht umhin zu denken, dass der CTE-Ausführungsplan ein Albtraum wäre.