sql >> Database >  >> RDS >> Sqlserver

De meest elegante manier om permutaties in SQL-server te genereren

Na een aantal misschien snarige opmerkingen te hebben gemaakt, bleef dit probleem de hele avond in mijn hoofd hangen, en uiteindelijk kwam ik op de volgende set-gebaseerde aanpak. Ik geloof dat het zeker kwalificeert als "elegant", maar dan denk ik ook dat het kwalificeert als "een beetje dom". Jij belt.

Stel eerst wat tabellen in:

--  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
 )

Dit is de 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

Het algemene idee is:als je een nieuw element (zeg "B") aan een string toevoegt (zeg "A"), om alle permutaties op te vangen, zou je B toevoegen aan alle mogelijke posities (Ba, aB), wat resulteert in een nieuwe set van snaren. Herhaal dan:Voeg een nieuw element (C) toe aan elke positie in een string (AB wordt Cab, aCb, abC), voor alle strings (Cba, bCa, baC), en je hebt de set permutaties. Herhaal elke resultaatset met het volgende teken totdat u geen tekens meer heeft... of middelen. 10 elementen is 3,6 miljoen permutaties, ongeveer 48 MB met het bovenstaande algoritme, en 14 (unieke) elementen zouden 87 miljard permutaties en 1.163 terabyte bereiken.

Ik weet zeker dat het uiteindelijk in een CTE kan worden geklemd, maar uiteindelijk zou het alleen maar een verheerlijkte lus zijn. De logica is op deze manier duidelijker en ik kan niet anders dan denken dat het CTE-uitvoeringsplan een nachtmerrie zou zijn.



  1. FOUT:toestemming geweigerd voor schema gebruiker1_gmail_com bij teken 46

  2. phpMyAdmin gooit een #2002 kan niet inloggen op de mysql-server phpmyadmin

  3. Hoe krijg ik het eerste record in elke groep in MySQL

  4. Hoe <,>, en &tekens te ontsnappen naar html-entiteiten in Oracle PL/SQL