Ist select * in SQL Server 2012 immer noch ein großes No-No?


41

Früher galt es als ein großes No-No select * from tableoder select count(*) from tablewegen des Performance-Hits.

Ist dies in späteren Versionen von SQL Server immer noch der Fall (ich verwende 2012, aber ich denke, die Frage würde für 2008 - 2014 gelten)?

Edit: Da die Leute mich hier ein wenig in den Schatten stellen, betrachte ich dies aus einer Benchmark / akademischen Sicht, nicht ob es das "richtige" ist (was natürlich nicht ist)

Antworten:


50

Wenn Sie SELECT COUNT(*) FROM TABLEnur eine Zeile (die Anzahl) zurückgeben, ist dies relativ einfach und der Weg, um dieses Datum zu erhalten.

Und SELECT *ist kein physisches Nein-Nein, da es legal und erlaubt ist.

Das Problem dabei SELECT *ist jedoch, dass Sie viel mehr Datenbewegungen verursachen können. Sie bearbeiten jede Spalte in der Tabelle. Wenn Sie SELECTnur wenige Spalten angeben, können Sie Ihre Antwort möglicherweise aus einem Index oder mehreren Indizes abrufen, wodurch die E / A und auch die Auswirkungen auf den Server-Cache verringert werden.

Also, ja, es wird generell empfohlen, da es Ihre Ressourcen verschwendet.

Der einzige wirkliche Vorteil SELECT *besteht darin, nicht alle Spaltennamen einzugeben. In SSMS können Sie jedoch per Drag & Drop die Spaltennamen in Ihrer Abfrage abrufen und diejenigen löschen, die Sie nicht benötigen.

Eine Analogie: Wenn jemand Gebrauch , SELECT *wenn sie nicht jede Spalte benötigen, würden sie auch verwenden , SELECTohne WHERE(oder eine andere Begrenzung Klausel) , wenn sie jede Zeile brauchen nicht tun?


24

Zusätzlich zu der Antwort, die der Anbieter bereits gegeben hat, sollte darauf hingewiesen werden, dass Entwickler bei der Arbeit mit modernen ORMs wie Entity Framework häufig zu faul sind. Während DBAs alles daran setzen, dies zu vermeiden SELECT *, schreiben Entwickler häufig das semantische Äquivalent, z. B. in c # Linq:

var someVariable = db.MyTable.Where(entity => entity.FirstName == "User").ToList();

Im Wesentlichen würde dies zu Folgendem führen:

SELECT * FROM MyTable WHERE FirstName = 'User'

Es gibt auch einen zusätzlichen Aufwand, der noch nicht abgedeckt wurde. Das sind die Ressourcen, die erforderlich sind, um jede Spalte in jeder Zeile für das betreffende Objekt zu verarbeiten. Außerdem muss für jedes Objekt, das sich im Speicher befindet, dieses Objekt bereinigt werden. Wenn Sie nur die benötigten Spalten auswählen, können Sie problemlos mehr als 100 MB RAM einsparen. Es ist zwar keine große Menge für sich, aber der kumulative Effekt der Müllabfuhr usw. ist der Kostenfaktor für den Kunden.

Also ja, zumindest für mich ist und bleibt es ein großes Nein. Wir müssen auch über die "versteckten" Kosten aufklären, die damit verbunden sind.

Nachtrag

Im folgenden Beispiel werden nur die Daten abgerufen, die Sie in den Kommentaren angefordert haben:

var someVariable = db.MyTable.Where(entity => entity.FirstName == "User")
                             .Select(entity => new { entity.FirstName, entity.LastNight });

13

Leistung: Eine Abfrage mit SELECT * wird wahrscheinlich niemals eine abdeckende Abfrage sein ( einfache Erläuterung , Erklärung des Stapelüberlaufs ).

Zukunftssicherheit: Ihre Abfrage gibt heute möglicherweise alle sieben Spalten zurück. Wenn jedoch im nächsten Jahr fünf Spalten hinzugefügt werden, gibt Ihre Abfrage in einem Jahr zwölf Spalten zurück und verschwendet E / A und CPU.

Indizierung: Wenn Sie möchten, dass Ihre Ansichten und Tabellenfunktionen an der Indizierung in SQL Server teilnehmen, müssen diese Ansichten und Funktionen mit Schemabindung erstellt werden, was die Verwendung von SELECT * verbietet.

Best Practice : Niemals SELECT *in Produktionscode verwenden.

Für Unteranfragen bevorzuge ich WHERE EXISTS ( SELECT 1 FROM … ).

Bearbeiten : Um Craig Youngs Kommentar unten zu adressieren, ist die Verwendung von "SELECT 1" in einer Unterabfrage keine "'Optimierung" - es ist so, dass ich mich vor meine Klasse stellen und sagen kann "benutze kein SELECT *, keine Ausnahmen!" "

Die einzige Ausnahme, die ich mir vorstellen kann, ist, wo der Client eine Art Pivot-Table-Operation ausführt und alle gegenwärtigen und zukünftigen Spalten benötigt.

Ich könnte eine Ausnahme akzeptieren, die CTEs und abgeleitete Tabellen betrifft, obwohl ich Ausführungspläne sehen möchte.

Beachten Sie, dass ich COUNT(*)eine Ausnahme in Betracht ziehe, da es sich um eine andere syntaktische Verwendung von "*" handelt.


10

In SQL Server 2012 (oder einer beliebigen Version ab 2005) ist die Verwendung SELECT *...nur ein mögliches Leistungsproblem in der SELECT-Anweisung der obersten Ebene einer Abfrage.

Es ist also KEIN Problem in Views (*), in Unterabfragen, in EXIST-Klauseln, in CTEs oder in SELECT COUNT(*)..usw. usw. Beachten Sie, dass dies wahrscheinlich auch für Oracle, DB2 und möglicherweise PostGres gilt (nicht sicher). , aber es ist sehr wahrscheinlich, dass es in vielen Fällen immer noch ein Problem für MySql ist.

Um zu verstehen, warum (und warum es in einem SELECT der obersten Ebene immer noch ein Problem sein kann), ist es hilfreich zu verstehen, warum es jemals ein Problem war, denn die Verwendung von SELECT *..bedeutet " ALLE Spalten zurückgeben ". Im Allgemeinen werden dadurch viel mehr Daten zurückgegeben, als Sie wirklich möchten, was offensichtlich zu viel mehr E / A führen kann, sowohl auf der Festplatte als auch im Netzwerk.

Weniger offensichtlich ist, dass dies auch einschränkt, welche Indizes und Abfragepläne ein SQL-Optimierer verwenden kann, da er weiß, dass er letztendlich alle Datenspalten zurückgeben muss. Wenn das Unternehmen im Voraus weiß, dass Sie nur bestimmte Spalten benötigen, kann es häufig effizientere Abfragepläne verwenden, indem es Indizes nutzt, die nur diese Spalten enthalten. Glücklicherweise gibt es eine Möglichkeit, dies im Voraus zu wissen, dh Sie können die gewünschten Spalten in der Spaltenliste explizit angeben. Aber wenn Sie "*" verwenden, verzichten Sie zugunsten von "Geben Sie mir einfach alles, ich werde herausfinden, was ich brauche."

Ja, die Verarbeitung jeder Spalte erfordert zusätzlichen CPU- und Arbeitsspeicherbedarf, ist jedoch im Vergleich zu diesen beiden Faktoren fast immer geringfügig: die erhebliche zusätzliche Festplatten- und Netzwerkbandbreite, die für nicht benötigte Spalten erforderlich ist, und die Notwendigkeit, weniger zu verwenden optimierter Abfrageplan, da er jede Spalte enthalten muss.

Was hat sich also geändert? Grundsätzlich haben die SQL-Optimierer erfolgreich eine Funktion namens "Spaltenoptimierung" integriert, die nur bedeutet, dass sie jetzt in den Unterabfragen der unteren Ebene herausfinden können, ob Sie jemals tatsächlich eine Spalte in den oberen Ebenen der Abfrage verwenden werden.

Das Fazit ist, dass es keine Rolle mehr spielt, wenn Sie 'SELECT * ..' in den unteren / inneren Ebenen einer Abfrage verwenden. Entscheidend ist vielmehr, was sich in der Spaltenliste des SELECT der obersten Ebene befindet. Sofern Sie nicht SELECT *..oben verwenden, muss erneut davon ausgegangen werden, dass ALLE Spalten gewünscht werden , und daher können Spaltenoptimierungen nicht effektiv eingesetzt werden.

(* - Beachten Sie, dass es in Views ein anderes, geringfügiges Bindungsproblem gibt, bei *dem die Änderung in Spaltenlisten nicht immer registriert wird, wenn "*" verwendet wird. Es gibt andere Möglichkeiten, dies zu beheben, und dies wirkt sich nicht auf die Leistung aus.)


5

Es gibt noch einen kleinen Grund, den Sie nicht verwenden sollten SELECT *: Wenn sich die Reihenfolge der zurückgegebenen Spalten ändert, wird Ihre Anwendung nicht mehr funktionieren ... wenn Sie Glück haben. Wenn Sie nicht sind, haben Sie einen subtilen Fehler, der für eine lange Zeit unentdeckt bleiben könnte. Die Reihenfolge der Felder in einer Tabelle ist ein Implementierungsdetail, das von Anwendungen niemals berücksichtigt werden sollte, da es nur dann sichtbar ist, wenn Sie a verwenden SELECT *.


4
Das ist irrelevant. Wenn Sie in Ihrem Anwendungscode über den Spaltenindex auf die Spalten zugreifen, haben Sie eine fehlerhafte Anwendung verdient. Der Zugriff auf die Spalten über den Namen führt immer zu deutlich besser lesbarem Anwendungscode und ist so gut wie nie der Leistungsengpass.
Lie Ryan

3

Es ist physisch und problematisch erlaubt select * from table, es ist jedoch eine schlechte Idee. Warum?

Zunächst werden Sie feststellen, dass Sie Spalten zurückgeben, die Sie nicht benötigen (ressourcenintensiv).

Zweitens dauert es bei einer großen Tabelle länger als bei der Benennung der Spalten, da Sie bei Auswahl von * tatsächlich die Spaltennamen aus der Datenbank auswählen und sagen: "Geben Sie mir die Daten, die mit Spalten verknüpft sind, die Namen in dieser anderen Liste haben . " Während dies für den Programmierer schnell geht, können Sie sich vorstellen, dies auf dem Computer einer Bank zu tun, auf dem in einer Minute buchstäblich Hunderttausende von Suchvorgängen ausgeführt wurden.

Drittens wird es für den Entwickler schwieriger, dies zu tun. Wie oft müssen Sie von SSMS zu VS wechseln, um alle Spaltennamen abzurufen?

Viertens ist es ein Zeichen von Faulheit beim Programmieren, und ich glaube nicht, dass ein Entwickler diesen Ruf haben möchte.


Ihr zweites Argument in dieser aktuellen Form weist einige kleine Fehler auf. Zunächst wird das Schema der Tabellen von allen RDBMS zwischengespeichert. Dies liegt hauptsächlich daran, dass das Schema ohnehin in der Analysephase der Abfrage geladen wird, um festzustellen, welche Spalte in der Tabelle der Abfrage vorhanden ist oder fehlt. Daher hat der Abfrageparser die Spaltennamensliste bereits selbst abgefragt und * sofort durch eine Liste der Spalten ersetzt. Dann versuchen die meisten RDBMS-Engines, alles Mögliche zwischenzuspeichern. Wenn Sie also die SELECT * FROM-Tabelle ausgeben, wird die kompilierte Abfrage zwischengespeichert, sodass das Parsen nicht jedes Mal erfolgt. Und Entwickler sind faul :-)
Gabor Garami

In Bezug auf Ihr zweites Argument ist dies ein weit verbreitetes Missverständnis - das Problem bei SELECT * ist keine Metadatensuche, da SQL Server beim Benennen der Spalten noch deren Namen überprüfen, Datentypen überprüfen usw. muss.
Aaron Bertrand

@Gabor Eines der Probleme mit SELECT * tritt auf, wenn Sie das in eine Ansicht einfügen. Wenn Sie das zugrunde liegende Schema ändern, kann die Ansicht verwirrend werden. Sie hat nun ein anderes Konzept des Schemas der Tabelle (ihres eigenen) als die Tabelle selbst. Ich spreche hier darüber .
Aaron Bertrand

3

Es kann ein Problem sein, wenn Sie den Select * ...Code in ein Programm einfügen, da sich die Datenbank, wie bereits erwähnt, im Laufe der Zeit möglicherweise ändert und mehr Spalten enthält, als Sie beim Schreiben der Abfrage erwartet hatten. Dies kann zu Programmfehlern führen (im besten Fall), oder das Programm geht auf eine fröhliche Art und Weise und beschädigt einige Daten, da es Feldwerte betrachtet, für die es nicht geschrieben wurde. Kurz gesagt, der Produktionscode sollte IMMER die Felder angeben, die im Feld zurückgegeben werden sollen SELECT.

Abgesehen davon habe ich weniger Probleme, wenn das Select *Teil einer EXISTSKlausel ist, da alles, was an das Programm zurückgegeben wird, ein Boolescher Wert ist, der den Erfolg oder Misserfolg der Auswahl anzeigt. Andere mögen mit dieser Haltung nicht einverstanden sein, und ich respektiere ihre Meinung dazu. Der Code Select *ist möglicherweise etwas weniger effizient als der Code für 'Select 1' in einer EXISTSKlausel, aber ich glaube, es besteht in keiner Weise die Gefahr einer Datenbeschädigung.


Ja, ich wollte eigentlich auf die EXISTS-Klausel verweisen. Mein Fehler.
Mark Ross

2

Viele Antworten, warum select *ist falsch, so werde ich abdecken, wenn ich das Gefühl habe, es ist richtig oder zumindest in Ordnung.

1) In EXISTS wird der Inhalt des SELECT-Teils der Abfrage ignoriert, sodass Sie sogar schreiben können SELECT 1/0und kein Fehler auftritt . EXISTSüberprüft nur, dass einige Daten zurückkehren würden und gibt darauf basierend einen Booleschen Wert zurück.

IF EXISTS(
    SELECT * FROM Table WHERE X=@Y
)

2) Dies könnte einen Feuersturm auslösen, aber ich verwende gerne select *Trigger in meiner Verlaufstabelle. Dadurch select *wird verhindert, dass die Haupttabelle eine neue Spalte erhält, ohne dass die Spalte auch der Verlaufstabelle hinzugefügt wird, da ein Fehler sofort beim Einfügen / Aktualisieren / Löschen in die Haupttabelle auftritt. Dies hat mehrfach verhindert, dass Entwickler Spalten hinzufügten und vergaßen, sie der Verlaufstabelle hinzuzufügen.


3
Ich bevorzuge SELECT 1es immer noch, weil es am offensichtlichsten zukünftige Code-Betreuer über Ihre Absicht informiert. Es ist keine Voraussetzung , aber wenn ich es sehe ... WHERE EXISTS (SELECT 1 ...), kündigt es sich ganz offensichtlich als Wahrheitstest an.
Swasheck

1
@zlatanViele Leute verwenden SELECT 1basierend auf einem Mythos, dass Leistung besser wäre als SELECT *. Beide Optionen sind jedoch durchaus akzeptabel. Aufgrund der Art und Weise, wie der Optimierer mit EXISTS umgeht, gibt es keine Leistungsunterschiede. Auch kein Unterschied in der Lesbarkeit aufgrund des Wortes "EXISTS", das eindeutig einen Wahrheitstest ankündigt.
Desillusioniert

Zu Punkt 2 verstehe ich Ihre Argumentation, aber es gibt immer noch Risiken. Lassen Sie mich ein Szenario für Sie malen ... Entwickler fügt Column8der Haupttabelle hinzu und vergisst die Verlaufstabelle. Entwickler schreibt eine Reihe von Code in Spalte 8. Dann fügt Column9er der Haupttabelle hinzu; diesmal daran denken, auch zur Geschichte beizutragen. Beim späteren Testen merkt er, dass er vergessen hat, Column9der Historie etwas hinzuzufügen (dank Ihrer Fehlererkennungstechnik), und fügt es sofort hinzu. Jetzt scheint der Trigger zu funktionieren, aber die Daten in den Spalten 8 und 9 sind im Verlauf vertauscht. : S
Desillusioned

cont ... Der Punkt ist, dass das oben beschriebene Szenario nur eines von vielen ist, die dazu führen können, dass Ihr Fehlererkennungs-Trick versagt und die Dinge tatsächlich noch schlimmer macht. Grundsätzlich braucht man eine bessere Technik. Eine, die sich nicht darauf stützt, dass Ihr Trigger Annahmen über die Reihenfolge der Spalten in einer von Ihnen ausgewählten Tabelle trifft. Vorschläge: - Persönliche Codeüberprüfungen mit Checklisten Ihrer häufigsten Fehler. - Peer-Code-Überprüfungen. - Alternative Technik zur Verfolgung der Historie (persönlich halte ich auslöserbasierte Mechanismen für reaktiv anstatt proaktiv und daher fehleranfällig).
Enttäuscht

@CraigYoung Das ist eine Möglichkeit. Aber ich würde jemanden drosseln, wenn sie das tun würden. Das ist kein Fehler, den Sie leicht machen könnten
UnhandledExcepSean
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.