Domanda

Sto cercando di capire il modo migliore per inserire un record in una singola tabella, ma solo se l'elemento non esiste già. La chiave in questo caso è un campo NVARCHAR (400). Per questo esempio, facciamo finta che sia il nome di una parola nel dizionario inglese di Oxford / inserisci qui il tuo dizionario preferito. Inoltre, suppongo che dovrò rendere il campo Word una chiave primaria. (la tabella avrà anche un identificatore univoco PK).

Quindi .. potrei ottenere queste parole che devo aggiungere alla tabella ...

ad es.

  • Gatto
  • Cane
  • Foo
  • Bar
  • PewPew
  • ecc ...

Quindi, tradizionalmente, proverei quanto segue (pseudo codice)

SELECT WordID FROM Words WHERE Word = @Word
IF WordID IS NULL OR WordID <= 0
    INSERT INTO Words VALUES (@Word)

es. Se la parola non esiste, inserirla.

Ora .. il problema di cui sono preoccupato è che stiamo ottenendo MOLTE hit ... quindi è possibile che la parola possa essere inserita da un altro processo tra SELECT e INSERT .. che poi lancerebbe un errore di vincolo? (ad es. a Condizioni di gara ).

Ho quindi pensato di poter fare quanto segue ...

INSERT INTO Words (Word)
SELECT @Word
WHERE NOT EXISTS (SELECT WordID FROM Words WHERE Word = @Word)

sostanzialmente, inserisci una parola quando non esiste.

Sintassi errata a parte, non sono sicuro che questo sia negativo o buono a causa di come blocca il tavolo (se lo fa) e non è così performante su un tavolo che ottiene letture massicce e molte scritture.

Quindi - cosa pensate / fate i guru Sql?

Speravo di avere un semplice inserto e "catturarlo" per eventuali errori generati.

È stato utile?

Soluzione

La tua soluzione:

INSERT INTO Words (Word)
    SELECT @Word
WHERE NOT EXISTS (SELECT WordID FROM Words WHERE Word = @Word)

... è buono quasi quanto diventa. Potresti semplificarlo a questo:

INSERT INTO Words (Word)
    SELECT @Word
WHERE NOT EXISTS (SELECT * FROM Words WHERE Word = @Word)

... perché EXISTS non ha effettivamente bisogno di restituire alcun record, quindi l'ottimizzatore di query non si preoccuperà di guardare quali campi hai richiesto.

Come hai detto, tuttavia, questo non è particolarmente performante, perché bloccherà l'intera tabella durante INSERT. Ad eccezione del fatto che, se aggiungi un indice univoco (non deve essere la chiave primaria) a Word, dovrai solo bloccare le pagine pertinenti.

L'opzione migliore è simulare il carico previsto e osservare le prestazioni con SQL Server Profiler. Come con qualsiasi altro campo, l'ottimizzazione prematura è una cosa negativa. Definisci metriche delle prestazioni accettabili, quindi misura prima di fare qualsiasi altra cosa.

Se questo non ti dà ancora prestazioni adeguate, allora ci sono un sacco di tecniche dal campo di data warehousing che potrebbero aiutare.

Altri suggerimenti

Penso di aver trovato una risposta migliore (o almeno più veloce) a questo. Crea un indice come:

CREATE UNIQUE NONCLUSTERED INDEX [IndexTableUniqueRows] ON [dbo].[table] 
(
    [Col1] ASC,
    [Col2] ASC,

)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = ON, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]

Includi tutte le colonne che definiscono l'unicità. La parte importante è IGNORE_DUP_KEY = ON. Ciò trasforma gli inserti non univoci in avvisi. SSIS ignora questi avvisi e puoi comunque utilizzare anche il caricamento rapido.

Se si utilizza MS SQL Server, è possibile creare un indice univoco sulle colonne della tabella che deve essere univoco (documentato qui ):

CREATE UNIQUE [ CLUSTERED | NONCLUSTERED ] INDEX <index_name>
    ON Words ( word [ ASC | DESC ])

Specifica Clustered o NonClustered , a seconda del tuo caso. Inoltre, se lo desideri ordinato (per consentire una ricerca più rapida), specifica ASC o DESC per l'ordinamento.

Vedi qui , se vuoi saperne di più sull'architettura degli indici.

Altrimenti, potresti usare VINCOLI UNICI come documentato qui :

ALTER TABLE Words
ADD CONSTRAINT UniqueWord
UNIQUE (Word); 

Ho avuto un problema simile ed è così che l'ho risolto

insert into Words
( selectWord , Fixword)
SELECT word,'theFixword'
FROM   OldWordsTable
WHERE 
(
    (word LIKE 'junk%') OR
     (word LIKE 'orSomthing') 

)
and word not in 
    (
        SELECT selectWord FROM words WHERE selectWord = word
    ) 

mentre il vincolo univoco è sicuramente un modo per procedere, puoi anche usarlo per la tua logica di inserimento: http://www.sqlteam.com/ ARTICOLO / applicativi-serrature-o-mutex-in-sql-server-2005

praticamente non metti alcun lucchetto sulla tabella qui sotto, quindi non preoccuparti delle letture mentre i controlli di esistenza verranno eseguiti correttamente.

è un mutex nel codice sql.

Non posso parlare con i particolari di MS SQL, ma un punto di una chiave primaria in SQL è garantire l'univocità. Quindi, per definizione in termini SQL generici, una chiave primaria è uno o più campi univoci per una tabella. Mentre ci sono diversi modi per applicare questo comportamento (sostituire la vecchia voce con quella nuova anziché rifiutare quella nuova), sarei sorpreso se MS SQL entrambi non avessero un meccanismo per applicare questo comportamento e che non lo fosse rifiuta la nuova voce. Assicurati solo di impostare la chiave primaria sul campo Word e che dovrebbe funzionare.

Ancora una volta, disconosco che questo è tutto dalle mie conoscenze dalla programmazione MySQL e dalla mia classe di database, quindi mi scuso se sono fuori dalle complessità di MS SQL.

declare @Error int

begin transaction
  INSERT INTO Words (Word) values(@word)
  set @Error = @@ERROR
  if @Error <> 0 --if error is raised
  begin
      goto LogError
  end
commit transaction
goto ProcEnd

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