Domanda

Sto lavorando a un database per una piccola app Web nella mia scuola utilizzando SQL Server 2005.
Vedo un paio di scuole di pensiero sulla questione varchar contro nvarchar:

  1. Utilizzo varchar a meno che non si tratti di molti dati internazionalizzati, utilizzare nvarchar.
  2. Basta usare nvarchar per tutto.

Sto cominciando a vedere i meriti della vista 2.So che nvarchar occupa il doppio dello spazio, ma non è necessariamente un grosso problema poiché memorizzerà i dati solo per poche centinaia di studenti.A me sembra che sarebbe più semplice non preoccuparsene e consentire a tutto di utilizzare nvarchar.Oppure c'è qualcosa che mi sfugge?

È stato utile?

Soluzione

Utilizzare sempre nvarchar.

Potrebbe non essere mai necessario utilizzare i caratteri a doppio byte per la maggior parte delle applicazioni.Tuttavia, se è necessario supportare lingue a doppio byte e nello schema del database è disponibile solo il supporto a byte singolo, è davvero costoso tornare indietro e modificare l'intera applicazione.

Il costo della migrazione di un'applicazione da varchar a nvarchar sarà molto superiore allo spazio su disco aggiuntivo che utilizzerai nella maggior parte delle applicazioni.

Altri suggerimenti

Lo spazio su disco non è il problema...ma la memoria e le prestazioni lo saranno.Raddoppia la lettura della pagina, doppia dimensione dell'indice, MI PIACE strano e = comportamento costante ecc

Hai bisogno di memorizzare script cinesi, ecc.?Sì o no...

E da MS BOL "Effetti sulla memorizzazione e sulle prestazioni di Unicode"

Modificare:

Domanda SO recente che evidenzia quanto possano essere pessime le prestazioni di nvarchar...

SQL Server utilizza una CPU elevata durante la ricerca all'interno di stringhe nvarchar

Sii coerente!UNIRSI a un VARCHAR a NVARCHAR ha un grande successo in termini di prestazioni.

nvarchar avrà un sovraccarico significativo in memoria, archiviazione, set di lavoro e indicizzazione, quindi se le specifiche lo impongono davvero Mai essere necessario, non preoccuparti.

Non avrei una regola rigida e veloce "sempre nvarchar" perché può essere uno spreco completo in molte situazioni, in particolare ETL da ASCII/EBCDIC o identificatori e colonne di codice che sono spesso chiavi e chiavi esterne.

D'altra parte, ci sono molti casi di colonne in cui sarei sicuro di porre questa domanda in anticipo e se non ottenessi immediatamente una risposta dura e veloce, creerei la colonna nvarchar.

Per la tua applicazione, nvarchar va bene perché la dimensione del database è piccola.Dire "usa sempre nvarchar" è una semplificazione eccessiva.Se non ti viene richiesto di archiviare cose come Kanji o altri caratteri pazzi, usa VARCHAR, utilizzerà molto meno spazio.Il mio predecessore nel mio attuale lavoro ha progettato qualcosa utilizzando NVARCHAR quando non era necessario.Recentemente l'abbiamo passato a VARCHAR e abbiamo risparmiato 15 GB solo su quella tabella (era altamente scritta).Inoltre, se hai un indice su quella tabella e desideri includere quella colonna o creare un indice composito, hai semplicemente aumentato le dimensioni del file dell'indice.

Sii solo attento nella tua decisione;nello sviluppo SQL e nelle definizioni dei dati sembra esserci raramente una "risposta predefinita" (a parte evitare i cursori a tutti i costi, ovviamente).

Esito ad aggiungere ancora un'altra risposta perché ce ne sono già parecchie, ma è necessario sottolineare alcuni punti che non sono stati formulati o non sono stati chiariti.

Primo: Fare non utilizzare sempre NVARCHAR.Questo è un atteggiamento/approccio molto pericoloso e spesso costoso.E non è meglio dire "Mai utilizzare i cursori" poiché a volte sono il mezzo più efficiente per risolvere un particolare problema e la soluzione comune di eseguire un WHILE il ciclo sarà quasi sempre più lento di a correttamente fatto Cursore.

L'unico momento in cui dovresti usare il termine "sempre" è quando consigli di "fare sempre ciò che è meglio per la situazione".Certo, questo è spesso difficile da determinare, soprattutto quando si cerca di bilanciare i guadagni a breve termine in termini di tempo di sviluppo (manager:"abbiamo bisogno di questa funzionalità, di cui non sapevi fino a poco fa, una settimana fa!") con costi di manutenzione a lungo termine (manager che inizialmente ha fatto pressioni sul team per completare un progetto di 3 mesi in uno sprint di 3 settimane :"perché abbiamo questi problemi di prestazioni?Come avremmo potuto fare X che non ha flessibilità?Non possiamo permetterci uno o due sprint per risolvere questo problema.Cosa possiamo fare in una settimana per poter tornare ai nostri obiettivi prioritari?E abbiamo sicuramente bisogno di dedicare più tempo al design affinché ciò non continui ad accadere!").

Secondo: La risposta di @gbn tocca alcuni punti molto importanti da considerare quando si prendono determinate decisioni sulla modellazione dei dati quando il percorso non è chiaro al 100%.Ma c’è ancora altro da considerare:

  • dimensione dei file di registro delle transazioni
  • tempo necessario per replicarsi (se si utilizza la replica)
  • tempo necessario per ETL (se ETLing)
  • tempo necessario per inviare i registri a un sistema remoto e ripristinarli (se si utilizza il Log Shipping)
  • dimensione dei backup
  • il tempo necessario per completare il backup
  • tempo necessario per eseguire un ripristino (questo potrebbe essere importante un giorno ;-)
  • dimensione necessaria per tempdb
  • prestazioni dei trigger (per le tabelle inserite ed eliminate archiviate in tempdb)
  • prestazioni del controllo delle versioni delle righe (se si utilizza SNAPSHOT ISOLATION, poiché l'archivio delle versioni è in tempdb)
  • capacità di ottenere nuovo spazio su disco quando il CFO afferma di aver speso appena 1 milione di dollari per una SAN l'anno scorso e quindi non autorizzerà altri 250.000 dollari per spazio di archiviazione aggiuntivo
  • il tempo necessario per eseguire le operazioni INSERT e UPDATE
  • periodo di tempo necessario per eseguire la manutenzione dell'indice
  • ecc, ecc, ecc.

Sprecare spazio ha a Enorme effetto a cascata su tutto il sistema.Ho scritto un articolo che entra nei dettagli espliciti su questo argomento: Il disco è economico!ORLY? (è richiesta la registrazione gratuita;mi dispiace, non controllo quella politica).

Terzo: Sebbene alcune risposte si concentrino erroneamente sull'aspetto "questa è una piccola app" e alcune suggeriscano correttamente di "utilizzare ciò che è appropriato", nessuna delle risposte ha fornito una guida reale all'O.P.Un dettaglio importante menzionato nella domanda è che questa è una pagina web per la loro scuola.Grande!Quindi possiamo suggerire che:

  • I campi per i nomi degli studenti e/o delle facoltà dovrebbero essere probabilmente Essere NVARCHAR poiché, col passare del tempo, è sempre più probabile che nomi di altre culture compaiano in quei luoghi.
  • Ma per l'indirizzo stradale e i nomi delle città?Lo scopo dell'app non è stato dichiarato (sarebbe stato utile), ma presupponendo che i record di indirizzi, se presenti, si riferiscano solo a una particolare regione geografica (ad es.una singola lingua/cultura), quindi utilizzare VARCHAR con la Code Page appropriata (che è determinata dalla Collazione del campo).
  • Se si memorizzano codici ISO di stato e/o paese (non è necessario memorizzare INT / TINYINT poiché i codici ISO sono di lunghezza fissa, leggibili dall'uomo e, beh, standard :) CHAR(2) per codici a due lettere e CHAR(3) se si utilizzano codici a 3 lettere.E considera l'utilizzo di una raccolta binaria come Latin1_General_100_BIN2.
  • Se si memorizzano codici postali (ad es.codici postali), utilizzare VARCHAR poiché è uno standard internazionale non utilizzare mai lettere al di fuori della A-Z.E sì, lo uso ancora VARCHAR anche se si memorizzano solo codici postali statunitensi e non INT poiché i codici postali non sono numeri, sono stringhe e alcuni di essi hanno uno "0" iniziale.E considera l'utilizzo di una raccolta binaria come Latin1_General_100_BIN2.
  • Se si memorizzano indirizzi e-mail e/o URL, utilizzare NVARCHAR poiché entrambi ora possono contenere caratteri Unicode.
  • e così via....

Il quarto: Ora che hai NVARCHAR i dati occupano il doppio dello spazio necessario per i dati che si adattano perfettamente VARCHAR ("si adatta bene" = non diventa "?") e in qualche modo, come per magia, l'applicazione è cresciuta e ora ci sono milioni di record in almeno uno di questi campi in cui maggior parte le righe sono ASCII standard ma alcune contengono caratteri Unicode quindi devi mantenerle NVARCHAR, considerare quanto segue:

  1. Se utilizzi SQL Server 2008-2016 RTM E sono in Enterprise Edition, OPPURE se utilizzi SQL Server 2016 SP1 (che ha reso disponibile la compressione dei dati in tutte le edizioni) o versioni successive, puoi abilitare Compressione dati.La compressione dei dati può (ma non "sempre") comprimere i dati Unicode NCHAR E NVARCHAR campi.I fattori determinanti sono:

    1. NCHAR(1 - 4000) E NVARCHAR(1 - 4000) Usa il Schema di compressione standard per Unicode, ma solo a partire da SQL Server 2008 R2 E solo per i dati IN ROW, non OVERFLOW!Questo sembra essere migliore del normale algoritmo di compressione RIGA/PAGINA.
    2. NVARCHAR(MAX) E XML (e immagino anche VARBINARY(MAX), TEXT, E NTEXT) i dati IN ROW (non fuori riga nelle pagine LOB o OVERFLOW) possono essere almeno compressi in PAGE, ma non RIGA compressa.Naturalmente, la compressione della PAGINA dipende dalla dimensione del valore nella riga:Ho provato con VARCHAR(MAX) e ho visto che 6000 righe di caratteri/byte non si comprimevano, ma 4000 righe di caratteri/byte lo facevano.
    3. Qualsiasi dato OFF ROW, LOB o OVERLOW = nessuna compressione per te!
  2. Se si utilizza SQL Server 2005 o 2008-2016 RTM e non sull'edizione Enterprise, puoi avere due campi:uno VARCHAR e uno NVARCHAR.Ad esempio, supponiamo che tu stia memorizzando URL che sono per lo più tutti caratteri ASCII di base (valori 0 - 127) e quindi si adattano a VARCHAR, ma a volte contengono caratteri Unicode.Il tuo schema può includere i seguenti 3 campi:

      ...
      URLa VARCHAR(2048) NULL,
      URLu NVARCHAR(2048) NULL,
      URL AS (ISNULL(CONVERT(NVARCHAR([URLa])), [URLu])),
      CONSTRAINT [CK_TableName_OneUrlMax] CHECK (
                        ([URLa] IS NOT NULL OR [URLu] IS NOT NULL)
                    AND ([URLa] IS NULL OR [URLu] IS NULL))
    );
    

    In questo modello tu soltanto SELEZIONA da [URL] colonna calcolata.Per l'inserimento e l'aggiornamento si determina quale campo utilizzare vedendo se la conversione altera il valore in ingresso, che deve essere di NVARCHAR tipo:

    INSERT INTO TableName (..., URLa, URLu)
    VALUES (...,
            IIF (CONVERT(VARCHAR(2048), @URL) = @URL, @URL, NULL),
            IIF (CONVERT(VARCHAR(2048), @URL) <> @URL, NULL, @URL)
           );
    
  3. Puoi GZIP i valori in entrata VARBINARY(MAX) e poi decomprimere all'uscita:

    • Per SQL Server 2005-2014:puoi usare SQLCLR. SQL# (una libreria SQLCLR che ho scritto) viene fornita con Utili_GZip E Util_GUnzip nella versione gratuita
    • Per SQL Server 2016 e versioni successive:puoi usare il built-in COMPRESS E DECOMPRESS funzioni, che sono anche GZip.
  4. Se si utilizza SQL Server 2017 o versione successiva, è possibile considerare la possibilità di rendere la tabella un indice columnstore cluster.

  5. Sebbene questa non sia ancora un'opzione praticabile, SQL Server 2019 introduce il supporto nativo per UTF-8 in VARCHAR / CHAR tipi di dati.Attualmente ci sono troppi bug per poterlo utilizzare, ma se vengono risolti, questa è un'opzione Alcuni scenari.Si prega di consultare il mio post, "Supporto UTF-8 nativo in SQL Server 2019:Salvatore o falso profeta?", per un'analisi dettagliata di questa nuova funzionalità.

Poiché l'applicazione è piccola, non vi è essenzialmente alcun aumento apprezzabile dei costi nell'utilizzo di nvarchar su varchar e si risparmiano potenziali grattacapi in futuro se è necessario archiviare dati Unicode.

Parlando in generale;Inizia con il tipo di dati più costoso che presenta i minori vincoli. Mettilo in produzione.Se le prestazioni iniziano a essere un problema, scopri cosa viene effettivamente memorizzato in essi nvarchar colonne.C'è qualche personaggio lì dentro che non si adatterebbe? varchar?In caso contrario, passa a varchar.Non provare a pre-ottimizzare prima di sapere dov'è il problema.La mia ipotesi è questa la scelta tra nvarchar/varchar non è ciò che rallenterà la tua applicazione nel prossimo futuro.Ci saranno altre parti dell'applicazione in cui l'ottimizzazione delle prestazioni ti darà molto di più un vero affare.

Negli ultimi anni tutti i nostri progetti hanno utilizzato NVARCHAR per tutto, poiché tutti questi progetti sono multilingue.Dati importati da fonti esterne (es.un file ASCII, ecc.) viene convertito in Unicode prima di essere inserito nel database.

Devo ancora riscontrare problemi relativi alle prestazioni dagli indici più grandi, ecc.Gli indici utilizzano più memoria, ma la memoria è economica.

Sia che utilizzi procedure memorizzate o costruisca SQL al volo, assicurati che tutte le costanti stringa abbiano il prefisso N (ad es.SET @foo = N'Hello world.';) quindi anche la costante è Unicode.Ciò evita qualsiasi conversione del tipo di stringa in fase di esecuzione.

YMMV.

Posso parlare per esperienza, attenzione nvarchar.A meno che tu non lo richieda assolutamente, questo tipo di campo dati distrugge le prestazioni su database più grandi.Ho ereditato un database che faceva male in termini di prestazioni e spazio.Siamo riusciti a ridurre le dimensioni di un database da 30 GB del 70%!Sono state apportate alcune altre modifiche per migliorare le prestazioni, ma sono sicuro che varcharha aiutato in modo significativo anche in questo.Se il tuo database ha il potenziale per far crescere le tabelle fino a un milione di record, stai lontano nvarchar a tutti i costi.

Mi occupo spesso di questa domanda al lavoro:

  • Feed FTP di inventario e prezzi: le descrizioni degli articoli e altro testo erano in nvarchar quando varchar funzionava correttamente.La conversione di questi in varchar ha ridotto le dimensioni del file quasi della metà e ha davvero aiutato con i caricamenti.

  • Lo scenario sopra ha funzionato bene finché qualcuno non ha inserito un carattere speciale nella descrizione dell'oggetto (forse un marchio, non ricordo)

Continuo a non utilizzare nvarchar ogni volta su varchar.Se c'è qualche dubbio o potenziale per caratteri speciali, utilizzo nvarchar.Trovo di utilizzare varchar principalmente quando ho il controllo al 100% di ciò che sta popolando il campo.

Perché in tutta questa discussione non è stato menzionato UTF-8?Essere in grado di memorizzare l'intero intervallo di caratteri Unicode non significa che sia necessario allocare sempre due byte per carattere (o "punto di codice" per usare il termine UNICODE).Tutto ASCII è UTF-8.SQL Server verifica per i campi VARCHAR() che il testo sia ASCII rigoroso (ovverobyte superiore bit zero)?Spero di no.

Se quindi desideri archiviare unicode E voglio la compatibilità con le vecchie applicazioni solo ASCII, penso che usare VARCHAR() e UTF-8 sarebbe la bacchetta magica:Utilizza più spazio solo quando necessario.

Per quelli di voi che non hanno familiarità con UTF-8, potrei consigliarlo un primer.

Ci saranno casi eccezionali in cui vorrai limitare deliberatamente il tipo di dati per garantirlo no contenere caratteri di un determinato set.Ad esempio, avevo uno scenario in cui dovevo archiviare il nome di dominio in un database.All'epoca l'internazionalizzazione dei nomi di dominio non era affidabile, quindi era meglio limitare l'immissione a livello di base e contribuire ad evitare potenziali problemi.

Se stai usando NVARCHAR solo perché una procedura memorizzata di sistema lo richiede, l'evento più frequente è inspiegabilmente sp_executesql, e il tuo SQL dinamico è molto lungo, dal punto di vista delle prestazioni sarebbe meglio eseguire tutte le manipolazioni delle stringhe (concatenazione, sostituzione, ecc.) in VARCHAR quindi convertendo il risultato finale in NVARCHAR e inserendolo nel parametro proc.Quindi no, non usarlo sempre NVARCHAR!

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top