Quali sono gli anti-pattern SQL più comuni? [chiuso]


232

Tutti noi che lavoriamo con database relazionali abbiamo imparato (o stanno imparando) che SQL è diverso. Ottenere i risultati desiderati e farlo in modo efficiente implica un processo noioso in parte caratterizzato dall'apprendimento di paradigmi sconosciuti e dalla scoperta che alcuni dei nostri schemi di programmazione più familiari non funzionano qui. Quali sono gli antipattern comuni che hai visto (o che hai commesso)?


Questa è una domanda che non è conforme agli standard più recenti su quale tipo di domanda è appropriata per Stack Overflow. Quando è stato chiesto, questo potrebbe non essere vero.
David Manheim,

@casperOne non esiste una clausola di "significato storico" che non renderebbe accettabile questa domanda?
Amy B,

26
Trovo triste che una delle domande più utili sul sito di wohole sia chiusa perché non costruttiva.
HLGEM,

11
@HLGEM Sono totalmente d'accordo. Questa domanda è un perfetto esempio di tutto ciò che è sbagliato in StackExchange
Kevin Morse,

1
L'argomento è assolutamente importante e pertinente. Ma la domanda è troppo aperta ed è per questo che le risposte descrivono ognuna delle bugbear anti-pattern personali di un singolo ingegnere.
Shane,

Risposte:


156

Sono costantemente deluso dalla tendenza della maggior parte dei programmatori di mescolare la loro logica dell'interfaccia utente nel livello di accesso ai dati:

SELECT
    FirstName + ' ' + LastName as "Full Name",
    case UserRole
        when 2 then "Admin"
        when 1 then "Moderator"
        else "User"
    end as "User's Role",
    case SignedIn
        when 0 then "Logged in"
        else "Logged out"
    end as "User signed in?",
    Convert(varchar(100), LastSignOn, 101) as "Last Sign On",
    DateDiff('d', LastSignOn, getDate()) as "Days since last sign on",
    AddrLine1 + ' ' + AddrLine2 + ' ' + AddrLine3 + ' ' +
        City + ', ' + State + ' ' + Zip as "Address",
    'XXX-XX-' + Substring(
        Convert(varchar(9), SSN), 6, 4) as "Social Security #"
FROM Users

Normalmente, i programmatori lo fanno perché intendono associare il loro set di dati direttamente a una griglia, ed è conveniente avere sul server un formato SQL Server piuttosto che un formato.

Le query come quella mostrata sopra sono estremamente fragili perché associano strettamente il livello dati al livello dell'interfaccia utente. Inoltre, questo stile di programmazione impedisce che le procedure memorizzate siano riutilizzabili.


10
Un buon modello poster-child per il massimo accoppiamento attraverso il maggior numero possibile di livelli / livelli di astrazione.
dkretz,

3
Potrebbe non essere utile per il disaccoppiamento, anche se per motivi di prestazioni ho fatto cose del genere spesso, le modifiche iterative apportate da SQL Server sono più rapide di quelle eseguite dal codice a livello intermedio. Non ti capisco il punto di riusabilità - niente ti impedisce di eseguire l'SP e rinominare i cols se lo desideri.
Joe Pineda,

54
Il mio preferito è quando le persone incorporano HTML E javascript, ad esempio SELEZIONA '<a href=... onclick="">' + nome '</a>'
Matt Rogish,

15
Con query come questa, è possibile modificare la griglia in un sito Web con una semplice dichiarazione di modifica. Oppure modifica il contenuto di un'esportazione o riformatta una data in un rapporto. Questo rende felici i clienti e mi fa risparmiare tempo. Quindi grazie, ma no grazie, seguirò domande come questa.
Andomar,

4
@Matt Rogish - Gesù, qualcuno lo fa davvero?
Axarydax,

118

Ecco i miei 3 migliori.

Numero 1. Impossibile specificare un elenco di campi. (Modifica: per evitare confusione: questa è una regola del codice di produzione. Non si applica agli script di analisi una tantum, a meno che non sia l'autore.)

SELECT *
Insert Into blah SELECT *

dovrebbe essere

SELECT fieldlist
Insert Into blah (fieldlist) SELECT fieldlist

Numero 2. Utilizzando un cursore e ciclo while, quando lo farà un ciclo while con una variabile loop.

DECLARE @LoopVar int

SET @LoopVar = (SELECT MIN(TheKey) FROM TheTable)
WHILE @LoopVar is not null
BEGIN
  -- Do Stuff with current value of @LoopVar
  ...
  --Ok, done, now get the next value
  SET @LoopVar = (SELECT MIN(TheKey) FROM TheTable
    WHERE @LoopVar < TheKey)
END

Numero 3. DateLogic attraverso i tipi di stringa.

--Trim the time
Convert(Convert(theDate, varchar(10), 121), datetime)

Dovrebbe essere

--Trim the time
DateAdd(dd, DateDiff(dd, 0, theDate), 0)

Ho visto un recente picco di "Una query è meglio di due, prima?"

SELECT *
FROM blah
WHERE (blah.Name = @name OR @name is null)
  AND (blah.Purpose = @Purpose OR @Purpose is null)

Questa query richiede due o tre piani di esecuzione diversi a seconda dei valori dei parametri. Viene generato un solo piano di esecuzione e bloccato nella cache per questo testo sql. Tale piano verrà utilizzato indipendentemente dal valore dei parametri. Ciò si traduce in scarse prestazioni intermittenti. È molto meglio scrivere due query (una query per piano di esecuzione previsto).


7
hmmm, ti darò un +1 solo per i punti 2 e 3, ma gli sviluppatori hanno esagerato con la regola 1. A volte ha il suo posto.
annakata,

1
Qual è il ragionamento dietro # 1?
jalf

29
Quando usi select *, ottieni tutto ciò che è nella tabella. Quelle colonne possono cambiare nome e ordine. Il codice client si basa spesso su nomi e ordini. Ogni 6 mesi mi viene chiesto come conservare l'ordine delle colonne quando si modifica una tabella. Se la regola fosse seguita non avrebbe importanza.
Amy B,

Ho usato # 2 a volte, altri ho seguito il percorso del cursore (anche se poi prima ho salvato i risultati della query su una tabella var, apro il cursore su quello). Mi sono sempre chiesto se qualcuno ha fatto un test delle prestazioni di entrambi.
Joe Pineda,

4
... ma ovviamente i cursori dovrebbero quasi sempre essere l'ultima risorsa, dopo l'incapacità di capire come eseguire il lavoro con SQL basato su set. Una volta ho trascorso circa 45 minuti a dissezionare con cura un cursore PL / SQL orrendo e gigantesco in una procedura memorizzata (tracciato diagrammi della cosa marcia), che popolava una grande tabella temporanea, quindi selezionava il contenuto della tabella temporanea per richiamare un chiamante rapporto. Sono stati necessari 8,5 minuti per l'esecuzione, su hardware sostanziale. Dopo aver schematizzato il tutto, sono stato in grado di sostituirlo con un'unica query che ha restituito gli stessi risultati in meno di 2 secondi. Cursori, amico ...
Craig,

71
  • Campi password leggibili dall'uomo , egad. Autoesplicativo.

  • Usando LIKE su colonne indicizzate , e sono quasi tentato di dire LIKE in generale.

  • Riciclaggio dei valori PK generati da SQL.

  • Sorpresa nessuno ha ancora menzionato la divinità . Nulla dice "organico" come 100 colonne di bit flag, stringhe di grandi dimensioni e numeri interi.

  • Poi c'è il modello "Mi mancano i file .ini" : memorizzazione di CSV, stringhe delimitate da pipe o altri dati richiesti di analisi in campi di testo di grandi dimensioni.

  • E per server MS SQL l'uso di cursori a tutti . C'è un modo migliore per eseguire qualsiasi attività del cursore.

Modificato perché ce ne sono così tanti!


19
torto sui cursori, esiterei a dire che fare qualsiasi cosa in particolare è giusto al 100% o sbagliato al 100%
Shawn,

4
Finora ogni esempio di difesa del cursore che ho visto sta usando lo strumento sbagliato per il lavoro. Ma se tutto ciò che sai è SQL, o lo usi in modo inappropriato o impari a scrivere altri tipi di software.
dkretz,

3
@tuinstoel: In che modo LIKE '% blah%' riesce a utilizzare un indice? L'indicizzazione si basa sull'ordinamento e questo esempio cerca una posizione centrale casuale di una stringa. (Gli indici ordinano per il 1 ° carattere, quindi guardare i 4 caratteri centrali dà un ordine praticamente casuale ...)
MatBailie,

12
Sulla maggior parte dei server di database (almeno quelli che ho usato), LIKE può usare gli indici .. purché sia ​​una ricerca di prefissi (LIKE 'xxx%') - cioè, purché i caratteri jolly non lo facciano vieni prima nella stringa di ricerca. Penso che qui potresti parlare un po 'di scopi incrociati.
Cowan,

10
È come se non ti piacesse LIKE '%LIKE'.
Johan,

62

Non è necessario scavare a fondo per questo: non usare istruzioni preparate.


3
Sì. Seguito da vicino nello stesso contesto, nella mia esperienza, con "errori non intrappolanti".
dkretz,

1
@stesch: questo non è nulla se paragonato all'uso delle viste e alla data di segnalazione variabile. Le visualizzazioni sono un antipattern se si dispone di una data di segnalazione variabile (suppongo che la maggior parte delle applicazioni abbia). Aggiungerei questo in una risposta separata, ma purtroppo è chiusa.
Stefan Steiger,

56

Utilizzo di alias di tabella senza significato:

from employee t1,
department t2,
job t3,
...

Rende la lettura di una grande istruzione SQL molto più difficile di quanto debba essere


49
alias? inferno ho visto nomi di colonne reali come quello
annakata,

10
gli alias concreti sono OK. Se vuoi un nome significativo, allora non usare affatto un alias.
Joel Coehoorn,

43
Non ha detto "conciso", ha detto "insignificante". Nel mio libro non ci sarebbe nulla di sbagliato nell'usare e, d e j come alias nella query di esempio.
Robert Rossney,

11
Assolutamente, Robert - e, d e j starebbero bene con me.
Tony Andrews,

8
Vorrei usare emp per dipendente, reparto per reparto e lavoro per lavoro (o forse jb) :)
Andrei Rînea

53
var query = "select COUNT(*) from Users where UserName = '" 
            + tbUser.Text 
            + "' and Password = '" 
            + tbPassword.Text +"'";
  1. Confidando ciecamente nell'input dell'utente
  2. Non utilizzare query con parametri
  3. Password in chiaro

Tutto ciò può essere utilmente affrontato usando un layer abstracton di database di qualche (qualsiasi) tipo.
dkretz,

@doofledorfer: D'accordo, un livello intermedio sarebbe sicuramente migliore in un caso come questo, oltre a fornire la memorizzazione dei risultati nella cache come un piacevole effetto collaterale.
Joe Pineda,

Fantastico esempio Se uno sviluppatore cerca di sostituirlo con una buona soluzione, è a metà strada per diventare un decente sviluppatore SQL.
Steve McLeod,

46

I miei bugbear sono le 450 tabelle di accesso della colonna che sono state messe insieme dal figlio di 8 anni del toelettatore di cani dei migliori amici dell'amministratore delegato e la tabella di ricerca ingannevole che esiste solo perché qualcuno non sa come normalizzare correttamente una struttura di dati.

In genere, questa tabella di ricerca è simile alla seguente:

ID INT,
Nome NVARCHAR (132),
IntValue1 INT,
IntValue2 INT,
CharValue1 NVARCHAR (255),
CharValue2 NVARCHAR (255),
Data1 DATETIME,
Data2 DATETIME

Ho perso il conto del numero di clienti che ho visto che hanno sistemi che si basano su abominazioni come questa.


1
Peggio ancora, ho letto che nella versione più recente di Access che in realtà è supportata automaticamente, il che teme incoraggerà maggiormente questo feticismo di colonna Value1, Value2, Value3 ...
Joe Pineda,

Aspetta, quindi il figlio di 8 anni è il figlio del toelettatore?
barrypicker,

28

Quelli che non mi piacciono di più sono

  1. Usando gli spazi durante la creazione di tabelle, sprocs ecc. Sto bene con CamelCase o under_scores e singolare o plurale e MAIUSCOLO o minuscolo ma dovendo fare riferimento a una tabella o colonna [con spazi], specialmente se [è stranamente spaziato] (sì, Mi sono imbattuto in questo) mi irrita davvero.

  2. Dati denormalizzati. Una tabella non deve essere perfettamente normalizzata, ma quando mi imbatto in una tabella di dipendenti che ha informazioni sul loro punteggio di valutazione corrente o sul loro elemento principale, mi dice che probabilmente dovrò creare una tabella separata ad un certo punto e quindi prova a mantenerli sincronizzati. Prima normalizzerò i dati e poi se vedo un posto in cui la denormalizzazione aiuta, la prenderò in considerazione.

  3. Uso eccessivo di viste o cursori. Le viste hanno uno scopo, ma quando ogni tabella è racchiusa in una vista è troppo. Ho dovuto usare i cursori alcune volte, ma generalmente puoi usare altri meccanismi per questo.

  4. Accesso. Un programma può essere un anti-schema? Abbiamo SQL Server al mio lavoro, ma un certo numero di persone utilizza l'accesso a causa della sua disponibilità, "facilità d'uso" e "facilità" per gli utenti non tecnici. C'è troppo qui per approfondire, ma se sei stato in un ambiente simile, lo sai.


2
# 4 - c'è un altro thread solo per <a href=' stackoverflow.com/questions/327199/...> :).
dkretz,

4
L'accesso NON è un DBMS. È un ambiente RAD, con un gestore di database molto semplice incluso. SQL Server, Oracle, et al. sarà mai sostituirlo, a meno che non si aggiunge un VB-come il linguaggio e un Crystal Reports come struttura.
Joe Pineda,

26

usa SP come prefisso del nome della procedura del negozio perché cercherà prima nella posizione delle procedure di sistema piuttosto che in quelle personalizzate.


1
Può anche essere esteso all'uso di qualsiasi altro prefisso comune per tutte le procedure memorizzate, rendendo più difficile la selezione di un elenco ordinato.
dkretz,

7
+1 per il commento doofledorfer !! L'ho visto molto, lo trovo idiota e rende davvero molto difficile la ricerca di un particolare SP !!! Esteso anche a "vw_" per le visualizzazioni, "tbl_" per le tabelle e simili, come le odio!
Joe Pineda,

1
I prefissi possono essere utili se stai copiando gli oggetti in file (ad esempio: per il controllo del codice sorgente, distribuzioni o migrazione)
Rick

1
Perché mai sarebbe utile aggiungere come prefisso ogni singola procedura memorizzata con sp o usp? Rende solo più difficile scansionare l'elenco per quello che desideri.
Ryan Lundy,

25

Uso eccessivo di tabelle e cursori temporanei.


2
Buone prove del fatto che "tutto ciò che so sono i linguaggi procedurali".
dkretz,

2
L'uso eccessivo di qualcosa è per definizione indesiderato. Un esempio specifico di dove non sarebbe necessario l'uso di tabelle / cursori temporanei sarebbe utile.
Jace Rhea,

6
Principalmente vedo tabelle temporanee sottoutilizzate. con SQL Server spesso si ottengono miglioramenti delle prestazioni facendo cose con un sacco di tabelle temporanee invece di una query monolitica.
Cervo,

24

Per la memorizzazione dei valori temporali, utilizzare solo il fuso orario UTC. L'ora locale non deve essere utilizzata.


3
Non ho ancora trovato una buona soluzione semplice per la conversione dall'ora UTC all'ora locale per le date passate, quando è necessario prendere in considerazione l'ora legale, con date di modifica variabili in anni e paesi, nonché tutte le eccezioni all'interno dei paesi. Quindi UTC non ti salva dalla complessità della conversione. Tuttavia, è importante avere un modo per conoscere il fuso orario di ogni datetime memorizzato.
ckarras,

1
@CsongorHalmai Molti luoghi praticano l'ora legale, quindi i valori dell'ora entro un'ora dallo spostamento dell'orario possono essere ambigui.
Frank Schwieterman,

Questo è certamente giusto per il presente e il passato, ma per il futuro, specialmente per il futuro abbastanza lontano, i fusi orari espliciti sono spesso una necessità. Se hai un'opzione di 30 anni che è stata appena scritta e scade il 2049-09-27T17: 00: 00 a New York, non puoi presumere ciecamente che sarà 21: 00: 00Z. Il Congresso degli Stati Uniti potrebbe cambiare le regole dell'ora legale. Devi mantenere l'ora locale e il fuso orario vero (America / New_York) separati.
John Cowan,

23

utilizzando @@ IDENTITY anziché SCOPE_IDENTITY ()

Citato da questa risposta :

  • @@ IDENTITY restituisce l'ultimo valore di identità generato per qualsiasi tabella nella sessione corrente, in tutti gli ambiti. Devi stare attento qui, poiché è attraverso gli ambiti. È possibile ottenere un valore da un trigger, anziché dall'istruzione corrente.
  • SCOPE_IDENTITY restituisce l'ultimo valore di identità generato per qualsiasi tabella nella sessione corrente e nell'ambito corrente. Generalmente quello che vuoi usare.
  • IDENT_CURRENT restituisce l'ultimo valore di identità generato per una tabella specifica in qualsiasi sessione e ambito. Questo ti consente di specificare da quale tabella desideri il valore, nel caso in cui i due sopra non siano proprio ciò di cui hai bisogno (molto raro). È possibile utilizzarlo se si desidera ottenere il valore IDENTITÀ corrente per una tabella in cui non è stato inserito un record.

+1 molto vero, potrebbe causare un bug che sarebbe difficile da eliminare
Axarydax,

23

Riutilizzare un campo "morto" per qualcosa a cui non era destinato (ad es. Memorizzazione dei dati utente in un campo "Fax") - molto allettante come soluzione rapida!


21
select some_column, ...
from some_table
group by some_column

e supponendo che il risultato sarà ordinato per some_column. L'ho visto un po 'con Sybase, dove il presupposto vale (per ora).


1
ha votato per SEMPRE assumere l'ordinamento, solo perché quello era il modo in cui è apparso nello strumento di query una volta
Joel Coehoorn,

3
Ho anche visto questo segnalato come un bug più di una volta.
dkretz,

6
in MySQL, è documentato per ordinare. < dev.mysql.com/doc/refman/5.0/en/select.html >. Quindi dai la colpa a MySQL (di nuovo).
derobert il

1
In Oracle, i risultati non ordinati (quasi) corrispondevano sempre al raggruppamento, fino alla versione 10G. Molte rielaborazioni per gli sviluppatori che erano soliti tralasciare ORDER BY!
Tony Andrews,

1
Sono stato anche in un corso di formazione in cui questo è stato dichiarato come un fatto per SQL Server. Ho dovuto protestare molto forte. Per il semplice salvataggio di 20 caratteri, fai affidamento su comportamenti oscuri o non documentati.
erikkallen,

20
SELECT FirstName + ' ' + LastName as "Full Name", case UserRole when 2 then "Admin" when 1 then "Moderator" else "User" end as "User's Role", case SignedIn when 0 then "Logged in" else "Logged out" end as "User signed in?", Convert(varchar(100), LastSignOn, 101) as "Last Sign On", DateDiff('d', LastSignOn, getDate()) as "Days since last sign on", AddrLine1 + ' ' + AddrLine2 + ' ' + AddrLine3 + ' ' + City + ', ' + State + ' ' + Zip as "Address", 'XXX-XX-' + Substring(Convert(varchar(9), SSN), 6, 4) as "Social Security #" FROM Users

Oppure, stipando tutto in una riga.


Ho usato la query di un commento precedente, solo perché quella era la prima istruzione SQL che avevo a disposizione.
Jasper Bekkers,

17
  • La FROM TableA, TableB WHEREsintassi per JOINS anzichéFROM TableA INNER JOIN TableB ON

  • Fare ipotesi sul fatto che una query verrà restituita ordinata in un certo modo senza inserire una clausola ORDER BY, solo perché era così che si presentava durante il test nello strumento di query.


5
I miei amministratori di database Oracle si lamentano sempre che utilizzo "join ANSI", ovvero ciò che presenti nel modo corretto. Ma continuo a farlo e sospetto che in fondo sappiano che è meglio.
Steve McLeod,

1
Sospetto che Oracle desideri che lo standard SQL sparisse. :-) Inoltre, non puoi combinare JOIN impliciti ed espliciti (aka ANSI JOINs) in MySQL 5 - non funziona. Questo è un altro argomento per JION espliciti.
staticsan

3
Direi che anche A INNER JOIN B ON è un modello anti. Preferisco UN INNER JOIN B USING.
John Nilsson,

Oracle supporta ora la sintassi ANSI, ma in passato aveva questa strana sintassi per i join esterni e ci sono ancora troppe persone che la usano ancora.
Cervo,

bene ... Oracle non ti consente ancora di utilizzare i join ANSI per visualizzazioni materializzate in aggiornamento rapido, su
commit

14

Imparare SQL nei primi sei mesi della loro carriera e non imparare mai nient'altro per i prossimi 10 anni. In particolare, non apprendere o utilizzare efficacemente le funzionalità SQL analitiche / per finestre. In particolare l'uso di over () e la partizione di.

Le funzioni della finestra, come le funzioni di aggregazione, eseguono un'aggregazione su un set definito (un gruppo) di righe, ma anziché restituire un valore per gruppo, le funzioni della finestra possono restituire più valori per ciascun gruppo.

Vedi l' Appendice A del ricettario SQL O'Reilly per una bella panoramica delle funzioni di finestratura.


12

Devo inserire qui il mio preferito preferito, solo per completare l'elenco. Il mio antipasto preferito non sta testando le tue domande .

Questo vale quando:

  1. La tua query coinvolge più di una tabella.
  2. Pensi di avere un design ottimale per una query, ma non preoccuparti di testare i tuoi presupposti.
  3. Accetti la prima query che funziona, senza alcun indizio sul fatto che sia anche vicino all'ottimizzazione.

E tutti i test eseguiti su dati atipici o insufficienti non contano. Se si tratta di una procedura memorizzata, inserire l'istruzione test in un commento e salvarla, con i risultati. Altrimenti, inseriscilo in un commento nel codice con i risultati.


Una tecnica molto utile per un test T-SQL minimo: nel file .SQL in cui si definisce SP, UDF, ecc., Immediatamente dopo aver creato un test a blocchi come IF 1 = 2 BEGIN (casi di esempio per il codice, con risultati previsti come commenti) FINE
Joe Pineda

SQL Server analizza il codice all'interno del blocco di test, anche se non viene mai eseguito. Quindi quando il tuo oggetto viene modificato e riceve più parametri, o di tipo diverso, ecc. O viene modificato un oggetto da cui dipende, riceverai un errore semplicemente chiedendo un piano di esecuzione!
Joe Pineda,

Non è sempre possibile testare con dati reali. Spesso il server dev / server "test" è sottopagato e ottiene una frazione del server live. Generalmente i test sono disapprovati sul server live. Alcuni posti sono migliori e dispongono di un server di test o di gestione temporanea con dati in tempo reale.
Cervo,

11

Abuso di tabella temporanea.

In particolare questo genere di cose:

SELECT personid, firstname, lastname, age
INTO #tmpPeople
FROM People
WHERE lastname like 's%'

DELETE FROM #tmpPeople
WHERE firstname = 'John'

DELETE FROM #tmpPeople
WHERE firstname = 'Jon'

DELETE FROM #tmpPeople
WHERE age > 35

UPDATE People
SET firstname = 'Fred'
WHERE personid IN (SELECT personid from #tmpPeople)

Non creare una tabella temporanea da una query, solo per eliminare le righe non necessarie.

E sì, ho visto pagine di codice in questo modulo nei DB di produzione.


1
+1, sono d'accordo. Tuttavia, ho trovato almeno uno o due casi in cui questa tecnica ha migliorato le prestazioni: le query in questione erano a dir poco complesse.
a'r

1
Vero - hanno un posto, ma non in ogni domanda :)
geofftnz,

1
A volte devi farlo se le condizioni sono super complicate. È vero che può essere abusato agli estremi. Ma molte volte una semplice eliminazione è molto più semplice della logica per ottenere il caso nella query iniziale. Inoltre, a volte se la clausola non è ampia, la query iniziale rallenterà. Ma farlo sulla tabella temporanea più piccola è più efficiente. E altre volte continui ad aggiungere casi che gli uomini d'affari continuano ad aggiungere dopo il fatto.
Cervo,

9

Vista contraria: eccessiva ossessione per la normalizzazione.

La maggior parte dei sistemi SQL / RBDB offre molte funzionalità (transazioni, replica) che sono piuttosto utili, anche con dati non normalizzati. Lo spazio su disco è economico e talvolta può essere più semplice (codice più semplice, tempo di sviluppo più veloce) manipolare / filtrare / cercare dati recuperati, piuttosto che scrivere uno schema 1NF e gestire tutte le seccature (join complessi, sottoselezioni cattive) , eccetera).

Ho scoperto che i sistemi sovra-normalizzati sono spesso un'ottimizzazione prematura, specialmente durante le prime fasi di sviluppo.

(altri pensieri su di esso ... http://writeonly.wordpress.com/2008/12/05/simple-object-db-using-json-and-python-sqlite/ )


22
Penso che la non normalizzazione sia spesso un'ottimizzazione prematura.
tuinstoel

A volte lo è, a volte no. Fortunatamente, è spesso facile da testare e diverse opzioni funzionano con diverse esigenze di db.
Gregg Lind,

17
La normalizzazione non è solo per il risparmio di spazio su disco. Serve anche a creare una fonte autorevole per i dati. Se i dati sono memorizzati in un solo posto, la coerenza non è un sottoprodotto di un'attenta codifica, ma è invece un sottoprodotto del design.
Grant Johnson,

La memorizzazione di dati composti in formato JSON è una cosa: c'è sempre più supporto per esso ed è un compromesso consapevole. L'uso di valori separati da virgola (o qualsiasi altra cosa) nel tentativo di salvare un join è un centesimo e una sterlina.
John Cowan,

Le soluzioni noSQL mostrano un vantaggio in termini di prestazioni a scapito di dati duplicati eliminando le ricerche su più tabelle. Mette l'intera cosa di normalizzazione in testa. In alcuni esempi i dati vengono raccolti in più punti per garantire che un processo abbia il tempo di risposta più veloce possibile. Naturalmente, vengono in gioco domande su fonti autorevoli.
barrypicker,

9

Ho appena messo insieme questo, basato su alcune delle risposte SQL qui su SO.

È un grave antipattern pensare che i trigger siano per i database come i gestori di eventi sono per OOP. C'è questa percezione secondo cui qualsiasi vecchia logica può essere messa in trigger, per essere attivata quando una transazione (evento) si verifica su un tavolo.

Non vero. Una delle grandi differenze è che i trigger sono sincroni, con una vendetta, perché sono sincroni su un'operazione impostata, non su un'operazione di riga. Sul lato OOP, esattamente l'opposto: gli eventi sono un modo efficiente per implementare transazioni asincrone.


8

Stored procedure o funzioni senza commenti ...


E viste;) Funzioni vere, tranne le funzioni con valori di tabella (= viste con parametri).
Stefan Steiger,

7

1) Non so che sia un anti-pattern "ufficiale", ma non mi piace e cerco di evitare i letterali di stringa come valori magici in una colonna del database.

Un esempio dalla tabella 'image' di MediaWiki:

img_media_type ENUM("UNKNOWN", "BITMAP", "DRAWING", "AUDIO", "VIDEO", 
    "MULTIMEDIA", "OFFICE", "TEXT", "EXECUTABLE", "ARCHIVE") default NULL,
img_major_mime ENUM("unknown", "application", "audio", "image", "text", 
    "video", "message", "model", "multipart") NOT NULL default "unknown",

(Noto solo involucri diversi, un'altra cosa da evitare)

Progetto casi come ricerche int in tabelle ImageMediaType e ImageMajorMime con chiavi primarie int.

2) conversione data / stringa che si basa su impostazioni NLS specifiche

CONVERT(NVARCHAR, GETDATE())

senza identificatore di formato


E nessuna indentazione sintattica, neanche. Argghh.
dkretz,

2
Perché è così male? sicuramente se stai cercando di esprimere un insieme di valori, funziona bene come una tabella di ricerca e si adatta meglio al codice che lo chiama. Preferisco avere un enum nel mio codice dell'app che si associa a un vincolo enum nel mio DB piuttosto che un enum nel mio codice dell'app che si associa a righe specifiche di una tabella di ricerca. Sembra più pulito.
Jack Ryan,

@JackRyan: Questo è un male perché quando cambi l'elenco enum in seguito, devi ricordarti di cambiarlo in due punti ora. Viola il secco . Il database dovrebbe essere l'unica fonte di verità.
Gerrat,

7

Sottoquery identiche in una query.


10
Sfortunatamente, a volte non puoi proprio evitarlo - in SQL 2000 non esisteva una parola chiave "WITH" e l'uso di UDF per incapsulare sottoquery comuni a volte porta a penali di prestazione, dai la colpa a MS su questo ...
Joe Pineda,

Bene, si spera che riescano ad aggiungerlo uno di questi giorni.
EvilTeach

In SQL 2000, è possibile utilizzare le variabili di tabella.
ricorsivo il

@recursive: non puoi avere indici su una variabile di tabella, il che spesso lo renderà più lento di una sottoquery. Tuttavia, è possibile utilizzare una tabella temporanea con indici personalizzati.
Rick,

Fantastico, ho lavorato con SQL per anni e non sapevo nemmeno che esistessero le comuni espressioni da tavolo (sebbene ne avrei avuto bisogno). Ora faccio! Grazie!
sleske,

7
  • La vista alterata - Una vista che viene modificata troppo spesso e senza preavviso o motivo. Il cambiamento verrà notato nel momento più inappropriato o peggio sarà sbagliato e mai notato. Forse l'applicazione si interromperà perché qualcuno ha pensato a un nome migliore per quella colonna. Di norma le opinioni dovrebbero estendere l'utilità delle tabelle di base mantenendo un contratto con i consumatori. Risolvi i problemi, ma non aggiungere funzionalità o peggiorare il comportamento, per questo creare una nuova vista. Per mitigare non condividere le opinioni con altri progetti e, utilizzare CTE quando le piattaforme lo consentono. Se il tuo negozio ha un DBA, probabilmente non puoi modificare le visualizzazioni, ma tutte le visualizzazioni saranno obsolete e / o inutili in quel caso.

  • Il! Paramed - Una query può avere più di uno scopo? Probabilmente, ma la prossima persona che lo legge non lo saprà fino a quando la meditazione profonda. Anche se non ne hai bisogno in questo momento è probabile che lo farai, anche se è "solo" il debug. L'aggiunta di parametri riduce i tempi di manutenzione e mantiene le cose ASCIUTTE. Se hai una clausola where dovresti avere dei parametri.

  • Il caso per nessun caso -

    SELECT  
    CASE @problem  
      WHEN 'Need to replace column A with this medium to large collection of strings hanging out in my code.'  
        THEN 'Create a table for lookup and add to your from clause.'  
      WHEN 'Scrubbing values in the result set based on some business rules.'  
        THEN 'Fix the data in the database'  
      WHEN 'Formating dates or numbers.'   
        THEN 'Apply formating in the presentation layer.'  
      WHEN 'Createing a cross tab'  
        THEN 'Good, but in reporting you should probably be using cross tab, matrix or pivot templates'   
    ELSE 'You probably found another case for no CASE but now I have to edit my code instead of enriching the data...' END  

Mi è piaciuto molto il terzo. Lo sto già usando localmente ...
alphadogg,

Grazie per gli oggetti di scena. :)
jason saldo,

5

I due che trovo di più e che possono avere un costo significativo in termini di prestazioni sono:

  • Utilizzo dei cursori anziché di un'espressione basata su set. Immagino che questo si verifichi frequentemente quando il programmatore sta pensando in modo procedurale.

  • Utilizzando le query secondarie correlate, quando un join a una tabella derivata può eseguire il lavoro.


Sono d'accordo se intendi ciò che penso che intendi; sebbene una sottoquery correlata sia un tipo di tabella derivata IIRC.
dkretz,

1
Una tabella derivata è un'operazione impostata, mentre una sottoquery correlata viene eseguita per ogni riga della query esterna, rendendola meno efficiente (9 volte su 10)
Mitch Wheat

Un paio di anni fa ho scoperto con mia sorpresa che SQL S. è in qualche modo ottimizzato per la gestione di query correlate: per quelle semplici si ottiene lo stesso piano di esecuzione di una query logicamente equivalente usando un JOIN! Inoltre, le query correlate che mettono Oracle in ginocchio funzionano solo lentamente su SQL S.!
Joe Pineda,

Ecco perché lo collaudo sempre in entrambi i modi. E io <i> faccio </> di solito lo provo in entrambi i modi. In pratica, per SQL Server comunque, di solito ho trovato che lo sq correlato non è più lento.
dkretz,

3
PER FAVORE, comprendi che una sottoquery correlata e un'unione sono IDENTICHE (nella maggior parte dei casi). Non sono nemmeno cose diverse che sono ottimizzate l'una con l'altra, ma solo diverse rappresentazioni testuali della stessa operazione.
erikkallen,

5

Inserimento di elementi nelle tabelle temporanee, in particolare le persone che passano da SQL Server a Oracle hanno l'abitudine di abusare delle tabelle temporanee. Usa solo le istruzioni selezionate nidificate.


5

Gli sviluppatori che scrivono query senza avere una buona idea di ciò che rende le applicazioni SQL (sia singole query che sistemi multiutente) veloci o lente. Ciò include l'ignoranza su:

  • strategie di minimizzazione degli I / O fisici, dato che il collo di bottiglia della maggior parte delle query è I / O non CPU
  • perfetto impatto di diversi tipi di accesso all'archiviazione fisica (ad es. molti I / O sequenziali saranno più veloci di molti piccoli I / O casuali, anche se meno se l'archiviazione fisica è un SSD!)
  • come ottimizzare manualmente una query se il DBMS produce un piano di query scadente
  • come diagnosticare scarse prestazioni del database, come "eseguire il debug" di una query lenta e come leggere un piano di query (o EXPLAIN, a seconda del DBMS scelto)
  • strategie di blocco per ottimizzare il throughput ed evitare deadlock nelle applicazioni multiutente
  • importanza del batch e altri trucchi per gestire l'elaborazione dei set di dati
  • progettazione di tabelle e indici per bilanciare al meglio lo spazio e le prestazioni (ad es. coprire gli indici, mantenere gli indici piccoli ove possibile, ridurre i tipi di dati alla dimensione minima necessaria, ecc.)

3

Utilizzo di SQL come pacchetto ISAM (metodo di accesso sequenziale indicizzato) glorificato. In particolare, annidando i cursori invece di combinare le istruzioni SQL in una singola istruzione, sebbene più ampia. Questo vale anche come "abuso dell'ottimizzatore" poiché in realtà non c'è molto che l'ottimizzatore possa fare. Questo può essere combinato con dichiarazioni non preparate per la massima inefficienza:

DECLARE c1 CURSOR FOR SELECT Col1, Col2, Col3 FROM Table1

FOREACH c1 INTO a.col1, a.col2, a.col3
    DECLARE c2 CURSOR FOR
        SELECT Item1, Item2, Item3
            FROM Table2
            WHERE Table2.Item1 = a.col2
    FOREACH c2 INTO b.item1, b.item2, b.item3
        ...process data from records a and b...
    END FOREACH
END FOREACH

La soluzione corretta (quasi sempre) è combinare le due istruzioni SELECT in una:

DECLARE c1 CURSOR FOR
    SELECT Col1, Col2, Col3, Item1, Item2, Item3
        FROM Table1, Table2
        WHERE Table2.Item1 = Table1.Col2
        -- ORDER BY Table1.Col1, Table2.Item1

FOREACH c1 INTO a.col1, a.col2, a.col3, b.item1, b.item2, b.item3
    ...process data from records a and b...
END FOREACH

L'unico vantaggio della versione a doppio loop è che puoi facilmente individuare le interruzioni tra i valori in Tabella1 perché il loop interno termina. Questo può essere un fattore nei rapporti di interruzione del controllo.

Inoltre, l'ordinamento nell'applicazione è di solito un no-no.


Lo stile, sebbene non questa sintassi, è particolarmente dilagante in PHP nella mia esperienza.
dkretz,

La sintassi è in realtà IBM Informix-4GL - ma è abbastanza chiaro da non aver bisogno di molto in termini di spiegazione (credo). E lo stile dilaga in molti programmi SQL, indipendentemente dal linguaggio di programmazione.
Jonathan Leffler,

Tranne il fatto che stai usando un antipattern ben noto (join impliciti) per illustrare il tuo antipattern, il tipo di sconfigge il punto.
Johan,

E ovviamente l'uso dei cursori è un antipasto SQl. Praticamente tutti i cursori possono essere riscritti come operazioni basate su set. I pochi che non possono essere del tipo sono solo i DBA con anni di esperienza e che capiscono come dovrebbero essere scritti gli interni della base di dati. Nessuno sviluppatore di applicazioni dovrebbe mai aver bisogno di scrivere un cursore SQL.
HLGEM,

3

Utilizzo delle chiavi primarie come surrogato per gli indirizzi dei record e utilizzo delle chiavi esterne come surrogato per i puntatori incorporati nei record.

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.