2009-02-17 4 views
5

Ho discusso di questo problema per un po 'ma continua a venire. Abbiamo un sistema e il nostro valore delle nostre tabelle inizia con una descrizione che è stata memorizzata originariamente come NVARCHAR(150) e poi otteniamo un ticket che chiede di espandere le dimensioni del campo a 250, quindi 1000 ecc. Ecc.Che cos'è un modo gestibile per archiviare campi di testo di grandi dimensioni senza sacrificare le prestazioni?

il ciclo si ripete sempre sul campo "nota" e/o sul campo "descrizione" che aggiungiamo alla maggior parte delle tabelle. Naturalmente la preoccupazione per me è la performance e rompendo il limite di 8k della pagina. Tuttavia, la mia altra preoccupazione è rendere il sistema meno gestibile interrompendo questi campi da OGNI tabella nel sistema in un riferimento caricato pigro.

Quindi qui mi trovo di fronte a queste stesse a 2 opzioni che mi hanno guardato in faccia. (altri sono i benvenuti) per favore prestami le tue opinioni.

  1. Change tutto può note e/o descrizioni a NVARCHAR(MAX) e assicurarsi che noi non escludiamo questi campi in tutto l'elenco. Fondamentalmente non fare mai un: SELECT * FROM [TableName] a meno che non stia recuperando solo un record.

  2. Rimuovere tutte le note e/o i campi descrizione e sostituirli con un riferimento chiave di assegnazione a una tabella [Notes].

    CREATE TABLE [dbo].[Notes] (
    [NoteId] [int] NOT NULL,
    [NoteText] [NVARCHAR]
    (MAX) NOT NULL)

Ovviamente uso l'opzione 1 preferirei perché cambierà così tanto nel nostro sistema, se andiamo con 2. Tuttavia, se l'opzione 2 è davvero l'unico buon modo di procedere, quindi almeno posso dire che questi cambiamenti sono necessari e ho fatto i compiti.


UPDATE: ho corse diversi test su un database di esempio con 100.000 record in esso. Quello che trovo è che a causa delle scansioni dell'indice del cluster, l'IO richiesto per l'opzione 1 è "grosso modo" il doppio di quello dell'opzione 2. Se seleziono un numero elevato di record (1000 o più) l'opzione 1 è due volte più lento anche se lo faccio non includere il campo di testo grande nella selezione. Mentre richiedo meno righe, le linee si sfocano di più. Io sono un'app web in cui le dimensioni di pagina pari a 50 sono la norma, quindi l'opzione 1 funzionerà, ma convertirò tutte le istanze all'opzione 2 nel (molto) prossimo futuro per la scalabilità.

risposta

5

Opzione 2 è meglio per diversi motivi:

  1. Quando interrogare le tabelle, i grandi campi di testo si riempiono rapidamente le pagine, costringendo il database per eseguire la scansione di più pagine per recuperare i dati. Questo è in particolare la tassazione quando non è necessario effettivamente restituire il testo dati.
  2. Come hai detto, ti dà un'interruzione netta per cambiare il tipo di dati in un colpo solo. Microsoft ha il TESTO deprecato in SQL Server 2008, quindi è necessario attenersi a VARCHAR/VARBINARY.
  3. Filegroup separati. Avere tutti i tuoi dati di testo in un più lento, posizione di archiviazione più economica potrebbe essere qualcosa che si decide di perseguire in il futuro. In caso contrario, nessun danno, nessun fallo .

Mentre l'opzione 1 è più semplice per ora, l'opzione 2 ti darà maggiore flessibilità a lungo termine. Il mio suggerimento sarebbe di implementare un semplice proof-of-concept con le informazioni "notes" separate dalla tabella principale ed eseguire alcune delle query su entrambi gli esempi. Confrontare i piani di esecuzione, le statistiche dei client e le letture I/O logiche (SET STATISTICS IO ON) per alcune delle query su queste tabelle.

Una breve nota a coloro che suggerisce l'uso di un testo/NTEXT da MSDN:

Questa funzionalità verrà rimossa in una versione futura di Microsoft SQL Server . Evitare di utilizzare questa funzione nel nuovo lavoro di sviluppo e pianificare modificare le applicazioni che attualmente utilizzano questa funzionalità. Utilizzare invece i tipi varchar (max), nvarchar (max) e varbinary (max) . Per ulteriori informazioni, vedere Utilizzo di tipi di dati di valore elevato.

2

mi piacerebbe andare con opzione 2.

È possibile creare una vista che unisce le due tabelle per rendere la transizione più facile per tutti, e poi passare attraverso un processo di pulizia che rimuove la vista e utilizza il tavolo unico ove possibile.

1

Il tipo di dati TEXT/NTEXT ha una lunghezza praticamente illimitata mentre si registra quasi nulla nel record.

Viene fornito con alcune stringhe collegate, come un comportamento speciale con le funzioni di stringa, ma per un campo secondario di tipo "note/descrizione" potrebbero essere meno problemi.

2

Si desidera utilizzare un campo TESTO. I campi TEXT non vengono memorizzati direttamente nella riga; invece, memorizza un puntatore ai dati di testo. Ciò è trasparente per le query, tuttavia, se si richiede un campo TESTO, verrà restituito il testo effettivo, non il puntatore.

In sostanza, l'utilizzo di un campo TESTO è un po 'tra le due soluzioni.Mantiene le righe della tabella molto più piccole rispetto all'utilizzo di un varchar, ma vorrai comunque evitare di chiederle nelle tue query, se possibile.

1

Giusto per espandere l'Opzione 2

Si potrebbe:

Rinominare MyTable esistente per MyTable_V2

spostare la colonna Note in una tabella Notes incollati (con 1: 1 che unisce ID)

Creare una vista denominata MyTable che unisce tabelle MyTable_V2 e note

Crea un trigger INSTEAD OF nella vista MyTable che salva la colonna Notes nella tabella Notes (SE NULL elimina quindi qualsiasi riga di Notes esistente, se NON è NULL, quindi Inserisci se non trovato, altrimenti Aggiorna).Esegui l'azione appropriata sulla tabella MyTable_V2

Nota: abbiamo avuto problemi nel fare ciò in presenza di una colonna calcolata in MyTable_V2 (penso che questo fosse il problema, in entrambi i casi abbiamo riscontrato problemi con tabelle "insolite")

Tutti i nuovi Insert/Update/Delete codice dovrebbe essere scritto di operare direttamente sui tavoli MyTable_V2 e Note

Opzionalmente: Avere l'inserimento di trigger su MyTable registrare il fatto che è stato chiamato (si può fare questo in minima parte , AGGIORNA una riga della tabella di log preesistente con GetDate() solo se la data della riga esistente è> 24 ore precedente, quindi eseguirà un aggiornamento solo una volta al giorno).

Quando non si ottengono più record di registro, è possibile rilasciare il trigger INSTEAD OF nella vista MyTable e ora si è completamente compatibili con MyTable_V2!

Enorme quantità di problemi da implementare, come avete ipotizzato.

In alternativa scorrere il codice per tutti i riferimenti a MyTable e modificarli in MyTable_V2, inserire una VISTA al posto di MyTable solo per SELECT e non creare il trigger INSTEAD OF.

Il mio piano sarebbe quello di correggere tutte le istruzioni Insert/Update/Delete che fanno riferimento alla MyTable ora deprecata. Per me questo sarebbe reso un po 'più semplice perché usiamo nomi univoci per tutte le tabelle e le colonne nel database, e usiamo gli stessi nomi in tutti i codici applicativi, quindi assicurarmi di aver trovato tutte le istanze con un semplice TROVA sarebbe alto.

P.S. Anche l'opzione 2 è preferibile se hai SELECT * in giro. Abbiamo avuto clienti in cui le prestazioni delle applicazioni si sono ridotte rapidamente quando hanno aggiunto colonne Text/Blob di grandi dimensioni alle tabelle esistenti, a causa delle istruzioni "lazy" SELECT *. Speriamo che non sia il caso nel tuo negozio!

Problemi correlati