Jeder weiß, dass neue Entwickler lange Funktionen schreiben. Je weiter Sie voranschreiten, desto besser können Sie Ihren Code in kleinere Teile aufteilen, und die Erfahrung zeigt Ihnen, welchen Wert dies hat.
Geben Sie SQL ein. Ja, die SQL-Denkweise über Code unterscheidet sich von der prozeduralen Denkweise über Code, aber dieses Prinzip scheint ebenso anwendbar zu sein.
Angenommen, ich habe eine Abfrage in der folgenden Form:
select * from subQuery1 inner join subQuerry2 left join subquerry3 left join join subQuery4
Verwendung einiger IDs oder Daten usw.
Diese Unterabfragen sind selbst komplex und können eigene Unterabfragen enthalten. In keinem anderen Programmierkontext würde ich denken, dass die Logik für komplexe Unterabfragen 1-4 zu meiner übergeordneten Abfrage gehört, die sie alle verbindet. Es scheint so einfach, dass diese Unterabfragen als Ansichten definiert werden sollten, genau wie sie Funktionen wären, wenn ich prozeduralen Code schreiben würde.
Warum ist das nicht üblich? Warum schreiben die Leute so oft diese langen monolithischen SQL-Abfragen? Warum fördert SQL nicht die Verwendung umfangreicher Sichten wie die prozedurale Programmierung die Verwendung umfangreicher Funktionen? (In vielen Unternehmensumgebungen ist das Erstellen von Ansichten gar nicht so einfach. Es sind Anforderungen und Genehmigungen erforderlich. Stellen Sie sich vor, andere Programmierertypen müssten bei jeder Erstellung einer Funktion eine Anforderung stellen!)
Ich habe mir drei mögliche Antworten überlegt:
Das ist schon üblich und ich arbeite mit unerfahrenen Leuten
Erfahrene Programmierer schreiben kein komplexes SQL, weil sie es vorziehen, schwierige Datenverarbeitungsprobleme mit prozeduralem Code zu lösen
Etwas anderes
Antworten:
Ich denke, das Hauptproblem ist, dass nicht alle Datenbanken Common Table Expressions unterstützen.
Mein Arbeitgeber nutzt DB / 2 für viele Dinge. Die neuesten Versionen unterstützen CTEs, sodass ich folgende Aufgaben ausführen kann:
Das Ergebnis ist, dass wir stark abgekürzte Tabellen- / Feldnamen haben können und ich im Wesentlichen temporäre Ansichten mit besser lesbaren Namen erstelle, die ich dann verwenden kann. Klar, die Abfrage wird länger. Aber das Ergebnis ist, dass ich etwas schreiben kann, das ziemlich klar voneinander getrennt ist (indem ich CTEs so verwende, wie Sie Funktionen verwenden, um DRY zu erhalten) und am Ende gut lesbaren Code erhalte. Und weil ich meine Unterabfragen aufteilen und eine Unterabfrage auf eine andere verweisen kann, ist dies nicht alles "inline". Ich habe gelegentlich einen CTE geschrieben, dann vier andere CTEs, die alle darauf verweisen, und dann hatte die Hauptabfrage Union die Ergebnisse der letzten vier.
Dies kann erfolgen mit:
Aber es geht einen langen Weg, um den Code sauberer, lesbarer und trockener zu machen.
Ich habe eine "Standardbibliothek" von CTEs entwickelt, die ich in verschiedene Abfragen einbinden kann, um meine neue Abfrage zu starten. Einige von ihnen werden auch von anderen Entwicklern in meiner Organisation angenommen.
Mit der Zeit kann es sinnvoll sein, einige davon in Ansichten umzuwandeln, sodass diese "Standardbibliothek" verfügbar ist, ohne dass Sie kopieren / einfügen müssen. Aber am Ende werden meine CTEs so geringfügig an den verschiedenen Anforderungen angepasst, dass ich keinen einzigen CTE ohne Modifikationen so umfassend einsetzen konnte, dass es sich möglicherweise lohnt, eine Ansicht zu erstellen.
Es scheint, als ob ein Teil Ihrer Beschwerde lautet: "Warum weiß ich nichts über CTEs?" oder "Warum unterstützt meine DB keine CTEs?"
Was Aktualisierungen betrifft ... Ja, Sie können CTEs verwenden, aber meiner Erfahrung nach müssen Sie sie in der set-Klausel UND in der where-Klausel verwenden. Es wäre schön, wenn Sie einen oder mehrere vor der gesamten update-Anweisung definieren könnten und dann nur die "main query" -Teile in den set / where-Klauseln hätten, aber das funktioniert nicht. Und es gibt keinen Grund, obskure Tabellen- / Feldnamen in der Tabelle, die Sie aktualisieren, zu vermeiden.
Sie können CTEs zum Löschen verwenden. Möglicherweise sind mehrere CTEs erforderlich, um die PK / FK-Werte für Datensätze zu ermitteln, die Sie aus dieser Tabelle löschen möchten. Auch hier können Sie nicht vermeiden, dass die Tabellen- / Feldnamen in der Tabelle, die Sie ändern, undeutlich werden.
Soweit Sie eine Auswahl in eine Einfügung vornehmen können, können Sie CTEs für Einfügungen verwenden. Wie immer haben Sie es möglicherweise mit undurchsichtigen Tabellen- / Feldnamen in der Tabelle zu tun, die Sie ändern.
In SQL können Sie NICHT das Äquivalent eines Domänenobjekts erstellen, indem Sie eine Tabelle mit Gettern / Setzern umschließen. Dazu benötigen Sie eine Art ORM sowie eine prozedurale / OO-Programmiersprache. Ich habe solche Dinge in Java / Hibernate geschrieben.
quelle
Das Sperren der Erstellung von Datenbankansichten wird häufig von Organisationen vorgenommen, die Leistungsprobleme in der Datenbank paranoid behandeln. Dies ist eher ein Problem der Organisationskultur als ein technisches Problem mit SQL.
Darüber hinaus werden große monolithische SQL-Abfragen oft geschrieben, da der Anwendungsfall so spezifisch ist, dass nur sehr wenig des SQL-Codes in anderen Abfragen wirklich wiederverwendet werden kann. Wenn eine komplexe Abfrage benötigt wird, handelt es sich normalerweise um einen ganz anderen Anwendungsfall. Das Kopieren der SQL aus einer anderen Abfrage ist häufig ein Ausgangspunkt. Aufgrund der anderen Unterabfragen und JOINs in der neuen Abfrage müssen Sie die kopierte SQL jedoch so weit modifizieren, dass jede Art von Abstraktion, die eine "Funktion" in einer anderen Sprache ausführen würde, aufgehoben wird wird benutzt für. Das bringt mich zu dem wichtigsten Grund, warum SQL schwer umzugestalten ist.
SQL befasst sich nur mit konkreten Datenstrukturen, nicht mit abstraktem Verhalten (oder einer Abstraktion im wahrsten Sinne des Wortes). Da sich SQL um konkrete Ideen dreht, gibt es nichts, was man in ein wiederverwendbares Modul abstrahieren könnte. Datenbankansichten können dabei helfen, jedoch nicht auf der gleichen Ebene wie eine "Funktion" in einer anderen Sprache. Eine Datenbankansicht ist weniger eine Abstraktion als eine Abfrage. Nun, eigentlich ein Datenbank - View ist eine Abfrage. Es wird im Wesentlichen wie eine Tabelle verwendet, aber wie eine Unterabfrage ausgeführt. Wiederum handelt es sich also um etwas Konkretes, nicht um etwas Abstraktes.
Mit Abstraktionen lässt sich Code leichter umgestalten, da eine Abstraktion Implementierungsdetails vor dem Konsumenten dieser Abstraktion verbirgt. Straight SQL bietet keine solche Trennung, obwohl prozedurale Erweiterungen von SQL wie PL / SQL für Oracle oder Transact-SQL für SQL Server die Zeilen ein wenig verwischen.
quelle
Das, was ich denke, dass Sie aus Ihrer Frage / Sicht vermissen, ist, dass SQL Operationen auf Mengen ausführt (mit Mengenoperationen usw.).
Wenn Sie auf dieser Ebene arbeiten, geben Sie natürlich die Kontrolle über den Motor auf. Sie können immer noch einen prozeduralen Code mit Cursorn erzwingen, aber wie die Erfahrung zeigt, sollten Sie dies nicht tun.
Das Refactoring von SQL ist möglich, verwendet jedoch nicht die gleichen Prinzipien für das Refactoring von Code wie im Code auf Anwendungsebene. Stattdessen optimieren Sie, wie Sie die SQL-Engine selbst verwenden.
Dies kann auf verschiedene Arten erfolgen. Wenn Sie Microsoft SQL Server verwenden, können Sie mithilfe von SSMS einen ungefähren Ausführungsplan erstellen und anhand dieses Plans ermitteln, welche Schritte Sie zum Optimieren Ihres Codes ausführen können.
Beim Aufteilen von Code in kleinere Module ist SQL, wie bei @ greg-burghardt bereits erwähnt, in der Regel ein eigens dafür erstellter Code. Es macht das, wofür du es brauchst und sonst nichts. Es hält sich an das S in SOLID, es gibt nur einen Grund, geändert / beeinflusst zu werden, und dann brauchen Sie diese Abfrage, um etwas anderes zu tun. Der Rest des Akronyms (OLID) trifft hier nicht zu (AFAIK enthält keine Abhängigkeitsinjektion, Schnittstellen oder Abhängigkeiten als solche in SQL). Abhängig von der verwendeten SQL-Variante können Sie bestimmte Abfragen möglicherweise erweitern, indem Sie sie umbrechen Ich würde sagen, dass in einer Funktion für gespeicherte Prozeduren / Tabellen oder deren Verwendung als Unterabfragen das Open-Closed-Prinzip in gewisser Weise weiterhin gilt. Aber ich schweife ab.
Ich denke, Sie müssen Ihr Paradigma in Bezug auf die Anzeige von SQL-Code ändern. Aufgrund des festgelegten Charakters kann es nicht viele Funktionen bereitstellen, die Sprachen auf Anwendungsebene (Generika usw.) können. SQL wurde nie so konzipiert, es ist eine Sprache zum Abfragen von Datensätzen, und jeder Satz ist auf seine Weise einzigartig.
Abgesehen davon gibt es Möglichkeiten, wie Sie Ihren Code schöner aussehen lassen können, wenn die Lesbarkeit innerhalb der Organisation eine hohe Priorität hat. Speichern von Bits häufig verwendeter SQL-Blöcke (häufig verwendete Datensätze) in gespeicherten Prozeduren / Tabellenwertfunktionen und anschließendes Abfragen und Speichern in temporären Tabellen / Tabellenvariablen, gefolgt von deren Verwendung, um die Teile zu einer massiven Transaktion zusammenzufügen dass Sie sonst schreiben würden, ist eine Option. IMHO ist es nicht wert, so etwas mit SQL zu tun.
Als Sprache ist sie so konzipiert, dass sie für jeden, auch für Nicht-Programmierer, leicht lesbar und verständlich ist. Daher ist es nicht erforderlich, SQL-Code in kleinere Teile mit Byte-Größe umzugliedern, es sei denn, Sie tun etwas sehr Schlaues. Ich persönlich habe während der Arbeit an einer Data-Warehouse-ETL / Reporting-Lösung umfangreiche SQL-Abfragen geschrieben, und es war immer noch sehr klar, was los war. Alles, was für andere etwas seltsam ausgesehen hätte, würde eine kurze Reihe von Kommentaren erhalten, um eine kurze Erklärung zu liefern.
Ich hoffe das hilft.
quelle
Ich werde mich auf die "Unterabfragen" in Ihrem Beispiel konzentrieren.
Warum werden sie so oft benutzt? Weil sie die natürliche Denkweise einer Person anwenden: Ich habe diese Datenmenge und möchte eine Aktion auf einer Teilmenge davon ausführen und diese mit einer Teilmenge anderer Daten kombinieren. 9 von 10 Mal, wenn ich eine Unterabfrage sehe, wird sie falsch verwendet. Mein Witz über Unterabfragen ist: Leute, die Angst vor Joins haben, benutzen Unterabfragen.
Wenn Sie solche Unterabfragen sehen, ist dies oft auch ein Zeichen für ein nicht optimales Datenbankdesign.
Je normaler Ihre Datenbank ist, desto mehr Verknüpfungen erhalten Sie, je mehr Ihre Datenbank wie ein großes Excel-Arbeitsblatt aussieht, desto mehr Unterauswahlen erhalten Sie.
Refactoring in SQL hat oft ein anderes Ziel: mehr Leistung, bessere Abfragezeiten, "Vermeidung von Tabellenscans". Diese können den Code sogar weniger lesbar machen, sind aber sehr wertvoll.
Warum sehen Sie so viele riesige monolithische nicht refaktorierte Abfragen?
(Je erfahrener ich mit SQL bin, desto weniger werden meine Abfragen. SQL bietet Möglichkeiten für Menschen aller Könnensstufen, ihre Arbeit zu erledigen, egal was passiert.)
quelle
Aufgabentrennung
Im Sinne von SQL ist die Datenbank ein gemeinsam genutztes Asset, das die Unternehmensdaten enthält. Der Schutz dieser Daten ist von entscheidender Bedeutung. Betritt den DBA als Hüter des Tempels.
Das Erstellen einer neuen Ansicht in der Datenbank dient einem dauerhaften Zweck und wird von einer Benutzergemeinschaft geteilt. In der DBA-Sicht ist dies nur zulässig, wenn die Sicht durch die Struktur der Daten gerechtfertigt ist. Jede Änderung einer Ansicht ist dann mit Risiken für alle aktuellen Benutzer verbunden, auch für diejenigen, die die Anwendung nicht verwenden, aber die Ansicht entdeckt haben. Schließlich erfordert das Erstellen neuer Objekte das Verwalten von Berechtigungen und im Falle der Ansicht die Übereinstimmung mit den Berechtigungen der zugrunde liegenden Tabellen.
All dies erklärt, warum DBAs keine Views hinzufügen möchten, die nur für den Code einer einzelnen Anwendung gedacht sind.
SQL-Design
Wenn Sie eine Ihrer komplexen Abfragen zerlegen, stellen Sie möglicherweise fest, dass die Unterabfragen häufig einen Parameter benötigen, der von einer anderen Unterabfrage abhängt.
Das Transformieren von Unterabfragen in der Ansicht ist also nicht unbedingt so einfach wie angegeben. Sie müssen die variablen Parameter isolieren und Ihre Ansicht so gestalten, dass die Parameter als Auswahlkriterien zur Ansicht hinzugefügt werden können.
Leider müssen Sie dabei manchmal weniger effektiv und auf mehr Daten zugreifen als bei einer maßgeschneiderten Abfrage.
Proprietäre Erweiterungen
Sie können auf ein Refactoring hoffen, indem Sie einige Verantwortlichkeiten auf prozedurale Erweiterungen von SQL wie PL / SQL oder T-SQL übertragen. Diese sind jedoch herstellerabhängig und erzeugen eine technologische Abhängigkeit. Darüber hinaus werden diese Erweiterungen auf dem Datenbankserver ausgeführt, wodurch mehr Verarbeitungslast für eine Ressource entsteht, die viel schwieriger zu skalieren ist als ein Anwendungsserver.
Aber was ist das Problem am Ende?
Sind die Aufgabentrennung und das SQL-Design mit seinen Stärken und Einschränkungen ein echtes Problem? Letztendlich erwiesen sich diese Datenbanken als erfolgreich und zuverlässig für die Verarbeitung sehr kritischer Daten, auch in geschäftskritischen Umgebungen.
Um also ein erfolgreiches Refactoring zu erreichen:
Überlegen Sie sich eine bessere Kommunikation . Versuchen Sie, die Einschränkungen Ihres DBAs zu verstehen. Wenn Sie einem DBA beweisen, dass eine neue Sicht durch die Datenstrukturen gerechtfertigt ist, dass es sich nicht um eine Wegwerfumgehung handelt und dass sie keine Auswirkungen auf die Sicherheit hat, wird er / sie die Erstellung dieser Sicht auf jeden Fall zulassen. Denn dann wäre es ein gemeinsames Interesse.
Machen Sie zuerst Ihr eigenes Haus sauber : Nichts zwingt Sie dazu, an vielen Orten eine Menge SQL zu generieren. Überarbeiten Sie Ihren Anwendungscode, um die SQL-Zugriffe zu isolieren und die Klassen oder Funktionen zu erstellen, um wiederverwendbare Unterabfragen bereitzustellen, wenn diese häufig verwendet werden.
Verbesserung des Teambewusstseins : Stellen Sie sicher, dass Ihre Anwendung keine Aufgaben ausführt, die von der DBMS-Engine effizienter ausgeführt werden könnten. Wie Sie zu Recht betont haben, werden der prozedurale Ansatz und der datenorientierte Ansatz von verschiedenen Teammitgliedern nicht gleichermaßen beherrscht. Das hängt von ihrem Hintergrund ab. Um das System als Ganzes zu optimieren, muss Ihr Team es als Ganzes verstehen. Schaffen Sie also ein Bewusstsein, um sicherzustellen, dass weniger erfahrene Spieler das Rad nicht neu erfinden und ihre DB-Gedanken mit erfahreneren Mitgliedern teilen.
quelle
Zu Punkt 1 & 3: Ansichten sind nicht der einzige Weg. Es gibt auch temporäre Tabellen, Marts, Tabellenvariablen, aggregierte Spalten, CTEs, Funktionen, gespeicherte Prozeduren und möglicherweise andere Konstrukte, die vom RDBMS abhängen.
DBAs (und ich spreche von jemandem, der sowohl DBA als auch Entwickler war) neigen dazu, die Welt auf ziemlich binäre Weise zu betrachten, und sind daher aufgrund der wahrgenommenen Leistungsbeeinträchtigung oft gegen Ansichten und Funktionen.
In letzter Zeit hat sich der Bedarf an komplexen Verknüpfungen mit der Erkenntnis verringert, dass denormalisierte Tabellen, obwohl sie aus NF- Sicht nicht optimal sind, eine hohe Leistung erbringen.
Es gibt auch den Trend, Abfragen clientseitig mit Technologien wie LINQ durchzuführen, die Sie in Punkt 2 ansprechen .
Obwohl ich der Meinung bin, dass die Modularisierung von SQL schwierig sein kann, wurden große Fortschritte erzielt, obwohl es immer eine Zweiteilung zwischen clientseitigem Code und SQL geben wird - obwohl 4GL die Linien etwas verwischt hat.
Ich denke, es hängt wirklich davon ab, inwieweit Ihre Datenbankadministratoren / Architekten / Techniker bereit sind, diesbezüglich abzutreten. Wenn sie sich weigern, etwas anderes als Vanilla SQL mit vielen Verknüpfungen zuzulassen, können sich große Abfragen ergeben. Wenn Sie damit nicht weiterkommen, stoßen Sie Ihren Kopf nicht gegen eine Mauer, sondern eskalieren Sie sie. Es gibt im Allgemeinen bessere Möglichkeiten, Dinge mit ein wenig Kompromiss zu tun - insbesondere, wenn Sie die Vorteile nachweisen können.
quelle