Come posso convertire i primi 100 milioni di numeri interi positivi in ​​stringhe?


13

Questo è un po 'un diversivo dal vero problema. Se fornire un contesto aiuta, la generazione di questi dati potrebbe essere utile per testare le prestazioni in termini di elaborazione delle stringhe, per generare stringhe che devono essere applicate ad esse all'interno di un cursore o per generare sostituzioni di nomi univoci e anonime per dati sensibili. Sono solo interessato a modi efficienti di generare i dati all'interno dei server SQL, per favore non chiedermi perché devo generare questi dati.

Proverò a iniziare con una definizione in qualche modo formale. Una serie è inclusa nella serie se è composta solo da lettere maiuscole dalla A alla Z. Il primo termine della serie è "A". La serie è composta da tutte le stringhe valide ordinate per lunghezza prima e secondo ordine alfabetico tipico. Se le stringhe fossero in una tabella in una colonna chiamata STRING_COL, l'ordine potrebbe essere definito in T-SQL come ORDER BY LEN(STRING_COL) ASC, STRING_COL ASC.

Per dare una definizione meno formale, dai un'occhiata alle intestazioni alfabetiche delle colonne in Excel. La serie ha lo stesso modello. Considera come potresti convertire un numero intero in un numero di base 26:

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

L'analogia non è del tutto perfetta perché "A" si comporta diversamente da 0 nella base dieci. Di seguito è riportata una tabella dei valori selezionati che si spera renderà più chiaro:

╔════════════╦════════╗
 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 
╚════════════╩════════╝

L'obiettivo è scrivere una SELECTquery che restituisca le prime 100000000 stringhe nell'ordine sopra definito. Ho eseguito i test eseguendo query in SSMS con il set di risultati scartato anziché salvarlo in una tabella:

scartare il set di risultati

Idealmente la query sarà ragionevolmente efficiente. Qui sto definendo efficiente come tempo della CPU per una query seriale e tempo trascorso per una query parallela. È possibile utilizzare qualsiasi trucco non documentato che ti piace. Fare affidamento su comportamenti indefiniti o non garantiti va bene, ma sarebbe gradito se lo chiami nella tua risposta.

Quali sono alcuni metodi per generare in modo efficiente il set di dati sopra descritto? Martin Smith ha sottolineato che una procedura memorizzata CLR probabilmente non è un buon approccio a causa del sovraccarico dell'elaborazione di così tante righe.

Risposte:


7

La tua soluzione funziona per 35 secondi sul mio laptop. Il codice seguente richiede 26 secondi (inclusa la creazione e il popolamento delle tabelle temporanee):

Tabelle temporanee

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;

L'idea è quella di pre-popolare combinazioni ordinate di un massimo di quattro caratteri.

Codice principale

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

Questa è una semplice unione che preserva l'ordine * delle quattro tabelle precalcolate, con stringhe di 5 caratteri e 6 caratteri derivate secondo necessità. La separazione del prefisso dal suffisso evita l'ordinamento.

Progetto esecutivo

100 milioni di file


* Non c'è nulla nell'SQL sopra che specifichi direttamente un'unione che preserva l' ordine . L'ottimizzatore sceglie gli operatori fisici con proprietà che corrispondono alla specifica della query SQL, incluso l'ordine di livello superiore per. Qui, sceglie la concatenazione implementata dall'operatore fisico unione unione per evitare l'ordinamento.

La garanzia è che il piano di esecuzione fornisce la query semantica e l'ordine di livello superiore per specifica. Sapere che unire join concat mantiene l'ordine consente al writer di query di anticipare un piano di esecuzione, ma l'ottimizzatore fornirà solo se l'aspettativa è valida.


6

Invierò una risposta per iniziare. Il mio primo pensiero è stato che dovrebbe essere possibile sfruttare la natura che preserva l'ordine di un join ad anello nidificato insieme ad alcune tabelle helper che hanno una riga per ogni lettera. La parte difficile stava andando in loop in modo tale che i risultati fossero ordinati per lunghezza oltre a evitare duplicati. Ad esempio, quando si unisce un CTE che include tutte le 26 lettere maiuscole insieme a '', si può finire per generare 'A' + '' + 'A'e '' + 'A' + 'A'che è ovviamente la stessa stringa.

La prima decisione è stata dove archiviare i dati di supporto. Ho provato a utilizzare una tabella temporanea, ma questo ha avuto un impatto sorprendentemente negativo sulle prestazioni, anche se i dati si adattano a una singola pagina. La tabella temporanea conteneva i seguenti dati:

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

Rispetto all'utilizzo di un CTE, la query impiegava 3 volte più a lungo con una tabella cluster e 4 volte più a lungo con un heap. Non credo che il problema sia che i dati sono su disco. Dovrebbe essere letto in memoria come una singola pagina ed elaborato in memoria per l'intero piano. Forse SQL Server può funzionare con i dati di un operatore Constant Scan in modo più efficiente di quanto non possa fare con i dati archiviati nelle tipiche pagine del negozio di file.

È interessante notare che SQL Server sceglie di inserire i risultati ordinati da una tabella tempdb a pagina singola con i dati ordinati in uno spool di tabella:

cattivo spoool

SQL Server inserisce spesso i risultati per la tabella interna di un cross join in uno spool di tabella, anche se sembra assurdo farlo. Penso che l'ottimizzatore abbia bisogno di un po 'di lavoro in questo settore. Ho eseguito la query con ilNO_PERFORMANCE_SPOOL per evitare l'hit prestazioni.

Un problema con l'utilizzo di un CTE per memorizzare i dati di supporto è che non è garantito che i dati vengano ordinati. Non riesco a pensare al motivo per cui l'ottimizzatore avrebbe scelto di non ordinarlo e in tutti i miei test i dati sono stati elaborati nell'ordine in cui ho scritto il CTE:

ordine di scansione costante

Tuttavia, è meglio non correre rischi, soprattutto se c'è un modo per farlo senza un grande sovraccarico di prestazioni. È possibile ordinare i dati in una tabella derivata aggiungendo un TOPoperatore superfluo . Per esempio:

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

Tale aggiunta alla query dovrebbe garantire che i risultati vengano restituiti nell'ordine corretto. Mi aspettavo che tutti i tipi avessero un grande impatto negativo sulle prestazioni. Query Optimizer si aspettava questo anche in base ai costi stimati:

specie costose

Molto sorprendentemente, non ho potuto osservare alcuna differenza statisticamente significativa nel tempo della CPU o nel tempo di esecuzione con o senza ordinamento esplicito. Semmai, la query sembrava funzionare più velocemente con ORDER BY! Non ho spiegazioni per questo comportamento.

La parte difficile del problema era capire come inserire caratteri vuoti nei posti giusti. Come accennato in precedenza, un semplice CROSS JOINavrebbe come risultato dati duplicati. Sappiamo che la stringa 100000000th avrà una lunghezza di sei caratteri perché:

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

ma

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

Pertanto, dobbiamo solo aderire alla lettera CTE sei volte. Supponiamo di unirci al CTE sei volte, prendere una lettera da ogni CTE e concatenarli tutti insieme. Supponiamo che la lettera più a sinistra non sia vuota. Se una delle lettere successive è vuota significa che la stringa è lunga meno di sei caratteri, quindi è un duplicato. Pertanto, possiamo impedire i duplicati trovando il primo carattere non vuoto e richiedendo che tutti i caratteri dopo non siano vuoti. Ho scelto di rintracciarlo assegnando una FLAGcolonna a uno dei CTE e aggiungendo un segno di spunta alla WHEREclausola. Ciò dovrebbe essere più chiaro dopo aver esaminato la query. La query finale è la seguente:

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

I CTE sono come sopra descritti. ALL_CHARviene unito a cinque volte perché include una riga per un carattere vuoto. Il carattere finale della stringa non deve mai essere vuoto così un CTE separata è definito per esso, FIRST_CHAR. La colonna flag aggiuntiva ALL_CHARviene utilizzata per impedire i duplicati come descritto sopra. Potrebbe esserci un modo più efficiente per fare questo controllo, ma ci sono sicuramente modi più inefficienti per farlo. Un tentativo da parte mia LEN()e ha POWER()reso la query eseguita sei volte più lenta della versione corrente.

I suggerimenti MAXDOP 1e FORCE ORDERsono essenziali per assicurarsi che l'ordine venga conservato nella query. Un piano stimato con annotazioni potrebbe essere utile per capire perché i join sono nel loro ordine corrente:

stimato annotato

I piani di query vengono spesso letti da destra a sinistra, ma le richieste di riga si verificano da sinistra a destra. Idealmente, SQL Server richiederà esattamente 100 milioni di righe dall'operatore di d1scansione costante. Mentre ti sposti da sinistra a destra, mi aspetto che vengano richieste meno righe da ciascun operatore. Possiamo vederlo nel piano di esecuzione effettivo . Inoltre, di seguito è riportato uno screenshot di SQL Sentry Plan Explorer:

esploratore

Abbiamo ottenuto esattamente 100 milioni di righe da d1, il che è una buona cosa. Si noti che il rapporto tra le righe tra d2 e d3 è quasi esattamente 27: 1 (165336 * 27 = 4464072), il che ha senso se si pensa a come funzionerà il cross join. Il rapporto tra le file tra d1 e d2 è 22,4 che rappresenta un lavoro sprecato. Credo che le righe extra provengano da duplicati (a causa dei caratteri vuoti nel mezzo delle stringhe) che non superano l'operatore di loop nidificato che esegue il filtro.

Il LOOP JOINsuggerimento non è tecnicamente necessario poiché a CROSS JOINpuò essere implementato solo come loop loop in SQL Server. IlNO_PERFORMANCE_SPOOL è quello di evitare che la tabella di spooling inutili. L'omissione del suggerimento di spool ha reso la query più lunga di 3 volte sul mio computer.

La query finale ha un tempo di CPU di circa 17 secondi e un tempo totale trascorso di 18 secondi. Ciò avveniva quando si eseguiva la query tramite SSMS e si scartava il set di risultati. Sono molto interessato a vedere altri metodi per generare i dati.


2

Ho una soluzione ottimizzata per ottenere il codice stringa per qualsiasi numero specifico fino a 217.180.147.158 (8 caratteri). Ma non posso battere il tuo tempo:

Sulla mia macchina, con SQL Server 2014, la query richiede 18 secondi, mentre la mia richiede 3m 46s. Entrambe le query utilizzano flag di traccia non documentato 8690 perché 2014 non supporta il NO_PERFORMANCE_SPOOLsuggerimento.

Ecco il codice:

/* 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);

Il trucco qui è pre-calcolare dove iniziano le diverse permutazioni:

  1. Quando devi emettere un solo carattere, hai 26 ^ 1 permutazioni che iniziano a 26 ^ 0.
  2. Quando devi emettere 2 caratteri hai 26 ^ 2 permutazioni che iniziano con 26 ^ 0 + 26 ^ 1
  3. Quando devi generare 3 caratteri hai 26 ^ 3 permutazioni che iniziano con 26 ^ 0 + 26 ^ 1 + 26 ^ 2
  4. ripetere per n caratteri

L'altro trucco usato è semplicemente usare la somma per arrivare al valore giusto invece di provare a concatenare. Per raggiungere questo obiettivo, compenso semplicemente le cifre dalla base 26 alla base 256 e aggiungo il valore ASCII di 'A' per ogni cifra. Quindi otteniamo la rappresentazione binaria della stringa che stiamo cercando. Successivamente alcune manipolazioni di stringhe completano il processo.


-1

ok, ecco il mio ultimo script.

No loop, No ricorsivo.

Funziona solo per 6 caratteri

Il più grande svantaggio è che ci vogliono circa 22 minuti per 1.00,00.000

Questa volta la mia sceneggiatura è molto breve.

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

Sembra che la tabella derivata venga convertita in un singolo scalare di calcolo che contiene oltre 400000 caratteri di codice. Ho il sospetto che ci sia un sacco di spese generali per quel calcolo. Puoi provare qualcosa di simile al seguente: dbfiddle.uk/… Sentiti libero di integrare componenti di questo nella tua risposta.
Joe Obbish,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.