Was sind die häufigsten SQL-Anti-Patterns? [geschlossen]


232

Alle von uns, die mit relationalen Datenbanken arbeiten, haben gelernt (oder lernen), dass SQL anders ist. Um die gewünschten Ergebnisse zu erzielen und dies effizient zu tun, ist ein langwieriger Prozess erforderlich, der teilweise durch das Erlernen unbekannter Paradigmen gekennzeichnet ist und herausfindet, dass einige unserer bekanntesten Programmiermuster hier nicht funktionieren. Was sind die häufigsten Antimuster, die Sie gesehen haben (oder die Sie selbst begangen haben)?


Dies ist eine Frage, die nicht den neueren Standards entspricht, welche Art von Frage für den Stapelüberlauf geeignet ist. Als es gefragt wurde, war dies möglicherweise nicht der Fall.
David Manheim

@casperOne Gibt es nicht eine Klausel mit "historischer Bedeutung", die diese Frage in Akzeptanz bringen würde?
Amy B

26
Ich finde es traurig, dass eine der nützlichsten Fragen auf der Wohole-Site als nicht konstruktiv geschlossen wird.
HLGEM

11
@HLGEM Ich stimme vollkommen zu. Diese Frage ist ein perfektes Beispiel für alles, was mit StackExchange
Kevin Morse

1
Das Thema ist absolut wichtig und relevant. Die Frage ist jedoch zu offen, weshalb die Antworten jeweils den persönlichen Anti-Pattern-Bugbear eines einzelnen Ingenieurs beschreiben.
Shane

Antworten:


156

Ich bin immer wieder enttäuscht von der Tendenz der meisten Programmierer, ihre UI-Logik in der Datenzugriffsschicht zu mischen:

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

Normalerweise tun dies Programmierer, weil sie beabsichtigen, ihr Dataset direkt an ein Raster zu binden, und es ist einfach praktisch, das SQL Server-Format serverseitig als das Format auf dem Client zu haben.

Abfragen wie die oben gezeigte sind extrem spröde, da sie die Datenschicht eng mit der UI-Schicht koppeln. Darüber hinaus verhindert diese Art der Programmierung gründlich, dass gespeicherte Prozeduren wiederverwendbar sind.


10
Ein gutes Poster-Child-Muster für maximale Kopplung über die größtmögliche Anzahl von Ebenen / Abstraktionsschichten.
Dkretz

3
Es ist möglicherweise nicht gut für die Entkopplung, obwohl aus Leistungsgründen, die ich so oft gemacht habe, iterative Änderungen, die von SQL Server vorgenommen werden, schneller sind als von Code in der mittleren Ebene. Ich verstehe Sie nicht als Wiederverwendbarkeitspunkt - nichts hindert Sie daran, den SP auszuführen und die Spalten umzubenennen, wenn Sie dies wünschen.
Joe Pineda

54
Mein Favorit ist, wenn Leute HTML UND Javascript einbetten, zB SELECT '<a href=... onclick="">' + name '</a>'
Matt Rogish

15
Mit solchen Abfragen können Sie das Raster in einer Website mit einer einfachen alter-Anweisung bearbeiten. Oder ändern Sie den Inhalt eines Exports oder formatieren Sie ein Datum in einem Bericht neu. Das macht Kunden glücklich und spart mir Zeit. Also danke, aber nein danke, ich bleibe bei solchen Fragen.
Andomar

4
@ Matt Rogish - Jesus, macht das eigentlich jemand?
Axarydax

118

Hier sind meine Top 3.

Nummer 1. Keine Angabe einer Feldliste. (Bearbeiten: Um Verwirrung zu vermeiden: Dies ist eine Produktionscode-Regel. Sie gilt nicht für einmalige Analyseskripte - es sei denn, ich bin der Autor.)

SELECT *
Insert Into blah SELECT *

sollte sein

SELECT fieldlist
Insert Into blah (fieldlist) SELECT fieldlist

Nummer 2. Verwenden eines Cursors und einer while-Schleife, wenn eine while-Schleife mit einer Schleifenvariablen ausreicht.

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

Nummer 3. DateLogic durch Zeichenfolgentypen.

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

Sollte sein

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

Ich habe kürzlich einen Anstieg von "Eine Abfrage ist besser als zwei, oder?" Gesehen.

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

Diese Abfrage erfordert zwei oder drei verschiedene Ausführungspläne, abhängig von den Werten der Parameter. Für diesen SQL-Text wird nur ein Ausführungsplan generiert und im Cache gespeichert. Dieser Plan wird unabhängig vom Wert der Parameter verwendet. Dies führt zu einer zeitweise schlechten Leistung. Es ist viel besser, zwei Abfragen zu schreiben (eine Abfrage pro beabsichtigtem Ausführungsplan).


7
hmmm, ich gebe dir allein für die Punkte 2 und 3 eine +1, aber die Entwickler spielen Regel 1 über. Manchmal hat sie ihren Platz.
Annakata

1
Was ist die Begründung für # 1?
Jalf

29
Wenn Sie select * verwenden, erhalten Sie alles, was in der Tabelle enthalten ist. Diese Spalten können Namen und Reihenfolge ändern. Client-Code hängt häufig von Namen und Reihenfolge ab. Alle 6 Monate werde ich gefragt, wie die Spaltenreihenfolge beim Ändern einer Tabelle beibehalten werden soll. Wenn die Regel befolgt würde, wäre das egal.
Amy B

Ich habe manchmal # 2 verwendet, andere habe ich den Cursor-Weg gegangen (obwohl ich dann zuerst die Ergebnisse der Abfrage in einer Tabelle var speichere, öffne den Cursor darauf). Ich habe mich immer gefragt, ob jemand einen Leistungstest für beide durchgeführt hat.
Joe Pineda

4
... aber natürlich sollten Cursor fast immer das letzte Mittel sein, nachdem nicht herausgefunden wurde, wie die Arbeit mit satzbasiertem SQL ausgeführt werden soll. Ich habe einmal ungefähr 45 Minuten damit verbracht, einen schrecklichen, gigantischen PL / SQL-Cursor in einer gespeicherten Prozedur sorgfältig zu zerlegen (zeichnete Diagramme des faulen Dings), die eine große temporäre Tabelle füllte und dann den Inhalt der temporären Tabelle zurück zum Aufrufer auswählte, um a zu rendern Bericht. Die Ausführung auf umfangreicher Hardware dauerte 8,5 Minuten. Nachdem ich das Ganze grafisch dargestellt hatte, konnte ich es durch eine einzelne Abfrage ersetzen, die in weniger als 2 Sekunden dieselben Ergebnisse lieferte. Cursor, Mann ...
Craig

71
  • Vom Menschen lesbare Passwortfelder , z. Selbsterklärend.

  • Verwenden von LIKE für indizierte Spalten, und ich bin fast versucht, nur LIKE im Allgemeinen zu sagen.

  • Recycling von SQL-generierten PK-Werten.

  • Überraschung, dass noch niemand den Gott-Tisch erwähnt hat. Nichts sagt "organisch" wie 100 Spalten mit Bitflags, großen Strings und ganzen Zahlen.

  • Dann gibt es das Muster "Ich vermisse INI-Dateien" : Speichern von CSVs, durch Pipe getrennten Zeichenfolgen oder anderen analysierten erforderlichen Daten in großen Textfeldern.

  • Und für MS SQL Server die Verwendung von Cursorn überhaupt . Es gibt eine bessere Möglichkeit, eine bestimmte Cursoraufgabe auszuführen.

Bearbeitet, weil es so viele gibt!


19
falsch über Cursor, ich würde zögern zu sagen, dass eine bestimmte Sache 100% richtig oder 100% falsch ist
Shawn

4
Bisher verwendet jedes Cursor-Verteidigungsbeispiel, das ich gesehen habe, das falsche Werkzeug für den Job. Wenn Sie jedoch nur SQL kennen, verwenden Sie es entweder unangemessen oder Sie lernen, andere Arten von Software zu schreiben.
dkretz

3
@tuinstoel: Wie kann LIKE '% blah%' einen Index verwenden? Die Indizierung hängt von der Reihenfolge ab. In diesem Beispiel wird eine zufällige Mittelposition einer Zeichenfolge durchsucht. (Indexe sortieren nach dem 1. Zeichen 1., und so ergibt ein Blick auf die mittleren 4 Zeichen eine praktisch zufällige Reihenfolge ...)
MatBailie

12
Auf den meisten Datenbankservern (zumindest den von mir verwendeten) kann LIKE Indizes verwenden. Solange es sich um eine Präfixsuche handelt (LIKE 'xxx%') - das heißt, solange die Platzhalterzeichen dies nicht tun kommen zuerst in der Suchzeichenfolge. Ich denke, Sie sprechen hier vielleicht ein wenig über Kreuzzwecke.
Cowan

10
Es ist, als ob du nicht magst LIKE '%LIKE'.
Johan

62

Sie müssen nicht tief graben: Verwenden Sie keine vorbereiteten Anweisungen.


3
Jep. Nach meiner Erfahrung im gleichen Kontext genau verfolgt, mit "keine Fehler einfangen".
dkretz

1
@stesch: Dies ist nichts im Vergleich zur Verwendung von Ansichten und einem variablen Berichtsdatum. Ansichten sind ein Antimuster, wenn Sie ein variables Berichtsdatum haben (ich gehe davon aus, dass die meisten Anwendungen dies haben). Würde dies in einer separaten Antwort hinzufügen, aber es ist leider geschlossen.
Stefan Steiger

56

Verwenden bedeutungsloser Tabellen-Aliase:

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

Das Lesen einer großen SQL-Anweisung wird so viel schwieriger als nötig


49
Aliase? Hölle, ich habe
solche

10
knappe Aliase sind OKAY. Wenn Sie einen aussagekräftigen Namen wünschen, verwenden Sie überhaupt keinen Alias.
Joel Coehoorn

43
Er sagte nicht "knapp", er sagte "bedeutungslos". In meinem Buch wäre es nichts Falsches, e, d und j als Aliase in der Beispielabfrage zu verwenden.
Robert Rossney

11
Absolut, Robert - e, d und j würden mir gut tun.
Tony Andrews

8
Ich würde emp für Mitarbeiter, dep für Abteilung und Job für Job (oder vielleicht jb) verwenden :)
Andrei Rînea

53
var query = "select COUNT(*) from Users where UserName = '" 
            + tbUser.Text 
            + "' and Password = '" 
            + tbPassword.Text +"'";
  1. Benutzereingaben blind vertrauen
  2. Keine parametrisierten Abfragen verwenden
  3. Klartext-Passwörter

All dies kann sinnvollerweise mithilfe einer Datenbankabstraktion auf einer (beliebigen) Ebene behandelt werden.
dkretz

@doofledorfer: Stimmen Sie zu, eine mittlere Stufe wäre in einem solchen Fall definitiv besser und bietet das Zwischenspeichern von Ergebnissen als netten Nebeneffekt.
Joe Pineda

Tolles Beispiel. Wenn ein Entwickler weiß, wie er das durch eine gute Lösung ersetzen kann, ist er auf halbem Weg, ein anständiger SQL-Entwickler zu werden.
Steve McLeod

46

Meine Bugbears sind die 450-Spalten-Zugriffstabellen, die vom 8-jährigen Sohn des Hundefreundes des besten Freundes des Geschäftsführers zusammengestellt wurden, und die zwielichtige Nachschlagetabelle, die nur existiert, weil jemand nicht weiß, wie man eine Datenstruktur richtig normalisiert.

In der Regel sieht diese Nachschlagetabelle folgendermaßen aus:

ID INT,
Name NVARCHAR (132),
IntValue1 INT,
IntValue2 INT,
CharValue1 NVARCHAR (255),
CharValue2 NVARCHAR (255),
Datum1 DATETIME,
Datum2 DATETIME

Ich habe die Anzahl der Kunden verloren, die Systeme gesehen haben, die auf solchen Greueln beruhen.


1
Schlimmer noch, habe ich gelesen , dass die Version von Access in neueste , die automatisch tatsächlich unterstützt wird , die ich fürchte , ermutigen Wert2, Wert3 ... Spalte Fetischismus mehr dieser Value1,
Joe Pineda

Warten Sie - also ist der 8-jährige Sohn der Sohn des Hundefriseurs?
Barrypicker

28

Diejenigen, die ich am wenigsten mag, sind

  1. Verwenden von Leerzeichen beim Erstellen von Tabellen, Sprocs usw. Ich bin mit CamelCase oder under_scores und Singular oder Plural und Großbuchstaben oder Kleinbuchstaben einverstanden, muss mich aber auf eine Tabelle oder Spalte [mit Leerzeichen] beziehen, insbesondere wenn [sie einen merkwürdigen Abstand hat] (ja, Ich bin darauf gestoßen) irritiert mich wirklich.

  2. Denormalisierte Daten. Eine Tabelle muss nicht perfekt normalisiert sein, aber wenn ich auf eine Tabelle mit Mitarbeitern stoße, die Informationen über ihre aktuelle Bewertungspunktzahl oder ihre primären Daten enthält, muss ich wahrscheinlich irgendwann eine separate Tabelle erstellen Versuchen Sie dann, sie synchron zu halten. Ich werde zuerst die Daten normalisieren und dann, wenn ich einen Ort sehe, an dem die Denormalisierung hilft, werde ich darüber nachdenken.

  3. Überbeanspruchung von Ansichten oder Cursorn. Ansichten haben einen Zweck, aber wenn jede Tabelle in eine Ansicht eingeschlossen ist, ist es zu viel. Ich musste einige Male Cursor verwenden, aber im Allgemeinen können Sie dafür andere Mechanismen verwenden.

  4. Zugriff. Kann ein Programm ein Anti-Pattern sein? Wir haben SQL Server in meiner Arbeit, aber eine Reihe von Personen verwenden den Zugriff aufgrund seiner Verfügbarkeit, "Benutzerfreundlichkeit" und "Freundlichkeit" für nicht technische Benutzer. Es gibt hier zu viel, um darauf einzugehen, aber wenn Sie in einer ähnlichen Umgebung waren, wissen Sie.


2
# 4 - es ist ein anderer Thread nur für <a href=' stackoverflow.com/questions/327199/...> :).
dkretz

4
Der Zugriff ist KEIN DBMS. Es ist eine RAD-Umgebung mit einem sehr einfachen Datenbankmanager. SQL Server, Oracle et al. wird es niemals ersetzen, es sei denn, Sie fügen eine VB-ähnliche Sprache und eine Crystal Reports-ähnliche Einrichtung hinzu.
Joe Pineda

26

Verwenden Sie SP als Präfix für den Namen der Speicherprozedur, da diese zuerst am Speicherort der Systemprozeduren und nicht an den benutzerdefinierten suchen.


1
Kann auch auf die Verwendung eines anderen gemeinsamen Präfixes für alle gespeicherten Prozeduren erweitert werden, wodurch das Durchsuchen einer sortierten Liste erschwert wird.
Dkretz

7
+1 für doofledorfer Kommentar !! Ich habe das oft gesehen, finde das idiotisch und mache die Suche nach einem bestimmten SP in der Tat sehr schwierig !!! Auch erweitert auf "vw_" für Ansichten, "tbl_" für Tabellen und dergleichen, wie ich sie hasse!
Joe Pineda

1
Die Präfixe können nützlich sein, wenn Sie die Objekte in Dateien skripten (z. B. für die Quellcodeverwaltung, Bereitstellung oder Migration)
Rick

1
Warum um alles in der Welt wäre es nützlich, jeder einzelnen gespeicherten Prozedur sp oder usp voranzustellen? Es macht es nur schwieriger, die Liste nach der gewünschten zu durchsuchen.
Ryan Lundy

25

Überbeanspruchung temporärer Tabellen und Cursor.


2
Guter Beweis dafür, dass "ich nur Verfahrenssprachen kenne".
dkretz

2
Übermäßiger Gebrauch von irgendetwas ist per Definition unerwünscht. Ein spezielles Beispiel dafür, wo die Verwendung von temporären Tabellen / Cursorn nicht erforderlich wäre, wäre hilfreich.
Jace Rhea

6
Meistens sehe ich temporäre Tabellen, die nicht ausreichend genutzt werden. Mit SQL Server erzielen Sie häufig Leistungssteigerungen, wenn Sie Dinge mit einer Reihe von temporären Tabellen anstelle einer monolithischen Abfrage ausführen.
Cervo

24

Zum Speichern von Zeitwerten sollte nur die UTC-Zeitzone verwendet werden. Ortszeit sollte nicht verwendet werden.


3
Ich habe immer noch keine gute einfache Lösung für die Umrechnung von UTC auf Ortszeit für Daten in der Vergangenheit gefunden, wenn die Sommerzeit berücksichtigt werden muss, mit unterschiedlichen Änderungsdaten über Jahre und Länder hinweg sowie allen Ausnahmen innerhalb von Ländern. UTC erspart Ihnen also keine Komplexität bei der Konvertierung. Es ist jedoch wichtig, die Zeitzone jeder gespeicherten Uhrzeit zu kennen.
ckarras

1
@CsongorHalmai Viele Orte üben die Sommerzeit, sodass Zeitwerte innerhalb einer Stunde nach der Zeitverschiebung nicht eindeutig sein können.
Frank Schwieterman

Das ist sicherlich richtig für die Gegenwart und die Vergangenheit, aber für die Zukunft, insbesondere für die ziemlich ferne Zukunft, sind explizite Zeitzonen oft eine Notwendigkeit. Wenn Sie eine 30-Jahres-Option haben, die gerade geschrieben wurde und in 2049-09-27T17: 00: 00 New Yorker Zeit abläuft, können Sie nicht einfach blind davon ausgehen, dass dies 21: 00: 00Z sein wird. Der US-Kongress könnte die Sommerzeitregeln durchaus ändern. Sie müssen die Ortszeit und die wahre Zeitzone (America / New_York) getrennt halten.
John Cowan

23

Verwenden von @@ IDENTITY anstelle von SCOPE_IDENTITY ()

Zitiert aus dieser Antwort :

  • @@ IDENTITY gibt den letzten Identitätswert zurück, der für eine Tabelle in der aktuellen Sitzung in allen Bereichen generiert wurde. Sie müssen hier vorsichtig sein, da es sich um Bereiche handelt. Sie könnten anstelle Ihrer aktuellen Anweisung einen Wert von einem Trigger erhalten.
  • SCOPE_IDENTITY gibt den letzten Identitätswert zurück, der für eine Tabelle in der aktuellen Sitzung und im aktuellen Bereich generiert wurde. Im Allgemeinen, was Sie verwenden möchten.
  • IDENT_CURRENT gibt den letzten Identitätswert zurück, der für eine bestimmte Tabelle in einer Sitzung und einem Bereich generiert wurde. Auf diese Weise können Sie angeben, aus welcher Tabelle der Wert stammen soll, falls die beiden oben genannten nicht ganz Ihren Anforderungen entsprechen (sehr selten). Sie können dies verwenden, wenn Sie den aktuellen IDENTITY-Wert für eine Tabelle abrufen möchten, in die Sie keinen Datensatz eingefügt haben.

+1 sehr wahr, könnte einen Fehler verursachen, der schwer
auszumerzen

23

Wiederverwendung eines "toten" Felds für etwas, für das es nicht vorgesehen war (z. B. Speichern von Benutzerdaten in einem "Fax" -Feld) - sehr verlockend als schnelle Lösung!


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

und unter der Annahme, dass das Ergebnis nach some_column sortiert wird. Ich habe dies ein bisschen bei Sybase gesehen, wo die Annahme (vorerst) gilt.


1
Upvote für EVER Annahme der Sortierreihenfolge, nur weil es so im Abfrage-Tool angezeigt wurde, dass einmal
Joel Coehoorn

3
Ich habe sogar mehr als einmal gesehen, dass dies als Fehler gemeldet wurde.
dkretz

6
In MySQL ist das Sortieren dokumentiert. < dev.mysql.com/doc/refman/5.0/en/select.html >. Also beschuldige MySQL (wieder).
Derobert

1
In Oracle stimmten die unsortierten Ergebnisse (fast) immer mit der Gruppierung überein - bis Version 10G. Viel Nacharbeit für die Entwickler, die ORDER BY weggelassen haben!
Tony Andrews

1
Ich war sogar in einer Schulungsklasse, in der dies als Tatsache für SQL Server angegeben wurde. Ich musste sehr laut protestieren. Wenn Sie nur 20 Zeichen speichern möchten, verlassen Sie sich auf obskures oder nicht dokumentiertes Verhalten.
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

Oder alles in eine Zeile packen.


Verwendete die Abfrage eines vorherigen Kommentars, nur weil dies die erste verfügbare SQL-Anweisung war.
Jasper Bekkers

17
  • Die FROM TableA, TableB WHERESyntax für JOINS anstattFROM TableA INNER JOIN TableB ON

  • Die Annahme, dass eine Abfrage zurückgegeben wird, wird auf eine bestimmte Weise sortiert, ohne dass eine ORDER BY-Klausel eingefügt wird, nur weil dies beim Testen im Abfragetool so angezeigt wurde.


5
Meine Oracle-Datenbankadministratoren beschweren sich immer, dass ich "ANSI-Joins" verwende, dh was Sie als den richtigen Weg präsentieren. Aber ich mache es weiter und ich vermute, dass sie tief im Inneren wissen, dass es besser ist.
Steve McLeod

1
Ich vermute, dass Oracle wünscht, Standard-SQL würde verschwinden. :-) Außerdem können Sie in MySQL 5 keine impliziten und expliziten JOINS (auch bekannt als ANSI JOINs) mischen - es funktioniert nicht. Welches ist ein weiteres Argument für explizite JIONs.
staticsan

3
Ich würde sagen, dass sogar A INNER JOIN B ON ein Anti-Pattern ist. Ich bevorzuge A INNER JOIN B USING.
John Nilsson

Oracle unterstützt jetzt die ANSI-Syntax, aber in der Vergangenheit hatten sie diese wirklich seltsame Syntax für äußere Verknüpfungen, und es gibt immer noch zu viele Leute, die sie verwenden.
Cervo


14

In den ersten sechs Monaten ihrer Karriere SQL lernen und in den nächsten 10 Jahren nichts anderes lernen. Insbesondere nicht lernen oder Fensterfunktionen / analytische SQL-Funktionen effektiv nutzen. Insbesondere die Verwendung von over () und Partitionierung durch.

Fensterfunktionen führen wie Aggregatfunktionen eine Aggregation für eine definierte Menge (eine Gruppe) von Zeilen durch. Anstatt jedoch einen Wert pro Gruppe zurückzugeben, können Fensterfunktionen mehrere Werte für jede Gruppe zurückgeben.

In O'Reilly SQL Cookbook Anhang A finden Sie eine schöne Übersicht über die Fensterfunktionen.


12

Ich muss hier meinen eigenen aktuellen Favoriten eintragen, um die Liste zu vervollständigen. Mein Lieblings-Antimuster testet Ihre Fragen nicht .

Dies gilt, wenn:

  1. Ihre Anfrage umfasst mehr als eine Tabelle.
  2. Sie denken, Sie haben ein optimales Design für eine Abfrage, aber Sie müssen Ihre Annahmen nicht testen.
  3. Sie akzeptieren die erste Abfrage, die funktioniert, ohne eine Ahnung zu haben, ob sie überhaupt optimiert ist.

Und Tests, die gegen atypische oder unzureichende Daten durchgeführt werden, zählen nicht. Wenn es sich um eine gespeicherte Prozedur handelt, fügen Sie die Testanweisung in einen Kommentar ein und speichern Sie sie mit den Ergebnissen. Andernfalls fügen Sie es in einen Kommentar im Code mit den Ergebnissen ein.


Eine sehr nützliche Technik für den minimalen T-SQL-Test: Erstellen Sie in der .SQL-Datei, in der Sie SP, UDF usw. definieren, unmittelbar danach einen Blocktest wie IF 1 = 2 BEGIN (Beispielfälle für Ihren Code mit erwarteten Ergebnissen) als Kommentare) ENDE
Joe Pineda

SQL Server analysiert den Code innerhalb des Testblocks, obwohl er nie ausgeführt wird. Wenn Ihr Objekt also geändert wird und mehr Parameter oder einen anderen Typ usw. erhält oder ein Objekt, von dem es abhängt, geändert wird, erhalten Sie eine Fehlermeldung, indem Sie einfach nach einem Ausführungsplan fragen!
Joe Pineda

Es ist nicht immer möglich, mit realen Daten zu testen. Oft ist der Entwickler-Server / "Test" -Server unterbezahlt und erhält einen Bruchteil des Live-Servers. Im Allgemeinen werden Tests gegen den Live-Server verpönt. Einige Orte sind besser und verfügen über einen Test- oder Staging-Server mit Live-Daten.
Cervo

11

Vorübergehender Tischmissbrauch.

Speziell so etwas:

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)

Erstellen Sie keine temporäre Tabelle aus einer Abfrage, nur um die nicht benötigten Zeilen zu löschen.

Und ja, ich habe Codeseiten in dieser Form in Produktions-DBs gesehen.


1
+1, ich stimme zu. Obwohl ich mindestens ein oder zwei Fälle gefunden habe, in denen diese Technik die Leistung verbessert hat - die damit verbundenen Abfragen waren gelinde gesagt komplex.
Am

1
Richtig - sie haben einen Platz, nur nicht in jeder Anfrage :)
geofftnz

1
Manchmal muss man das tun, wenn die Bedingungen sehr kompliziert sind. Es stimmt, es kann bis zum Äußersten missbraucht werden. Aber oft ist ein einfaches Löschen viel einfacher als die Logik, um den Fall in der anfänglichen Abfrage zu erhalten. Manchmal wird die anfängliche Abfrage auch verlangsamt, wenn die Klausel nicht sarkierbar ist. Es ist jedoch effizienter, dies nur auf dem kleineren temporären Tisch zu tun. Und manchmal fügen Sie immer wieder Fälle hinzu, die Geschäftsleute nachträglich hinzufügen.
Cervo

9

Gegenteilige Ansicht: Überbesessenheit mit Normalisierung.

Die meisten SQL / RBDB-Systeme bieten eine Reihe von Funktionen (Transaktionen, Replikation), die selbst bei nicht normalisierten Daten sehr nützlich sind. Der Speicherplatz ist billig und manchmal kann es einfacher (einfacherer Code, schnellere Entwicklungszeit) sein, abgerufene Daten zu bearbeiten / filtern / durchsuchen, als ein 1NF-Schema zu schreiben und alle darin enthaltenen Probleme zu lösen (komplexe Verknüpfungen, unangenehme Unterauswahlen) , etc).

Ich habe festgestellt, dass die übernormalisierten Systeme häufig vorzeitig optimiert werden, insbesondere in frühen Entwicklungsphasen.

(Weitere Gedanken dazu ... http://writeonly.wordpress.com/2008/12/05/simple-object-db-using-json-and-python-sqlite/ )


22
Ich denke, Nicht-Normalisierung ist oft eine vorzeitige Optimierung.
Tuinstoel

Manchmal ist es das, manchmal nicht. Glücklicherweise ist es oft einfach zu testen und verschiedene Optionen funktionieren mit unterschiedlichen Datenbankanforderungen.
Gregg Lind

17
Die Normalisierung dient nicht nur der Speicherplatzersparnis. Es dient auch dazu, eine maßgebliche Quelle für die Daten zu erstellen. Wenn die Daten nur an einem Ort gespeichert werden, ist Konsistenz kein Nebenprodukt sorgfältiger Codierung, sondern ein Nebenprodukt des Designs.
Grant Johnson

Das Speichern zusammengesetzter Daten im JSON-Format ist eine Sache: Es wird immer mehr unterstützt und es ist ein bewusster Kompromiss. Die Verwendung von durch Kommas getrennten (oder was auch immer) Werten beim Versuch, einen Join zu speichern, ist penny-weise und Pfund-dumm.
John Cowan

noSQL-Lösungen weisen einen Leistungsvorteil auf Kosten doppelter Daten auf, da die Suche nach mehreren Tabellen entfällt. Setzt die ganze Normalisierungssache auf den Kopf. In einigen Beispielen werden die Daten an mehreren Stellen gesammelt, um sicherzustellen, dass ein Prozess die schnellstmögliche Antwortzeit hat. Natürlich kommen Fragen zu maßgeblichen Quellen ins Spiel.
Barrypicker

9

Ich habe gerade diese zusammengestellt, basierend auf einigen der SQL-Antworten hier auf SO.

Es ist ein ernstes Gegenmuster zu glauben, dass Trigger für Datenbanken gelten, wie Event-Handler für OOP. Es gibt diese Wahrnehmung, dass nur jede alte Logik in Trigger gesetzt werden kann, um ausgelöst zu werden, wenn eine Transaktion (ein Ereignis) auf einem Tisch stattfindet.

Nicht wahr. Einer der großen Unterschiede besteht darin, dass Trigger synchron sind - mit aller Macht, weil sie bei einer festgelegten Operation und nicht bei einer Zeilenoperation synchron sind. Auf der OOP-Seite ist genau das Gegenteil der Fall - Ereignisse sind eine effiziente Möglichkeit, asynchrone Transaktionen zu implementieren.


8

Gespeicherte Prozeduren oder Funktionen ohne Kommentare ...


Und Ansichten;) Funktionen wahr, außer Funktionen mit Tabellenwerten (= Ansichten mit Parametern).
Stefan Steiger

7

1) Ich weiß nicht, dass es sich um ein "offizielles" Anti-Pattern handelt, aber ich mag es nicht und versuche, String-Literale als magische Werte in einer Datenbankspalte zu vermeiden.

Ein Beispiel aus MediaWikis Tabelle 'image':

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",

(Ich bemerke nur ein anderes Gehäuse, eine andere Sache, die man vermeiden sollte)

Ich entwerfe solche Fälle wie int-Lookups in Tabellen ImageMediaType und ImageMajorMime mit int-Primärschlüsseln.

2) Datums- / Zeichenfolgenkonvertierung, die auf bestimmten NLS-Einstellungen basiert

CONVERT(NVARCHAR, GETDATE())

ohne Formatkennung


Und auch keine syntaktische Einrückung. Argghh.
dkretz

2
Warum ist das so schlimm? Wenn Sie versuchen, eine Reihe von Werten auszudrücken, funktioniert dies sicherlich genauso gut wie eine Nachschlagetabelle und passt besser zu Code, der sie aufruft. Ich habe eher eine Aufzählung in meinem App-Code, die einer Aufzählungsbeschränkung in meiner Datenbank zugeordnet ist, als eine Aufzählung in meinem App-Code, die bestimmten Zeilen einer Nachschlagetabelle zugeordnet ist. Es fühlt sich einfach sauberer an.
Jack Ryan

@JackRyan: Das ist schlecht, denn wenn Sie die Aufzählungsliste später ändern, müssen Sie daran denken, sie jetzt an zwei Stellen zu ändern. Es verletzt DRY . Die Datenbank sollte die einzige Quelle der Wahrheit sein.
Gerrat

7

Identische Unterabfragen in einer Abfrage.


10
Leider manchmal kann man einfach nicht vermeiden , dass - in SQL 2000 keine „MIT“ Stichwort war, und benutzerdefinierte Funktionen mit gemeinsamen Subqueries irgendwann führen zu Leistungseinbußen verkapseln, Schuld MS auf , dass ...
Joe Pineda

Hoffentlich werden sie es eines Tages hinzufügen.
EvilTeach

In SQL 2000 können Sie Tabellenvariablen verwenden.
rekursiv

@recursive: Sie können keine Indizes für eine Tabellenvariable haben, wodurch diese häufig langsamer als eine Unterabfrage ist. Sie können jedoch eine temporäre Tabelle mit benutzerdefinierten Indizes verwenden.
Rick

Cool, arbeite seit Jahren mit SQL und wusste nicht einmal, dass Common Table Expressions existieren (obwohl ich sie gebraucht hätte). Jetzt mache ich! Vielen Dank!
Sleske

7
  • Die geänderte Ansicht - Eine Ansicht, die zu oft und ohne Vorankündigung oder Grund geändert wird. Die Änderung wird entweder zum unangemessensten Zeitpunkt bemerkt oder ist schlimmer noch falsch und wird nie bemerkt. Möglicherweise wird Ihre Anwendung unterbrochen, weil sich jemand einen besseren Namen für diese Spalte ausgedacht hat. In der Regel sollten Ansichten den Nutzen von Basistabellen erweitern und gleichzeitig einen Vertrag mit Verbrauchern aufrechterhalten. Beheben Sie Probleme, fügen Sie jedoch keine Funktionen hinzu oder ändern Sie das Verhalten nicht. Erstellen Sie dazu eine neue Ansicht. Um dies zu verringern, teilen Sie keine Ansichten mit anderen Projekten und verwenden Sie CTEs, wenn die Plattformen dies zulassen. Wenn Ihr Shop über einen DBA verfügt, können Sie die Ansichten wahrscheinlich nicht ändern, aber alle Ihre Ansichten sind veraltet und in diesem Fall unbrauchbar.

  • The! Paramed - Kann eine Abfrage mehr als einen Zweck haben? Wahrscheinlich, aber die nächste Person, die es liest, wird es erst in tiefer Meditation wissen. Selbst wenn Sie sie gerade nicht brauchen, werden Sie es wahrscheinlich tun, auch wenn es "nur" zum Debuggen ist. Das Hinzufügen von Parametern verkürzt die Wartungszeit und hält die Dinge trocken. Wenn Sie eine where-Klausel haben, sollten Sie Parameter haben.

  • Der Fall für keinen Fall -

    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  

Liebte diesen dritten. Ich benutze es bereits lokal ...
Alphadogg

Danke für die Requisiten. :)
Jason Saldo

5

Die beiden, die ich am meisten finde und die erhebliche Kosten in Bezug auf die Leistung verursachen können, sind:

  • Verwenden von Cursorn anstelle eines satzbasierten Ausdrucks. Ich denke, dies tritt häufig auf, wenn der Programmierer prozedural denkt.

  • Verwenden von korrelierten Unterabfragen, wenn ein Join zu einer abgeleiteten Tabelle die Aufgabe übernehmen kann.


Ich stimme zu, wenn Sie meinen, was ich denke, dass Sie meinen; obwohl eine korrelierte Unterabfrage eine Art abgeleiteter Tabelle IIRC ist.
dkretz

1
Eine abgeleitete Tabelle ist eine festgelegte Operation, während für jede Zeile in der äußeren Abfrage eine korrelierte Unterabfrage ausgeführt wird, was sie weniger effizient macht (9 von 10)
Mitch Wheat

Vor ein paar Jahren stellte ich zu meiner Überraschung fest, dass SQL S. irgendwie für die Verarbeitung korrelierter Abfragen optimiert ist: Für einfache Abfragen erhalten Sie den gleichen Ausführungsplan wie für eine logisch äquivalente Abfrage mit einem JOIN! Außerdem werden korrelierte Abfragen, die Oracle in die Knie zwingen, unter SQL S nur langsam ausgeführt!
Joe Pineda

Deshalb teste ich es immer in beide Richtungen. Und ich <i> versuche </> es normalerweise in beide Richtungen. In der Praxis habe ich für SQL Server normalerweise festgestellt, dass das korrelierte Quadrat nicht langsamer ist.
dkretz

3
BITTE verstehen, dass eine korrelierte Unterabfrage und ein Join (in den meisten Fällen) IDENTISCH sind. Es sind nicht einmal verschiedene Dinge, die aufeinander optimiert sind, sondern nur verschiedene Textdarstellungen derselben Operation.
Erikkallen

5

Das Einfügen von Inhalten in temporäre Tabellen, insbesondere von Personen, die von SQL Server zu Oracle wechseln, hat die Angewohnheit, temporäre Tabellen zu häufig zu verwenden. Verwenden Sie einfach verschachtelte select-Anweisungen.


5

Entwickler, die Abfragen schreiben, ohne eine gute Vorstellung davon zu haben, was SQL-Anwendungen (sowohl einzelne Abfragen als auch Mehrbenutzersysteme) schnell oder langsam macht. Dies beinhaltet Unwissenheit über:

  • Strategien zur Minimierung der physischen E / A, da der Engpass bei den meisten Abfragen die E / A und nicht die CPU ist
  • Perfekte Auswirkung verschiedener Arten des physischen Speicherzugriffs (z. B. sind viele sequentielle E / A schneller als viele kleine zufällige E / A, wenn auch weniger, wenn es sich bei Ihrem physischen Speicher um eine SSD handelt!)
  • So optimieren Sie eine Abfrage von Hand, wenn das DBMS einen schlechten Abfrageplan erstellt
  • wie man eine schlechte Datenbankleistung diagnostiziert, wie man eine langsame Abfrage "debuggt" und wie man einen Abfrageplan liest (oder EXPLAIN, abhängig von dem DBMS Ihrer Wahl)
  • Sperrstrategien zur Optimierung des Durchsatzes und zur Vermeidung von Deadlocks in Mehrbenutzeranwendungen
  • Wichtigkeit von Batching und anderen Tricks für die Verarbeitung von Datensätzen
  • Tabellen- und Indexdesign, um Speicherplatz und Leistung optimal in Einklang zu bringen (z. B. Indizes abdecken, Indizes möglichst klein halten, Datentypen auf die erforderliche Mindestgröße reduzieren usw.)

3

Verwenden von SQL als verherrlichtes ISAM-Paket (Indexed Sequential Access Method). Insbesondere das Verschachteln von Cursorn anstelle der Kombination von SQL-Anweisungen zu einer einzigen, wenn auch größeren Anweisung. Dies gilt auch als "Missbrauch des Optimierers", da der Optimierer tatsächlich nicht viel tun kann. Dies kann mit nicht vorbereiteten Aussagen kombiniert werden, um maximale Ineffizienz zu erzielen:

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

Die richtige Lösung besteht (fast immer) darin, die beiden SELECT-Anweisungen zu einer zu kombinieren:

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

Der einzige Vorteil der Doppelschleifenversion besteht darin, dass Sie die Unterbrechungen zwischen den Werten in Tabelle 1 leicht erkennen können, da die innere Schleife endet. Dies kann ein Faktor in Kontrollunterbrechungsberichten sein.

Außerdem ist das Sortieren in der Anwendung normalerweise ein Nein-Nein.


Der Stil, obwohl nicht diese Syntax, ist meiner Erfahrung nach in PHP besonders verbreitet.
dkretz

Die Syntax ist eigentlich IBM Informix-4GL - aber es ist klar genug, um nicht viel Erklärung zu benötigen (glaube ich). Und der Stil ist in vielen SQL-Programmen weit verbreitet - unabhängig von der Programmiersprache.
Jonathan Leffler

Abgesehen von der Tatsache, dass Sie ein bekanntes Antimuster (implizite Verknüpfungen) verwenden, um Ihr Antimuster zu veranschaulichen, ist dies eine Art Niederlage.
Johan

Und natürlich ist die Verwendung von Cursorn überhaupt ein SQl-Antimuster. Praktisch alle Cursor können als satzbasierte Operationen umgeschrieben werden. Die wenigen, die dies nicht können, sind die DBAs mit langjähriger Erfahrung, die verstehen, wie die Interna der Datenbank funktionieren sollten. Kein Anwendungsentwickler sollte jemals einen SQL-Cursor schreiben müssen.
HLGEM

3

Verwenden von Primärschlüsseln als Ersatz für Datensatzadressen und Verwenden von Fremdschlüsseln als Ersatz für in Datensätze eingebettete Zeiger.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.