Siamo a metà ciclo tra le versioni, e non abbiamo ancora sentito parlare di nessuna delle funzionalità pianificate per SQL Server vNext. Questo è probabilmente il momento migliore per fare pressione su Microsoft per miglioramenti, a condizione che possiamo sostenere le nostre richieste con casi aziendali legittimi. In SQL Server 2016, STRING_SPLIT
ha risolto una lacuna mancante da tempo in un linguaggio che, certamente, non era destinato a complicate elaborazioni di stringhe. Ed è quello che voglio tirare fuori oggi.
Per anni prima di SQL Server 2016 (e per anni da allora), abbiamo scritto le nostre versioni, le abbiamo migliorate nel tempo e abbiamo persino discusso su chi fosse il più veloce. Abbiamo scritto sul blog ogni microsecondo che potevamo guadagnare e io, per esempio, ho affermato più volte "questo è il mio ultimo post sulla divisione delle stringhe!" Eppure eccoci qui.
Sosterrò sempre che i parametri con valori di tabella sono il modo giusto per dividere le stringhe. Ma mentre io credo che questi blocchi di testo separati da virgole non dovrebbero mai essere esposti al database in quella forma, la divisione delle stringhe continua a essere un caso d'uso prevalente:un paio dei miei post sul blog qui sono tra i primi 5 in visualizzazioni ogni singolo giorno .
Quindi, perché le persone stanno ancora cercando di dividere le stringhe con funzioni con valori di tabella quando esiste una sostituzione superiore? Alcuni, ne sono sicuro, perché sono ancora su versioni precedenti, bloccati in un livello di compatibilità precedente o non riescono a evitare di dividere le stringhe perché i TVP non sono supportati dalla loro lingua o ORM. Per il resto, mentre STRING_SPLIT
è allo stesso tempo comodo ed efficiente, non è perfetto. Ha restrizioni che comportano un certo attrito e che rendono la sostituzione delle chiamate di funzione esistenti con una chiamata nativa ingombrante o impossibile.
Ecco la mia lista.
Queste limitazioni non sono esaustive, ma ho elencato quelle importanti nel mio ordine di priorità (e anche Andy Mallon ne ha parlato oggi nel blog):
- Delimitatore di un carattere
Sembra che la funzione sia stata creata pensando solo al semplicissimo caso d'uso:CSV. Le persone hanno stringhe più complesse di1,2,3
oA|B|C
, e spesso vengono inseriti nei loro database da sistemi al di fuori del loro controllo. Come descrivo in questa risposta e in questo suggerimento, ci sono modi per aggirare questo problema (operazioni di sostituzione davvero inefficienti), ma sono davvero brutte e, francamente, annullano tutti i vantaggi in termini di prestazioni offerti dall'implementazione nativa. Inoltre, parte dell'attrito con questo si riduce specificamente a:"Bene,string_to_array
di PostgreSQL gestisce più delimitatori di caratteri, quindi perché SQL Server non può?"Implementazione:aumenta la dimensione massima delseparator
. - Nessuna indicazione dell'ordine di input
L'output della funzione è un insieme e, intrinsecamente, gli insiemi non hanno ordine. E mentre nella maggior parte dei casi vedrai una stringa di input comebob,ted,frank
escono in quest'ordine (bob
ted
frank
), non vi è alcuna garanzia (con o senza un(ORDER BY (SELECT NULL))
hackerare). Molte funzioni create in casa includono una colonna di output per indicare la posizione ordinale nella stringa, che può essere importante se l'elenco è disposto in un ordine definito o se la posizione ordinale esatta ha qualche significato.Implementazione:aggiungi un'opzione per includere la colonna della posizione ordinale in L'output. - Il tipo di output si basa solo sull'input
La colonna di output della funzione è fissata suvarchar
onvarchar
, ed è determinato precisamente dalla lunghezza dell'intera stringa di input, non dalla lunghezza dell'elemento più lungo. Quindi, hai un elenco di 25 lettere, il tipo di output è almenovarchar(51)
. Per stringhe più lunghe, questo può sfociare in problemi con le concessioni di memoria, a seconda dell'utilizzo, e può introdurre problemi se il consumatore fa affidamento sull'output di un altro tipo di dati (ad esempio,int
, quali funzioni a volte specificano per evitare conversioni implicite in un secondo momento). Come soluzione alternativa, gli utenti a volte creano le proprie tabelle temporanee o variabili di tabella e scaricano lì l'output della funzione prima di interagire con essa, il che può causare problemi di prestazioni. Implementazione:aggiungi un'opzione per specificare il tipo di output del valorevalue
. - Impossibile ignorare elementi vuoti o delimitatori finali
Quando hai una stringa comea,,,b,
, potresti aspettarti che vengano emessi solo due elementi, poiché gli altri tre sono vuoti. La maggior parte dei TVF personalizzati che ho visto tagliare i delimitatori finali e/o filtrare le stringhe di lunghezza zero, maSTRING_SPLIT
restituisce tutte e 5 le righe. Ciò rende difficile lo scambio nella funzione nativa perché devi anche aggiungere la logica di wrapping per eliminare queste entità. Implementazione:aggiungi un'opzione per ignorare gli elementi vuoti. - Impossibile filtrare i duplicati
Questa è probabilmente una richiesta meno comune e facile da risolvere utilizzandoDISTINCT
oGROUP BY
, ma molte funzioni lo fanno automaticamente per te. Nessuna reale differenza di prestazioni in questi casi, ma c'è se è qualcosa che dimentichi di aggiungere te stesso (pensa a un elenco grande, con molti duplicati, unendo a un grande tavolo). Implementazione:aggiungi un'opzione per filtrare i duplicati.
Ecco il business case.
Sembrano tutti teorici, ma ecco il business case, che posso assicurarvi che è molto reale. In Wayfair, disponiamo di una notevole proprietà di SQL Server e abbiamo letteralmente dozzine di team diversi che hanno creato le proprie funzioni con valori di tabella nel corso degli anni. Alcuni sono migliori di altri, ma sono tutti chiamati da migliaia e migliaia di righe di codice. Di recente abbiamo avviato un progetto in cui stiamo cercando di sostituirli con chiamate a STRING_SPLIT
, ma ci siamo imbattuti in casi di blocco implicando molte delle limitazioni di cui sopra.
Alcuni sono facili da aggirare, utilizzando una funzione wrapper. Ma il delimitatore di carattere singolo limitazione ci ha costretto a valutare la terribile soluzione alternativa utilizzando REPLACE
, e questo ha dimostrato di eliminare il vantaggio in termini di prestazioni che ci aspettavamo, facendoci pompare i freni. E in quei casi, abbiamo perso una merce di scambio chiave nel spingere per gli aggiornamenti al livello di compatibilità (non tutti i database sono su 130, non importa 140). In questi casi, stiamo perdendo non solo STRING_SPLIT
miglioramenti, ma anche su altri oltre 130 miglioramenti delle prestazioni di cui ci godremmo se STRING_SPLIT
era stato abbastanza convincente da solo per spingere per l'aggiornamento del livello di compatibilità.
Quindi, chiedo il tuo aiuto.
Visita questo elemento di feedback:
- STRING_SPLIT non è completo di funzionalità
Votalo! Ancora più importante, lascia un commento descrivendo i casi d'uso reali che hai che creano STRING_SPLIT
un dolore o un non-starter per te. I voti da soli non sono sufficienti ma, con un feedback tangibile e qualitativo sufficiente, c'è la possibilità che inizino a prendere sul serio queste lacune.
Mi sento di supportare i delimitatori multi-carattere (anche, diciamo, espandendo da [n]varchar(1)
a [n]varchar(5)
) è un miglioramento non invadente che sbloccherà molte persone che condividono il mio scenario. Altri miglioramenti potrebbero essere più difficili da implementare, alcuni richiedono sovraccarichi e/o miglioramenti del linguaggio, quindi non mi aspetto tutte queste correzioni in vNext. Ma anche un piccolo miglioramento lo ribadirebbe STRING_SPLIT
è stato un investimento utile e che non verrà abbandonato (come, ad esempio, database contenuti, una delle funzionalità drive-by più famose).
Grazie per l'ascolto!