Wie kann ich die ersten 100 Millionen positiven Ganzzahlen in Zeichenfolgen konvertieren?


13

Dies ist eine Ablenkung vom eigentlichen Problem. Wenn Sie Kontexthilfen bereitstellen, kann das Generieren dieser Daten hilfreich sein, um die Leistung von Zeichenfolgen zu testen, Zeichenfolgen zu generieren, auf die eine Operation innerhalb eines Cursors angewendet werden muss, oder um eindeutige, anonyme Namensersetzungen für vertrauliche Daten zu generieren. Ich bin nur an effizienten Möglichkeiten zum Generieren der Daten in SQL Server interessiert. Bitte fragen Sie nicht, warum ich diese Daten generieren muss.

Ich werde versuchen, mit einer etwas formalen Definition zu beginnen. Ein String ist in der Serie enthalten, wenn er nur aus Großbuchstaben von A bis Z besteht. Der erste Term der Serie ist "A". Die Reihe besteht aus allen gültigen Zeichenfolgen, die zuerst nach Länge und dann nach typischer alphabetischer Reihenfolge sortiert sind. Wenn sich die Zeichenfolgen in einer Tabelle in einer Spalte mit dem Namen befänden STRING_COL, könnte die Reihenfolge in T-SQL wie folgt definiert werdenORDER BY LEN(STRING_COL) ASC, STRING_COL ASC .

Um eine weniger formale Definition zu geben, werfen Sie einen Blick auf die alphabetischen Spaltenüberschriften in Excel. Die Serie ist das gleiche Muster. Überlegen Sie, wie Sie eine Ganzzahl in eine Zahl zur Basis 26 umwandeln können:

1 -> A, 2 -> B, 3 -> C, ..., 25 -> Y, 26 -> Z, 27 -> AA, 28 -> AB, ...

Die Analogie ist nicht ganz perfekt, weil "A" sich anders als 0 in der Basis 10 verhält. Unten finden Sie eine Tabelle ausgewählter Werte, die es hoffentlich klarer machen:

╔════════════╦════════╗
 ROW_NUMBER  STRING 
╠════════════╬════════╣
          1  A      
          2  B      
         25  Y      
         26  Z      
         27  AA     
         28  AB     
         51  AY     
         52  AZ     
         53  BA     
         54  BB     
      18278  ZZZ    
      18279  AAAA   
     475253  ZZZY   
     475254  ZZZZ   
     475255  AAAAA  
  100000000  HJUNYV 
╚════════════╩════════╝

Ziel ist es, eine SELECTAbfrage zu schreiben , die die ersten 100000000 Zeichenfolgen in der oben definierten Reihenfolge zurückgibt. Ich habe meine Tests durchgeführt, indem ich Abfragen in SSMS mit verworfener Ergebnismenge ausgeführt habe, anstatt sie in einer Tabelle zu speichern:

Ergebnismenge verwerfen

Idealerweise ist die Abfrage einigermaßen effizient. Hier definiere ich effizient als CPU-Zeit für eine serielle Abfrage und abgelaufene Zeit für eine parallele Abfrage. Sie können alle undokumentierten Tricks verwenden, die Sie mögen. Sich auf undefiniertes oder nicht garantiertes Verhalten zu verlassen, ist ebenfalls in Ordnung, aber es wäre wünschenswert, wenn Sie dies in Ihrer Antwort angeben würden.

Mit welchen Methoden kann der oben beschriebene Datensatz effizient generiert werden? Martin Smith wies darauf hin, dass eine gespeicherte CLR-Prozedur aufgrund des Verarbeitungsaufwands für so viele Zeilen wahrscheinlich kein guter Ansatz ist.

Antworten:


7

Ihre Lösung läuft 35 Sekunden auf meinem Laptop. Der folgende Code dauert 26 Sekunden (einschließlich des Erstellens und Auffüllens der temporären Tabellen):

Temporäre Tabellen

DROP TABLE IF EXISTS #T1, #T2, #T3, #T4;

CREATE TABLE #T1 (string varchar(6) NOT NULL PRIMARY KEY);
CREATE TABLE #T2 (string varchar(6) NOT NULL PRIMARY KEY);
CREATE TABLE #T3 (string varchar(6) NOT NULL PRIMARY KEY);
CREATE TABLE #T4 (string varchar(6) NOT NULL PRIMARY KEY);

INSERT #T1 (string)
VALUES
    ('A'), ('B'), ('C'), ('D'), ('E'), ('F'), ('G'),
    ('H'), ('I'), ('J'), ('K'), ('L'), ('M'), ('N'),
    ('O'), ('P'), ('Q'), ('R'), ('S'), ('T'), ('U'),
    ('V'), ('W'), ('X'), ('Y'), ('Z');

INSERT #T2 (string)
SELECT T1a.string + T1b.string
FROM #T1 AS T1a, #T1 AS T1b;

INSERT #T3 (string)
SELECT #T2.string + #T1.string
FROM #T2, #T1;

INSERT #T4 (string)
SELECT #T3.string + #T1.string
FROM #T3, #T1;

Die Idee dabei ist, geordnete Kombinationen von bis zu vier Zeichen vorab auszufüllen.

Haupt code

SELECT TOP (100000000)
    UA.string + UA.string2
FROM
(
    SELECT U.Size, U.string, string2 = '' FROM 
    (
        SELECT Size = 1, string FROM #T1
        UNION ALL
        SELECT Size = 2, string FROM #T2
        UNION ALL
        SELECT Size = 3, string FROM #T3
        UNION ALL
        SELECT Size = 4, string FROM #T4
    ) AS U
    UNION ALL
    SELECT Size = 5, #T1.string, string2 = #T4.string
    FROM #T1, #T4
    UNION ALL
    SELECT Size = 6, #T2.string, #T4.string
    FROM #T2, #T4
) AS UA
ORDER BY 
    UA.Size, 
    UA.string, 
    UA.string2
OPTION (NO_PERFORMANCE_SPOOL, MAXDOP 1);

Dies ist eine einfache, die Reihenfolge bewahrende Vereinigung * der vier vorberechneten Tabellen, wobei je nach Bedarf Zeichenfolgen mit 5 und 6 Zeichen abgeleitet werden. Das Trennen des Präfixes vom Suffix vermeidet das Sortieren.

Ausführungsplan

100 Millionen Zeilen


* In der obigen SQL gibt es nichts, was eine auftragserhaltende Union direkt angibt . Das Optimierungsprogramm wählt physische Operatoren mit Eigenschaften aus, die der SQL-Abfragespezifikation entsprechen, einschließlich der Reihenfolge auf oberster Ebene nach. Hier wird die Verkettung ausgewählt, die vom physischen Merge-Join-Operator implementiert wird, um ein Sortieren zu vermeiden.

Die Garantie besteht darin, dass der Ausführungsplan die Abfragesemantik und die Reihenfolge der obersten Ebene nach Spezifikation liefert. Wenn Sie wissen, dass merge join concat die Reihenfolge beibehält, kann der Abfrageersteller einen Ausführungsplan vorwegnehmen, der Optimierer liefert jedoch nur, wenn die Erwartung gültig ist.


6

Ich werde eine Antwort posten, um loszulegen. Mein erster Gedanke war, dass es möglich sein sollte, die sortierungserhaltende Natur eines verschachtelten Schleifen-Joins zusammen mit einigen Hilfstabellen zu nutzen, die für jeden Buchstaben eine Zeile haben. Der knifflige Teil sollte in einer solchen Schleife ablaufen, dass die Ergebnisse nach Länge sortiert und Duplikate vermieden wurden. Wenn Sie beispielsweise einem CTE beitreten, der alle 26 Großbuchstaben zusammen mit '' enthält, können Sie am Ende generieren, 'A' + '' + 'A'und '' + 'A' + 'A'das ist natürlich dieselbe Zeichenfolge.

Die erste Entscheidung war, wo die Helferdaten gespeichert werden sollten. Ich habe versucht, eine temporäre Tabelle zu verwenden, was sich jedoch überraschend negativ auf die Leistung auswirkte, obwohl die Daten auf eine einzelne Seite passten. Die Temp-Tabelle enthielt die folgenden Daten:

SELECT 'A'
UNION ALL SELECT 'B'
...
UNION ALL SELECT 'Y'
UNION ALL SELECT 'Z'

Im Vergleich zur Verwendung eines CTE dauerte die Abfrage bei einer gruppierten Tabelle 3-mal länger und bei einem Heap 4-mal länger. Ich glaube nicht, dass das Problem darin besteht, dass sich die Daten auf der Festplatte befinden. Es sollte als einzelne Seite in den Speicher eingelesen und für den gesamten Plan im Speicher verarbeitet werden. Möglicherweise kann SQL Server mit Daten eines Constant Scan-Operators effizienter arbeiten als mit Daten, die in typischen Rowstore-Seiten gespeichert sind.

Interessanterweise wählt SQL Server, die geordneten Ergebnisse aus einer einseitigen Tempdb-Tabelle mit geordneten Daten in einen Tabellenspool zu packen:

schlechte Spoool

SQL Server legt häufig Ergebnisse für die innere Tabelle eines Cross-Joins in einem Tabellenspool ab, auch wenn dies unsinnig erscheint. Ich denke, dass der Optimierer ein wenig Arbeit in diesem Bereich benötigt. Ich habe die Abfrage mit ausgeführt NO_PERFORMANCE_SPOOL, um den Leistungseinbruch zu vermeiden.

Ein Problem bei der Verwendung eines CTE zum Speichern der Hilfsdaten besteht darin, dass die Daten nicht garantiert bestellt werden können. Ich kann mir nicht vorstellen, warum der Optimierer es nicht bestellt hat, und in all meinen Tests wurden die Daten in der Reihenfolge verarbeitet, in der ich den CTE geschrieben habe:

konstante Scanreihenfolge

Es ist jedoch am besten, kein Risiko einzugehen, insbesondere wenn es eine Möglichkeit gibt, dies ohne großen Leistungsaufwand zu tun. Es ist möglich, die Daten in einer abgeleiteten Tabelle zu ordnen, indem ein überflüssiger TOPOperator hinzugefügt wird . Beispielsweise:

(SELECT TOP (26) CHR FROM FIRST_CHAR ORDER BY CHR)

Dieser Zusatz zur Abfrage sollte gewährleisten, dass die Ergebnisse in der richtigen Reihenfolge zurückgegeben werden. Ich habe erwartet, dass alle Sorten einen großen negativen Einfluss auf die Performance haben. Das Abfrageoptimierungsprogramm hat dies auch auf der Grundlage der geschätzten Kosten erwartet:

teure Sorten

Sehr überraschend konnte ich keinen statistisch signifikanten Unterschied in der CPU-Zeit oder Laufzeit mit oder ohne explizite Bestellung feststellen. Wenn überhaupt, schien die Abfrage mit ORDER BY! Schneller zu laufen. Ich habe keine Erklärung für dieses Verhalten.

Der knifflige Teil des Problems bestand darin, herauszufinden, wie leere Zeichen an den richtigen Stellen eingefügt werden können. Wie bereits erwähnt, CROSS JOINwürde ein einfacher Vorgang zu doppelten Daten führen. Wir wissen, dass die 100000000. Zeichenfolge eine Länge von sechs Zeichen hat, weil:

26 + 26 ^ 2 + 26 ^ 3 + 26 ^ 4 + 26 ^ 5 = 914654 <100000000

aber

26 + 26 ^ 2 + 26 ^ 3 + 26 ^ 4 + 26 ^ 5 + 26 ^ 6 = 321272406> 100000000

Deshalb müssen wir uns dem Brief CTE nur sechsmal anschließen. Angenommen, wir treten sechs Mal dem CTE bei, nehmen einen Buchstaben von jedem CTE und verknüpfen sie alle miteinander. Angenommen, der Buchstabe ganz links ist nicht leer. Wenn einer der nachfolgenden Buchstaben leer ist, bedeutet dies, dass die Zeichenfolge kürzer als sechs Zeichen ist, sodass es sich um ein Duplikat handelt. Daher können wir Duplikate vermeiden, indem wir das erste nicht leere Zeichen suchen und alle Zeichen danach auch nicht leer lassen. Ich entschied mich, dies zu verfolgen, indem ich FLAGeiner der CTEs eine Spalte zuwies und der WHEREKlausel ein Häkchen hinzufügte . Dies sollte nach Betrachtung der Abfrage klarer sein. Die letzte Abfrage lautet wie folgt:

WITH FIRST_CHAR (CHR) AS
(
    SELECT 'A'
    UNION ALL SELECT 'B'
    UNION ALL SELECT 'C'
    UNION ALL SELECT 'D'
    UNION ALL SELECT 'E'
    UNION ALL SELECT 'F'
    UNION ALL SELECT 'G'
    UNION ALL SELECT 'H'
    UNION ALL SELECT 'I'
    UNION ALL SELECT 'J'
    UNION ALL SELECT 'K'
    UNION ALL SELECT 'L'
    UNION ALL SELECT 'M'
    UNION ALL SELECT 'N'
    UNION ALL SELECT 'O'
    UNION ALL SELECT 'P'
    UNION ALL SELECT 'Q'
    UNION ALL SELECT 'R'
    UNION ALL SELECT 'S'
    UNION ALL SELECT 'T'
    UNION ALL SELECT 'U'
    UNION ALL SELECT 'V'
    UNION ALL SELECT 'W'
    UNION ALL SELECT 'X'
    UNION ALL SELECT 'Y'
    UNION ALL SELECT 'Z'
)
, ALL_CHAR (CHR, FLAG) AS
(
    SELECT '', 0 CHR
    UNION ALL SELECT 'A', 1
    UNION ALL SELECT 'B', 1
    UNION ALL SELECT 'C', 1
    UNION ALL SELECT 'D', 1
    UNION ALL SELECT 'E', 1
    UNION ALL SELECT 'F', 1
    UNION ALL SELECT 'G', 1
    UNION ALL SELECT 'H', 1
    UNION ALL SELECT 'I', 1
    UNION ALL SELECT 'J', 1
    UNION ALL SELECT 'K', 1
    UNION ALL SELECT 'L', 1
    UNION ALL SELECT 'M', 1
    UNION ALL SELECT 'N', 1
    UNION ALL SELECT 'O', 1
    UNION ALL SELECT 'P', 1
    UNION ALL SELECT 'Q', 1
    UNION ALL SELECT 'R', 1
    UNION ALL SELECT 'S', 1
    UNION ALL SELECT 'T', 1
    UNION ALL SELECT 'U', 1
    UNION ALL SELECT 'V', 1
    UNION ALL SELECT 'W', 1
    UNION ALL SELECT 'X', 1
    UNION ALL SELECT 'Y', 1
    UNION ALL SELECT 'Z', 1
)
SELECT TOP (100000000)
d6.CHR + d5.CHR + d4.CHR + d3.CHR + d2.CHR + d1.CHR
FROM (SELECT TOP (27) FLAG, CHR FROM ALL_CHAR ORDER BY CHR) d6
CROSS JOIN (SELECT TOP (27) FLAG, CHR FROM ALL_CHAR ORDER BY CHR) d5
CROSS JOIN (SELECT TOP (27) FLAG, CHR FROM ALL_CHAR ORDER BY CHR) d4
CROSS JOIN (SELECT TOP (27) FLAG, CHR FROM ALL_CHAR ORDER BY CHR) d3
CROSS JOIN (SELECT TOP (27) FLAG, CHR FROM ALL_CHAR ORDER BY CHR) d2
CROSS JOIN (SELECT TOP (26) CHR FROM FIRST_CHAR ORDER BY CHR) d1
WHERE (d2.FLAG + d3.FLAG + d4.FLAG + d5.FLAG + d6.FLAG) =
    CASE 
    WHEN d6.FLAG = 1 THEN 5
    WHEN d5.FLAG = 1 THEN 4
    WHEN d4.FLAG = 1 THEN 3
    WHEN d3.FLAG = 1 THEN 2
    WHEN d2.FLAG = 1 THEN 1
    ELSE 0 END
OPTION (MAXDOP 1, FORCE ORDER, LOOP JOIN, NO_PERFORMANCE_SPOOL);

Die CTEs sind wie oben beschrieben. ALL_CHARwird fünfmal verbunden, da es eine Zeile für ein Leerzeichen enthält. Das letzte Zeichen in der Zeichenfolge sollte niemals leer sein, damit ein separater CTE dafür definiert wird FIRST_CHAR. Die zusätzliche Flag-Spalte in ALL_CHARwird verwendet, um Duplikate wie oben beschrieben zu verhindern. Möglicherweise gibt es eine effizientere Methode, um diese Prüfung durchzuführen, aber es gibt definitiv ineffizientere Methoden, um sie durchzuführen. Ein Versuch von mir mit LEN()und POWER()machte die Abfrage sechsmal langsamer als die aktuelle Version.

Die MAXDOP 1und FORCE ORDER-Hinweise sind wichtig, um sicherzustellen, dass die Reihenfolge in der Abfrage erhalten bleibt. Ein mit Anmerkungen versehener geschätzter Plan kann hilfreich sein, um festzustellen, warum die Verknüpfungen in der aktuellen Reihenfolge vorliegen:

kommentiert geschätzt

Abfragepläne werden häufig von rechts nach links gelesen, aber Zeilenanforderungen erfolgen von links nach rechts. Im Idealfall fordert SQL Server vom d1Konstantenscan-Operator genau 100 Millionen Zeilen an . Wenn Sie sich von links nach rechts bewegen, erwarte ich, dass von jedem Operator weniger Zeilen angefordert werden. Wir können dies in der sehen tatsächlichen Ausführungsplan entnehmen . Darüber hinaus ist unten ein Screenshot von SQL Sentry Plan Explorer:

Forscher

Wir haben genau 100 Millionen Zeilen von d1, was eine gute Sache ist. Beachten Sie, dass das Verhältnis der Zeilen zwischen d2 und d3 fast genau 27: 1 beträgt (165336 * 27 = 4464072). Dies ist sinnvoll, wenn Sie sich überlegen, wie die Kreuzverknüpfung funktioniert. Das Verhältnis der Zeilen zwischen d1 und d2 beträgt 22,4, was eine Verschwendung darstellt. Ich glaube, die zusätzlichen Zeilen stammen von Duplikaten (aufgrund der Leerzeichen in der Mitte der Zeichenfolgen), die nicht hinter den Join-Operator für verschachtelte Schleifen gelangen, der die Filterung ausführt.

Der LOOP JOINHinweis ist technisch unnötig, da aCROSS JOIN nur als Loop-Join in SQL Server implementiert werden kann. Das NO_PERFORMANCE_SPOOList das unnötige Tabelle Spooling zu verhindern. Wenn der Spool-Hinweis weggelassen wurde, dauerte die Abfrage auf meinem Computer 3-mal länger.

Die endgültige Abfrage hat eine CPU-Zeit von ca. 17 Sekunden und eine Gesamtzeit von 18 Sekunden. Das war, als die Abfrage über SSMS ausgeführt und die Ergebnismenge verworfen wurde. Ich bin sehr daran interessiert, andere Methoden zur Erzeugung der Daten zu sehen.


2

Ich habe eine optimierte Lösung, um den Zeichenfolgencode für eine bestimmte Zahl bis zu 217.180.147.158 (8 Zeichen) zu erhalten. Aber ich kann deine Zeit nicht schlagen:

Auf meinem Computer mit SQL Server 2014 dauert Ihre Abfrage 18 Sekunden, während meine 3 bis 46 Minuten dauert. Beide Abfragen verwenden das undokumentierte Ablaufverfolgungsflag 8690, da 2014 das nicht unterstütztNO_PERFORMANCE_SPOOL Hinweis .

Hier ist der Code:

/* precompute offsets and powers to simplify final query */
CREATE TABLE #ExponentsLookup (
    offset          BIGINT NOT NULL,
    offset_end      BIGINT NOT NULL,
    position        INTEGER NOT NULL,
    divisor         BIGINT NOT NULL,
    shifts          BIGINT NOT NULL,
    chars           INTEGER NOT NULL,
    PRIMARY KEY(offset, offset_end, position)
);

WITH base_26_multiples AS ( 
    SELECT  number  AS exponent,
            CAST(POWER(26.0, number) AS BIGINT) AS multiple
    FROM    master.dbo.spt_values
    WHERE   [type] = 'P'
            AND number < 8
),
num_offsets AS (
    SELECT  *,
            -- The maximum posible value is 217180147159 - 1
            LEAD(offset, 1, 217180147159) OVER(
                ORDER BY exponent
            ) AS offset_end
    FROM    (
                SELECT  exponent,
                        SUM(multiple) OVER(
                            ORDER BY exponent
                        ) AS offset
                FROM    base_26_multiples
            ) x
)
INSERT INTO #ExponentsLookup(offset, offset_end, position, divisor, shifts, chars)
SELECT  ofst.offset, ofst.offset_end,
        dgt.number AS position,
        CAST(POWER(26.0, dgt.number) AS BIGINT)     AS divisor,
        CAST(POWER(256.0, dgt.number) AS BIGINT)    AS shifts,
        ofst.exponent + 1                           AS chars
FROM    num_offsets ofst
        LEFT JOIN master.dbo.spt_values dgt --> as many rows as resulting chars in string
            ON [type] = 'P'
            AND dgt.number <= ofst.exponent;

/*  Test the cases in table example */
SELECT  /*  1.- Get the base 26 digit and then shift it to align it to 8 bit boundaries
            2.- Sum the resulting values
            3.- Bias the value with a reference that represent the string 'AAAAAAAA'
            4.- Take the required chars */
        ref.[row_number],
        REVERSE(SUBSTRING(REVERSE(CAST(SUM((((ref.[row_number] - ofst.offset) / ofst.divisor) % 26) * ofst.shifts) +
            CAST(CAST('AAAAAAAA' AS BINARY(8)) AS BIGINT) AS BINARY(8))),
            1, MAX(ofst.chars))) AS string
FROM    (
            VALUES(1),(2),(25),(26),(27),(28),(51),(52),(53),(54),
            (18278),(18279),(475253),(475254),(475255),
            (100000000), (CAST(217180147158 AS BIGINT))
        ) ref([row_number])
        LEFT JOIN #ExponentsLookup ofst
            ON ofst.offset <= ref.[row_number]
            AND ofst.offset_end > ref.[row_number]
GROUP BY
        ref.[row_number]
ORDER BY
        ref.[row_number];

/*  Test with huge set  */
WITH numbers AS (
    SELECT  TOP(100000000)
            ROW_NUMBER() OVER(
                ORDER BY x1.number
            ) AS [row_number]
    FROM    master.dbo.spt_values x1
            CROSS JOIN (SELECT number FROM master.dbo.spt_values WHERE [type] = 'P' AND number < 676) x2
            CROSS JOIN (SELECT number FROM master.dbo.spt_values WHERE [type] = 'P' AND number < 676) x3
    WHERE   x1.number < 219
)
SELECT  /*  1.- Get the base 26 digit and then shift it to align it to 8 bit boundaries
            2.- Sum the resulting values
            3.- Bias the value with a reference that represent the string 'AAAAAAAA'
            4.- Take the required chars */
        ref.[row_number],
        REVERSE(SUBSTRING(REVERSE(CAST(SUM((((ref.[row_number] - ofst.offset) / ofst.divisor) % 26) * ofst.shifts) +
            CAST(CAST('AAAAAAAA' AS BINARY(8)) AS BIGINT) AS BINARY(8))),
            1, MAX(ofst.chars))) AS string
FROM    numbers ref
        LEFT JOIN #ExponentsLookup ofst
            ON ofst.offset <= ref.[row_number]
            AND ofst.offset_end > ref.[row_number]
GROUP BY
        ref.[row_number]
ORDER BY
        ref.[row_number]
OPTION (QUERYTRACEON 8690);

Der Trick dabei ist, vorab zu berechnen, wo die verschiedenen Permutationen beginnen:

  1. Wenn Sie ein einzelnes Zeichen ausgeben müssen, haben Sie 26 ^ 1-Permutationen, die bei 26 ^ 0 beginnen.
  2. Wenn Sie 2 Zeichen ausgeben müssen, haben Sie 26 ^ 2 Permutationen, die bei 26 ^ 0 + 26 ^ 1 beginnen
  3. Wenn Sie 3 Zeichen ausgeben müssen, haben Sie 26 ^ 3 Permutationen, die bei 26 ^ 0 + 26 ^ 1 + 26 ^ 2 beginnen
  4. Wiederholen Sie dies für n Zeichen

Der andere Trick besteht darin, einfach die Summe zu verwenden, um zum richtigen Wert zu gelangen, anstatt zu versuchen, sich zu unterhalten. Um dies zu erreichen, versetze ich einfach die Ziffern von Basis 26 zu Basis 256 und addiere den ASCII-Wert von 'A' für jede Ziffer. So erhalten wir die Binärdarstellung des gesuchten Strings. Danach schließen einige String-Manipulationen den Prozess ab.


-1

ok, hier geht mein neuestes script.

Keine Schleife, keine rekursive.

Es funktioniert nur für 6 Zeichen

Der größte Nachteil ist, dass es für 1,00,00,000 ungefähr 22 Minuten dauert

Dieses Mal ist mein Drehbuch sehr kurz.

SET NoCount on

declare @z int=26
declare @start int=@z+1 
declare @MaxLimit int=10000000

SELECT TOP (@MaxLimit) IDENTITY(int,1,1) AS N
    INTO NumbersTest1
    FROM     master.dbo.spt_values x1   
   CROSS JOIN (SELECT number FROM master.dbo.spt_values WHERE [type] = 'P' AND number < 500) x2
            CROSS JOIN (SELECT number FROM master.dbo.spt_values WHERE [type] = 'P' AND number < 500) x3
    WHERE   x1.number < 219
ALTER TABLE NumbersTest1 ADD CONSTRAINT PK_NumbersTest1 PRIMARY KEY CLUSTERED (N)


select N, strCol from NumbersTest1
cross apply
(
select 
case when IntCol6>0 then  char((IntCol6%@z)+64) else '' end 
+case when IntCol5=0 then 'Z' else isnull(char(IntCol5+64),'') end 
+case when IntCol4=0 then 'Z' else isnull(char(IntCol4+64),'') end 
+case when IntCol3=0 then 'Z' else isnull(char(IntCol3+64),'') end 
+case when IntCol2=0 then 'Z' else isnull(char(IntCol2+64),'') end 
+case when IntCol1=0 then 'Z' else isnull(char(IntCol1+64),'') end strCol
from
(
select  IntCol1,IntCol2,IntCol3,IntCol4
,case when IntCol5>0 then  IntCol5%@z else null end IntCol5

,case when IntCol5/@z>0 and  IntCol5%@z=0 then  IntCol5/@z-1 
when IntCol5/@z>0 then IntCol5/@z
else null end IntCol6
from
(
select IntCol1,IntCol2,IntCol3
,case when IntCol4>0 then  IntCol4%@z else null end IntCol4

,case when IntCol4/@z>0 and  IntCol4%@z=0 then  IntCol4/@z-1 
when IntCol4/@z>0 then IntCol4/@z
else null end IntCol5
from
(
select IntCol1,IntCol2
,case when IntCol3>0 then  IntCol3%@z else null end IntCol3
,case when IntCol3/@z>0 and  IntCol3%@z=0 then  IntCol3/@z-1 
when IntCol3/@z>0 then IntCol3/@z
else null end IntCol4

from
(
select IntCol1
,case when IntCol2>0 then  IntCol2%@z else null end IntCol2
,case when IntCol2/@z>0 and  IntCol2%@z=0 then  IntCol2/@z-1 
when IntCol2/@z>0 then IntCol2/@z
else null end IntCol3

from
(
select case when N>0 then N%@z else null end IntCol1
,case when N%@z=0 and  (N/@z)>1 then (N/@z)-1 else  (N/@z) end IntCol2 

)Lv2
)Lv3
)Lv4
)Lv5
)LV6

)ca

DROP TABLE NumbersTest1

Es sieht so aus, als würde die abgeleitete Tabelle in einen einzelnen Berechnungsskalar konvertiert, der mehr als 400.000 Zeichen Code enthält. Ich vermute, dass diese Berechnung viel Aufwand mit sich bringt. Möglicherweise möchten Sie Folgendes ausprobieren : dbfiddle.uk/… Sie können auch Komponenten davon in Ihre Antwort integrieren.
Joe Obbish
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.