Gestern diskutierte ich mit einem "Hobby" -Programmierer (ich selbst bin ein professioneller Programmierer). Wir sind auf einige seiner Arbeiten gestoßen, und er sagte, dass er immer alle Spalten in seiner Datenbank abfragt (sogar auf / in Produktionsserver / Code).
Ich habe versucht, ihn davon abzubringen, war aber noch nicht so erfolgreich. Meiner Meinung nach sollte ein Programmierer nur abfragen, was tatsächlich aus Gründen der "Hübschheit", Effizienz und des Datenverkehrs benötigt wird. Irre ich mich mit meiner Ansicht?
Antworten:
Überlegen Sie, was Sie zurückerhalten und wie Sie diese an Variablen in Ihrem Code binden.
Denken Sie nun darüber nach, was passiert, wenn jemand das Tabellenschema aktualisiert, um eine Spalte hinzuzufügen (oder zu entfernen), auch wenn Sie diese nicht direkt verwenden.
Die Verwendung von select *, wenn Sie Abfragen von Hand eingeben, ist in Ordnung, nicht wenn Sie Abfragen für Code schreiben.
quelle
Schemaänderungen
foo
, und eine andere Tabelle in der Abfrage eine Spalte hinzufügtfoo
, kann die Art und Weise, wie dies behandelt wird, Probleme verursachen, wenn versucht wird, die richtigefoo
Spalte abzurufen.In beiden Fällen kann eine Schemaänderung Probleme beim Extrahieren der Daten verursachen.
Ferner prüfen , ob eine Spalte , die benutzt wurde, wird entfernt aus der Tabelle. Das
select * from ...
klappt immer noch aber fehlerfrei beim Versuch die Daten aus der Ergebnismenge zu ziehen. Wenn die Spalte in der Abfrage angegeben ist, wird die Abfrage fehlschlagen und stattdessen eine eindeutige Angabe darüber geben, wo und was das Problem ist.Datenaufwand
Mit einigen Spalten kann eine erhebliche Datenmenge verknüpft sein. Durch Auswahl von Zurück
*
werden alle Daten abgerufen . Ja, hier ist dervarchar(4096)
Wert für 1000 Zeilen, die Sie zurück ausgewählt haben. Dies gibt Ihnen zusätzliche mögliche 4 Megabyte an Daten, die Sie nicht benötigen, die aber trotzdem über die Leitung gesendet werden.In Bezug auf die Schemaänderung ist das Varchar dort möglicherweise nicht vorhanden, als Sie die Tabelle zum ersten Mal erstellt haben, jetzt ist es dort.
Nichtvermittlung von Absichten
Wenn Sie "Zurück" auswählen
*
und 20 Spalten erhalten, aber nur zwei davon benötigen, vermitteln Sie nicht die Absicht des Codes. Wenn man sich die Abfrage ansieht, weißselect *
man nicht, worauf es ankommt. Kann ich die Abfrage so ändern, dass stattdessen dieser andere Plan verwendet wird, um sie zu beschleunigen, indem diese Spalten nicht berücksichtigt werden? Ich weiß es nicht, weil die Absicht der Rückgabe der Abfrage nicht klar ist.Schauen wir uns einige SQL-Fiddles an, die diese Schemaänderungen etwas genauer untersuchen.
Zunächst die ursprüngliche Datenbank: http://sqlfiddle.com/#!2/a67dd/1
DDL:
SQL:
Und die Spalten, die Sie zurückerhalten
oneid=1
,data=42
sindtwoid=2
, undother=43
.Was passiert nun, wenn ich einer Tabelle eine Spalte hinzufüge? http://sqlfiddle.com/#!2/cd0b0/1
Und meine Ergebnisse aus der gleichen Abfrage nach wie vor sind
oneid=1
,data=42
,twoid=2
, undother=foo
.Eine Änderung in einer der Tabellen unterbricht die Werte von a
select *
und plötzlich wird Ihre Bindung von 'other' an ein int einen Fehler auslösen, und Sie wissen nicht, warum.Wenn stattdessen Ihre SQL-Anweisung war
Die Änderung an Tabelle eins hätte Ihre Daten nicht gestört. Diese Abfrage wird vor und nach der Änderung gleich ausgeführt.
Indizierung
Wenn Sie eine
select * from
ausführen, ziehen Sie alle Zeilen aus allen Tabellen, die den Bedingungen entsprechen. Sogar Tische, die dir wirklich egal sind. Während dies bedeutet, dass mehr Daten übertragen werden, lauert ein weiteres Leistungsproblem weiter unten im Stapel.Indizes. (bezogen auf SO: Wie verwende ich den Index in der select-Anweisung? )
Wenn Sie viele Spalten zurückziehen, ignoriert das Datenbankplanoptimierungsprogramm möglicherweise die Verwendung eines Index, da Sie trotzdem alle diese Spalten abrufen müssen und es mehr Zeit in Anspruch nehmen würde, den Index und dann alle Spalten in der Abfrage abzurufen als wäre es nur ein kompletter Tabellenscan zu machen.
Wenn Sie nur den Nachnamen eines Benutzers auswählen (den Sie häufig verwenden und auf dem Sie einen Index haben), kann die Datenbank nur einen Index-Scan durchführen (nur Index-Scan für Postgres-Wiki , vollständiger MySQL- Tabellenscan vs vollständiger Index-Scan , Nur-Index-Scan: Vermeiden des Tabellenzugriffs ).
Es gibt eine ganze Reihe von Optimierungen, wenn möglich, nur aus Indizes zu lesen. Die Informationen können auf jeder Indexseite schneller abgerufen werden, da Sie auch weniger davon abrufen - Sie ziehen nicht alle anderen Spalten für die Indexseite ab
select *
. Es ist möglich, dass ein Nur-Index-Scan Ergebnisse in der Größenordnung von 100x schneller zurückgibt (Quelle: Wählen Sie * ist schlecht ).Dies bedeutet nicht, dass ein vollständiger Index-Scan großartig ist, er ist immer noch ein vollständiger Scan - aber er ist besser als ein vollständiger Tabellenscan. Sobald Sie alle Möglichkeiten
select *
ausloten, die der Leistung schaden, finden Sie immer wieder neue.Verwandte Lesung
quelle
select *
?Ein weiteres Problem: Wenn es sich um eine
JOIN
Abfrage handelt und Sie Abfrageergebnisse in einem assoziativen Array abrufen (wie dies in PHP der Fall sein könnte), ist dies fehleranfällig.Die Sache ist, dass
foo
Spaltenid
und enthältname
bar
spalten hatid
undaddress
,SELECT * FROM foo JOIN bar ON foo.id = bar.id
raten Sie mal, was passiert, wenn jemand
name
derbar
Tabelle eine Spalte hinzufügt .Der Code funktioniert plötzlich nicht mehr ordnungsgemäß, da die
name
Spalte jetzt zweimal in den Ergebnissen angezeigt wird. Wenn Sie die Ergebnisse in einem Array speichern , überschreiben die Daten von secondname
(bar.name
) die erstenname
(foo.name
)!Es ist ein ziemlich böser Fehler, weil es sehr nicht offensichtlich ist. Es kann eine Weile dauern, bis das herausgefunden ist, und die Person, die der Tabelle eine weitere Spalte hinzufügt, kann solche unerwünschten Nebenwirkungen auf keinen Fall vorhersehen.
(Wahre Geschichte).
Behalten Sie also nicht die
*
Kontrolle darüber, welche Spalten Sie abrufen, und verwenden Sie gegebenenfalls Aliase.quelle
SELECT
Klausel eine weitere hinzuzufügen. In diesem Fall erkennen Sie hoffentlich, dass der Name nicht eindeutig ist. Übrigens glaube ich nicht, dass es in Systemen mit großen Datenbanken so selten ist. Wie gesagt, ich habe einmal ein paar Stunden damit verbracht, diesen Fehler in einem großen Schlammball von PHP-Code zu jagen. Und ich habe gerade einen anderen Fall gefunden: stackoverflow.com/q/17715049/168719In vielen Fällen kann es durchaus legitim sein, jede Spalte abzufragen.
Nicht immer jede Spalte abfragen.
Es ist mehr Arbeit für Ihre Datenbank-Engine, die ihre internen Metadaten durchsuchen muss, um herauszufinden, mit welchen Spalten sie sich befassen muss, bevor sie mit dem eigentlichen Geschäft fortfahren kann, die Daten tatsächlich zu erhalten und an Sie zurückzusenden. OK, es ist nicht der größte Overhead der Welt, aber Systemkataloge können ein spürbarer Engpass sein.
Es ist mehr Arbeit für Ihr Netzwerk, weil Sie eine beliebige Anzahl von Feldern zurückziehen, wenn Sie nur ein oder zwei von ihnen möchten. Wenn jemand [anderes] ein paar Dutzend zusätzliche Felder hinzufügt, die alle große Textblöcke enthalten, geht der Durchsatz plötzlich durch den Boden - ohne ersichtlichen Grund. Dies wird noch schlimmer, wenn Ihre "where" -Klausel nicht besonders gut ist und Sie auch viele Zeilen zurückziehen - das ist möglicherweise eine Menge Daten, die über das Netzwerk zu Ihnen gelangen (dh sie werden langsam sein).
Es ist mehr Arbeit für Ihre Anwendung, all diese zusätzlichen Daten abzurufen und zu speichern, die ihr höchstwahrscheinlich egal sind.
Sie laufen Gefahr, dass Spalten ihre Reihenfolge ändern. OK, Sie sollten sich darüber keine Gedanken machen müssen (und auch nicht, wenn Sie nur die gewünschten Spalten auswählen), aber wenn Sie alle auf einmal abrufen, beschließt jemand anderes, die Spaltenreihenfolge in der Tabelle neu zu ordnen , dieser sorgfältig ausgearbeitete CSV-Export, den Sie den Konten im Flur geben, geht plötzlich ganz in den Topf - wieder ohne ersichtlichen Grund.
Übrigens, ich habe oben ein paar Mal "jemand anderes" gesagt. Denken Sie daran, dass Datenbanken von Natur aus Mehrbenutzer sind. Sie haben möglicherweise nicht die Kontrolle über sie, die Sie zu tun glauben.
quelle
TOP
Einschränkung hinzuzufügen . Ich bin nicht sicher, wie wichtig das ist, wenn der Code so viele Zeichen liest, wie er anzeigen soll, und dann die Abfrage freigibt. Ich denke, die Antworten auf Anfragen werden etwas träge verarbeitet, obwohl ich die Details nicht kenne. In jedem Fall denke ich, anstatt zu sagen, dass es "nicht legitim" ist, wäre es besser zu sagen, dass "... in weitaus weniger legitim ist "; Grundsätzlich würde ich die legitimen Fälle als solche zusammenfassen, bei denen der Benutzer eine bessere Vorstellung davon hat, was für ihn von Bedeutung ist als der Programmierer.Die kurze Antwort lautet: Es hängt davon ab, welche Datenbank sie verwenden. Relationale Datenbanken sind für das schnelle, zuverlässige und atomare Extrahieren der benötigten Daten optimiert . Bei großen Datenmengen und komplexen Abfragen ist dies viel schneller und wahrscheinlich sicherer als SELECTING * und entspricht den Verknüpfungen auf der "Code" -Seite. In Schlüsselwertgeschäften sind solche Funktionen möglicherweise nicht implementiert oder sie sind nicht ausgereift genug, um in der Produktion verwendet zu werden.
Trotzdem können Sie die von Ihnen verwendete Datenstruktur mit SELECT * füllen und den Rest im Code verarbeiten. Wenn Sie jedoch skalieren möchten, können Sie Leistungsengpässe feststellen.
Der nächste Vergleich ist das Sortieren von Daten: Sie können Quicksort oder Bubblesort verwenden, und das Ergebnis ist korrekt. Wird aber nicht optimiert und wird definitiv Probleme haben, wenn Sie Parallelität einführen und atomar sortieren müssen.
Natürlich ist es billiger, RAM und CPUs hinzuzufügen, als in einen Programmierer zu investieren, der SQL-Abfragen ausführen kann und sogar ein vages Verständnis dafür hat, was ein JOIN ist.
quelle
Customer customer = this._db.Customers.Where( “it.ID = @ID”, new ObjectParameter( “ID”, id ) ).First();
Siehe Time to Take Offense auf Seite 2.var cmd = db.CreateCommand(); cmd.CommandText = "SELECT TOP 1 * FROM Customers WHERE ID = @ID"; cmd.Parameters.AddWithValue("@ID", id); var result = cmd.ExecuteReader();
.... und dann fahren Sie fort, um einen Kunden aus jeder Zeile zu erstellen. LINQ schlägt die Hose aus.var customer = _db.Customers.Where(it => it.id == id).First();
.IMO, es geht darum, explizit oder implizit zu sein. Wenn ich Code schreibe, möchte ich, dass er funktioniert, weil ich ihn zum Laufen gebracht habe, nicht nur, weil alle Teile einfach da sind. Wenn Sie alle Datensätze abfragen und Ihr Code funktioniert, besteht die Tendenz, dass Sie fortfahren. Wenn sich später etwas ändert und Ihr Code jetzt nicht mehr funktioniert, ist es ein großer Aufwand, viele Abfragen und Funktionen zu debuggen, die nach einem Wert suchen, der vorhanden sein sollte, und die einzigen Referenzwerte sind *.
Auch in einem N-Tier-Ansatz ist es immer noch am besten, Störungen des Datenbankschemas in der Datenschicht zu isolieren. Wenn Ihre Datenebene * an die Geschäftslogik und höchstwahrscheinlich an die Präsentationsebene übergeben wird, erweitern Sie Ihren Debugging-Bereich exponentiell.
quelle
select *
ist viel schlechter!Denn wenn die Tabelle neue Spalten erhält, erhalten Sie all diese, auch wenn Sie sie nicht benötigen. mit
varchars
dieser kann eine Menge zusätzlicher Daten werden , die von der DB zu reisen brauchtEinige DB-Optimierungen extrahieren möglicherweise auch die Datensätze mit nicht fester Länge in eine separate Datei, um den Zugriff auf die Teile mit fester Länge zu beschleunigen
quelle
Abgesehen von dem Overhead, den Sie in erster Linie vermeiden möchten, würde ich sagen, dass Sie als Programmierer nicht auf die vom Datenbankadministrator festgelegte Spaltenreihenfolge angewiesen sind. Sie wählen jede Spalte aus, auch wenn Sie alle benötigen.
quelle
Ich sehe keinen Grund, warum Sie es nicht für den Zweck verwenden sollten, für den es erstellt wurde - alle Spalten aus einer Datenbank abrufen. Ich sehe drei Fälle:
Eine Spalte wird der Datenbank hinzugefügt und Sie möchten, dass sie auch im Code enthalten ist. a) Mit * wird bei einer korrekten Meldung fehlgeschlagen. b) Ohne * wird funktionieren, aber nicht tun, was Sie erwarten, was ziemlich schlecht ist.
Eine Spalte wird in der Datenbank hinzugefügt und soll nicht im Code enthalten sein. a) Mit * wird fehlschlagen; Dies bedeutet, dass * nicht mehr gilt, da seine Semantik "alle abrufen" bedeutet. b) Ohne * wird funktionieren.
Eine Spalte wird entfernt. Code schlägt in beiden Fällen fehl.
Jetzt ist der häufigste Fall Fall 1 (da Sie * verwendet haben, was bedeutet, dass Sie höchstwahrscheinlich alle möchten); Ohne * können Sie Code haben, der gut funktioniert, aber nicht das tut, was erwartet wird, was sehr viel schlimmer ist, als der Code, der mit einer richtigen Fehlermeldung fehlschlägt .
Ich berücksichtige nicht den Code, der die Spaltendaten basierend auf dem Spaltenindex abruft, der meiner Meinung nach fehleranfällig ist. Es ist viel logischer, es basierend auf dem Spaltennamen abzurufen.
quelle
Select *
war eher als Annehmlichkeit für Ad-hoc-Abfragen gedacht, nicht für Zwecke der Anwendungsentwicklung. Oder zur Verwendung in statistischen Konstrukten, beiselect count(*)
denen das Abfragemodul entscheidet, ob ein Index verwendet wird, welcher Index verwendet werden soll usw., und Sie keine tatsächlichen Spaltendaten zurückgeben. Oder zur Verwendung in Klauseln wiewhere exists( select * from other_table where ... )
: Dies ist wiederum eine Aufforderung an die Abfrage-Engine, den effizientesten Pfad selbst auszuwählen, und die Unterabfrage wird nur zum Einschränken der Ergebnisse der Hauptabfrage verwendet. Etc.select *
es die Semantik hat, alle Spalten abzurufen. Wenn Ihre Anwendung dies wirklich benötigt, sehe ich keine Gründe, warum Sie es nicht verwenden sollten. Können Sie auf einen Verweis (Oracle, IBM, Microsoft usw.) verweisen, in dem der Zweck, für den derselect *
Build erstellt wurde, darin besteht, nicht alle Spalten abzurufen?select *
gibt es die Möglichkeit, alle Spalten abzurufen ... als praktische Funktion für Ad-hoc-Abfragen, nicht weil dies in Produktionssoftware eine großartige Idee ist. Die Gründe sind in den Antworten auf dieser Seite bereits ziemlich gut erläutert, weshalb ich keine eigene detaillierte Antwort erstellt habe: •) Leistungsprobleme, wiederholtes Sammeln von Daten über das Netzwerk, die Sie nie verwenden, •) Probleme mit Spalten-Aliasing, •) Abfrage - Plan - Optimierung Ausfälle (Fehler verwenden , um Indizes in einigen Fällen), •) ineffizient Server I / O in Fällen , in denen ausgewählte begrenzt haben könnte allein Indizes verwendet, usw.select *
in einer tatsächlichen Produktionsanwendung rechtfertigt , aber die Natur eines Randfalls ist, dass dies nicht der übliche Fall ist. :-)select *
; Was ich gesagt habe, wenn Sie wirklich alle Spalten brauchen, sehe ich keinen Grund, warum Sie nicht verwenden solltenselect *
; Obwohl es nur wenige Szenarien geben muss, in denen alle Spalten benötigt werden.Stellen Sie sich das so vor ... wenn Sie alle Spalten einer Tabelle mit nur wenigen kleinen Zeichenfolgen oder numerischen Feldern abfragen, ergibt dies insgesamt 100.000 Daten. Schlechtes Training, aber es wird funktionieren. Fügen Sie nun ein einzelnes Feld hinzu, das beispielsweise ein Bild oder ein 10-MB-Word-Dokument enthält. Jetzt wird Ihre schnelle Abfrage sofort und auf mysteriöse Weise schlecht ausgeführt, nur weil der Tabelle ein Feld hinzugefügt wurde. Möglicherweise benötigen Sie dieses riesige Datenelement nicht, aber weil Sie
Select * from Table
es trotzdem erhalten haben.quelle