Frage

Alle von uns, die mit relationalen Datenbanken arbeiten gelernt haben (oder lernen), dass SQL unterscheidet. Hervorrufen der gewünschten Ergebnisse, und so effizient tun, beinhaltet einen langwierigen Prozess zum Teil durch das Lernen nicht vertraut Paradigmen gekennzeichnet, und herauszufinden, dass einige unserer bekanntesten Programmiermuster hier nicht funktionieren. Was sind die häufigsten Antipatterns Sie gesehen haben (oder sich verpflichtet)?

War es hilfreich?

Lösung

ich konsequent von den meisten Programmierern Tendenz enttäuscht bin 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 Programmierer tun dies, weil sie ihre Daten-Set direkt an ein Netz binden möchten, und es ist nur bequem SQL Server-Format Server-Seite als Format auf dem Client zu haben.

Abfragen wie die oben gezeigt sind extrem spröde, weil sie fest Paar der Datenschicht auf die UI-Ebene. Hinzu kommt, dass diese Art der Programmierung verhindert gründlich Verfahren davon, dass wieder verwendbare gespeichert.

Andere Tipps

Hier sind meine Top-3.

Nummer 1. Das Fehlen einer Feldliste angeben. (Edit: Verwirrung zu vermeiden:.. Das ist ein Produktionscode Regel nicht auf einmalige Analyse Skripte gilt - es sei denn, ich bin der Autor)

SELECT *
Insert Into blah SELECT *

sollte

SELECT fieldlist
Insert Into blah (fieldlist) SELECT fieldlist

Nummer 2. einen Cursor und while-Schleife verwenden, wenn eine while-Schleife mit einer Schleife Variable tun wird.

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 String-Typen.

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

sollte

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

Ich habe eine neue Spitze der gesehen „Eine Abfrage ist besser als zwei, amiright?“

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 auf den Werten der Parameter abhängig. Nur ein Ausführungsplan wird in die Cache für diesen SQL-Text erzeugt und steckte. Dieser Plan wird unabhängig vom Wert der Parameter verwendet werden. Dies führt zu einer intermittierenden schlechter Leistung. Es ist viel besser zwei Abfragen zu schreiben (eine Abfrage pro beabsichtigtem Ausführungsplan).

  • die Menschen lesbare Passwortfelder , egad. Selbsterklärend.

  • Mit LIKE gegen indiziert Spalten, und ich bin fast versucht zu nur sagen im Allgemeinen.

  • Recycling SQL-generierte PK Werte.

  • Überraschung niemand erwähnt die Gott-Tabelle noch. nichts sagt „Bio“ wie 100 Spalten von Bit Fahnen, große Strings und ganze Zahlen sind.

  • Dann gibt es die „Ich vermisse ini Dateien " Muster: Speichern CSVs, Rohr getrennte Zeichenfolge oder andere parsen erforderliche Daten in großen Textfeldern.

  • Und für MS SQL Server die Verwendung von Cursor bei allen . Es gibt eine bessere Art und Weise beliebige Cursor Aufgabe zu tun.

Edited, weil es so viele sind!

Sie müssen tief graben nicht für sie. Nicht vorbereitete Anweisungen mit

sinnlose Tabelle Aliase:

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

Macht eine große SQL-Anweisung zu lesen so viel schwieriger, als es sein muss

var query = "select COUNT(*) from Users where UserName = '" 
            + tbUser.Text 
            + "' and Password = '" 
            + tbPassword.Text +"'";
  1. blind zu vertrauen Benutzereingabe
  2. Nicht mit parametrisierte Abfragen
  3. Klartext-Passwörter

Meine bugbears sind die 450 Spalte Access-Tabellen, die von dem 8-jährigen Sohn des Geschäftsführers der besten Freunde hundegroomer und die zwielichtigen Lookup-Tabelle zusammengestellt wurden, die nur existiert, weil jemand nicht weiß, wie man eine Datenstruktur zu normalisieren richtig .

Normalerweise diese Lookup-Tabelle sieht wie folgt aus:

ID INT,
Name NVARCHAR(132),
IntValue1 INT,
IntValue2 INT,
CharValue1 NVARCHAR(255),
CharValue2 NVARCHAR(255),
Date1 DATETIME,
Date2 DATETIME

Ich habe Zählung der Anzahl der Kunden verloren die ich gesehen habe, die Systeme haben, die auf Abscheulichkeiten wie diese verlassen.

Die, die ich am wenigsten sind

  1. Leerzeichen verwenden, wenn Erstellen von Tabellen, sprocs etc. Ich bin fein mit Camelcase oder under_scores und Singular oder Plural und Groß- oder Klein aber mit einer Tabelle oder Spalte beziehen [mit Leerzeichen], vor allem, wenn [es ist seltsam Abstand] (ja, ich habe in diesem Lauf) ärgert mich wirklich.

  2. Denormalisierte Daten. Eine Tabelle muss nicht perfekt normalisiert werden, aber wenn ich in eine Tabelle der Mitarbeiter führe, die Informationen über ihre aktuelle Bewertungszahl oder ihre primär etwas hat, es sagt mir, dass ich wahrscheinlich eine separate Tabelle an einem gewissen Punkt machen müssen und dann versuchen, sie synchronisiert zu halten. Ich werde die Daten zuerst normalisieren und dann, wenn ich einen Ort sehen, wo Denormalisierung hilft, werde ich es in Betracht ziehen.

  3. Der übermäßige Einsatz von entweder Ansichten oder Cursor. Ansichten haben einen Zweck, aber wenn jede Tabelle in einer Ansicht gewickelt ist, es ist zu viel. Ich habe Cursor ein paar Mal zu verwenden, aber in der Regel können Sie andere Mechanismen für diese.

  4. Zugriff. Kann ein Programm ein Anti-Muster sein? Wir haben SQL Server bei meiner Arbeit, aber eine Reihe von Menschen den Zugang verwenden aufgrund seiner Verfügbarkeit der Zimmer ist „Benutzerfreundlichkeit“ und „Freundlichkeit“, um nicht-technische Anwender. Es ist zu viel, hier zu gehen, aber wenn Sie in einer ähnlichen Umgebung sind, wissen Sie.

Verwendung SP als Präfix des Ladens Prozedur Namen, weil es zuerst in der Systemprozeduren Standort suchen, anstatt die benutzerdefinierten diejenigen.

Die übermäßige Einsatz von temporären Tabellen und Cursor.

mit @@ IDENTITY statt SCOPE_IDENTITY ()

diese Antwort :

  • @@ IDENTITY den letzten Identitätswert für jede erzeugte zurück Tabelle in der aktuellen Sitzung, in allen Bereichen. Sie müssen hier vorsichtig sein, da sie über Bereiche ist. Sie könnten einen Wert von einem Trigger erhalten, statt der aktuellen Anweisung.
  • SCOPE_IDENTITY den letzten Identitätswert für jede Tabelle erzeugt kehrt in die aktuelle Sitzung und der aktuelle Bereich. Im Allgemeinen, was Sie verwenden möchten.
  • IDENT_CURRENT den letzten Identitätswert für eine bestimmte Tabelle erzeugt zurück in jeder Sitzung und jedem Umfang. Auf diese Weise können Sie angeben, welche Tabelle Sie den Wert aus, wollen, falls die beiden oben genannten sind nicht ganz das, was man braucht (sehr selten). Sie können dies verwenden, wenn Sie den aktuellen IDENTITY-Wert für einen Tisch zu bekommen, dass Sie nicht einen Datensatz in.
  • eingefügt haben

Für die Zeitwerte zu speichern, nur UTC-Zeitzone verwendet werden. Ortszeit sollte nicht verwendet werden.

Wieder mit einem ‚toten‘ Feld für etwas, das es nicht gedacht war (zum Beispiel Benutzerdaten in einem ‚Fax‘ Feld zu speichern) - sehr verlockend als eine schnelle Lösung, obwohl

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

und unter der Annahme, dass das Ergebnis von some_column sortiert werden. Ich habe das ein bisschen mit Sybase gesehen, wo die Annahme (vorerst) gilt.

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 pauken.

  • Die FROM TableA, TableB WHERE Syntax für Joins anstatt FROM TableA INNER JOIN TableB ON

  • zu machen Annahmen, die eine Abfrage ohne setzt eine ORDER BY-Klausel zurückgegeben wird, auf eine bestimmte Weise geordnet, nur weil das war so, wie es während des Tests in dem Abfrage-Tool auftauchte.

SQL in den ersten sechs Monaten ihrer Karriere Lernen und nie etwas anderes für die nächsten 10 Jahre zu lernen. Insbesondere nicht lernen oder effektiv Windowing / analytische SQL-Funktionen verwenden. Insbesondere die Verwendung von mehr als () und Partition durch.

  

Fensterfunktionen, wie aggregierte   Funktionen, eine Aggregation auf einem zuführen   definierten Satz (eine Gruppe) von Zeilen, aber   eher als einen Wert zurückkehrt pro   Gruppe, Fensterfunktionen können zurückkehren   mehrere Werte für jede Gruppe.

Siehe O'Reilly SQL-Kochbuch Anhang A für einen schönen Überblick Funktionen von Windowing.

Ich brauche hier meine aktuellen Favoriten setzen, nur die Liste vollständig zu machen. Mein Favorit ist Antipattern nicht Ihre Abfragen zu testen .

Dies gilt, wenn:

  1. Ihre Abfrage enthält mehr als eine Tabelle.
  2. Sie denken, Sie haben ein optimales Design für eine Abfrage, aber nicht die Mühe, Ihre Annahmen zu testen.
  3. Sie nehmen die erste Abfrage, die, ohne Ahnung, ob es auch nur annähernd optimiert funktioniert.

Und alle Tests laufen gegen atypische oder nicht genügend Daten zählen nicht. Wenn es sich um eine gespeicherte Prozedur ist, legen Sie die Test-Anweisung in einen Kommentar und speichern, mit den Ergebnissen. Ansonsten ist es in einem Kommentar setzen im Code mit den Ergebnissen.

Temporäre Tabelle Missbrauch.

Gerade diese Art der Sache:

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)

Sie eine temporäre Tabelle aus einer Abfrage nicht bauen, nur die Zeilen zu löschen, die Sie nicht benötigen.

Und ja, ich habe Seiten Code in dieser Form in der Produktion DBs gesehen.

Contrarian Blick: über Obsession mit einer Normalisierung.

Die meisten SQL / RBDBs Systeme geben eine viele Funktionen (Transaktionen, Replikation), die sehr nützlich sind, auch mit nicht normalisierten Daten. Speicherplatz ist billig, und manchmal kann es einfacher sein (einfacher Code, schnellere Entwicklungszeit) zu manipulieren / Filter / Such abgerufenen Daten, als es ist 1NF Schema zu schreiben, und befassen sich mit all dem Ärger darin (komplexe Joins, bösen Subselects , usw).

Ich habe die über normalisierte Systeme sind oft vorzeitige Optimierung, vor allem während der frühen Entwicklungsstadien gefunden.

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

Ich habe nur diese eine zusammen, basierend auf einigen der SQL-Antworten hier auf SO.

Es ist eine ernste Antipattern zu denken, dass Trigger-Datenbanken sind als Event-Handler OOP sind. Es gibt diese Wahrnehmung, dass irgendeine Logik in Trigger gesetzt werden kann, feuerte werden, wenn eine Transaktion (Ereignis) auf einem Tisch passiert.

Das stimmt nicht. Einer der großen Unterschiede sind, dass Trigger synchron ist - mit aller Macht, weil sie auf einer Set-Operation synchron sind, nicht auf einer Zeilenoperation. Auf der OOP Seite, genau das Gegenteil - Ereignisse sind eine effiziente Möglichkeit, asynchrone Transaktionen zu implementieren

.

Stored Procedures oder Funktionen ohne Kommentare ...

1) Ich weiß nicht, es ist ein „offizielles“ anti-Muster, aber ich mag nicht und versuche Stringliterale als magische Werte in einer Datenbankspalte zu vermeiden.

Ein Beispiel aus MediaWiki Table 'Bild':

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 verschiedene Gehäuse, eine andere Sache zu vermeiden)

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

2) Datum / String-Konvertierung, die auf bestimmten NLS-Einstellungen

stützt
CONVERT(NVARCHAR, GETDATE())

ohne Formatkennung

Identische Unterabfragen in einer Abfrage.

  • Die Altered View - Eine Ansicht, die allzu oft verändert und ohne vorherige Ankündigung oder Grund. Die Änderung wird entweder an der unpassendsten Zeit oder schlechter als falsch und nie bemerkt bemerkt werden. Vielleicht brechen Sie Ihre Anwendung, weil jemand einen besseren Namen gedacht für diese Spalte. In der Regel sollten Ansichten, welche die Nützlichkeit von Basistabellen erweitern, während ein Vertrag mit den Verbrauchern zu halten. Probleme beheben, aber nicht fügen Funktionen oder schlechtes Wechselverhalten, denn das ist eine neue Ansicht erstellen. Zur Milderung Ansichten nicht teilen mit anderen Projekten und verwenden Sie CTEs wenn Plattformen ermöglichen. Wenn Ihr Geschäft ein DBA hat, kann man wahrscheinlich nicht die Ansichten ändern, aber alle Ihre Ansichten werden veraltet und nutzlos oder in diesem Fall.

  • !
  • Die Paramed - Kann eine Abfrage mehr als einen Zweck? Wahrscheinlich aber die nächste Person, die es liest, wird nicht bis tiefer Meditation kennen. Selbst wenn Sie sie nicht brauchen gerade jetzt die Chancen sind, werden Sie, auch wenn es „nur“ zu debuggen. Hinzufügen von Parametern senkt die Wartungszeit und die Dinge trocken. Wenn Sie eine where-Klausel haben, sollten Sie Parameter haben.

  • Der Fall ohne CASE -

    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  
    

Die beiden, die ich die meisten finden, und kann erhebliche Kosten in Bezug auf die Leistung haben, sind:

  • Mit Cursor statt eines Satzes basiert Ausdruck. Ich denke, dies ist ein häufig auftritt, wenn der Programmierer procedurely denkt.

  • Mit korrelierte Unterabfragen, wenn ein beitreten zu einer abgeleiteten Tabelle kann das tun Job.

Putting Sachen in der temporären Tabellen, vor allem Menschen, die von SQL Server zu Oracle wechseln haben eine Gewohnheit von überbeanspruchend temporären Tabellen. Verwenden Sie einfach select-Anweisungen verschachtelt.

Entwickler, die Abfragen zu schreiben, ohne eine gute Idee, über das, was SQL macht Applikationen (sowohl individuelle Abfragen und Mehrplatzsysteme) schnell oder langsam. Dazu gehört Unkenntnis über:

  • physikalische I / O-Minimierungsstrategien, da die meisten Anfragen Engpass I / O nicht CPU
  • perf Auswirkungen der verschiedenen Arten von physischen Speicherzugriff (zum Beispiel viele sequenzielle E / A wird schneller sein als viele kleine zufällige I / O, wenn auch weniger, wenn Ihr physischer Speicher ist eine SSD!)
  • , wie eine Abfrage zur Hand Abstimmung, wenn das DBMS erzeugt ein schlechter Abfrage-Plan
  • , wie schlechte Datenbank-Performance zu diagnostizieren, wie man „debug“ eine langsame Abfrage, und wie ein Abfrage-Plan zu lesen (oder ERKLäRT, je nach DBMS der Wahl)
  • Strategien Sperren Durchsatz zu optimieren und vermeiden Deadlocks in Multi-User-Anwendungen
  • Bedeutung der Dosierung und andere Tricks Verarbeitung von Datensätzen verarbeiten
  • Tabellen- und Index Design beste Balance Raum und Leistung (zum Beispiel Indizes abdecken, Indizes klein, wo möglich zu halten, Datentypen auf minimale Größe reduziert erforderlich, usw.)

Verwenden von SQL als verherrlicht ISAM (Indexed Sequential Access Method) Paket. Insbesondere Verschachtelung Cursor anstelle der Kombination von SQL-Anweisungen in einem einzigen, wenn auch größer, Aussage. Dies gilt auch als ‚Missbrauch des Optimierers‘, da in der Tat gibt es nicht viel die Optimierer tun kann. Dies kann mit nicht vorbereiteten Anweisungen für maximale Ineffizienz kombiniert werden:

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 (fast immer) ist es, die beiden SELECT-Anweisungen zu einem 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 für die Doppel-Loop-Version ist, dass Sie leicht die Pausen zwischen den Werten in Tabelle 1, da die innere Schleife endet erkennen. Dies kann ein Faktor bei der Kontrolle Bruch Berichten sein.

Auch in der Anwendung ist die Sortierung in der Regel tabu.

Mit Primärschlüssel als Ersatz für die Aufzeichnungsadressen und mit Fremdschlüssel als Ersatz für Zeiger in Aufzeichnungen eingebettet ist.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top