Dies ist ein Thema, über das ich in der Vergangenheit stundenlang recherchiert habe. Es scheint mir etwas zu sein, das von modernen RDBMS- Lösungen hätte angegangen werden sollen , aber ich habe noch nichts gefunden, das wirklich das anspricht, was ich als unglaublich häufig in einer Web- oder Windows-Anwendung mit einem Datenbank-Back-End betrachte.
Ich spreche von dynamischer Sortierung. In meiner Fantasiewelt sollte es so einfach sein wie:
ORDER BY @sortCol1, @sortCol2
Dies ist das kanonische Beispiel, das von neuen SQL- und Stored Procedure- Entwicklern in allen Foren im Internet gegeben wurde. "Warum ist das nicht möglich?" Sie Fragen. Ausnahmslos kommt irgendwann jemand vorbei, um sie über die Kompilierung gespeicherter Prozeduren, Ausführungspläne im Allgemeinen und viele andere Gründe zu belehren, warum es nicht möglich ist, einen Parameter direkt in eine ORDER BY
Klausel einzufügen.
Ich weiß, was einige von Ihnen bereits denken: "Dann lassen Sie den Kunden die Sortierung durchführen." Dies entlastet natürlich die Arbeit aus Ihrer Datenbank. In unserem Fall sind unsere Datenbankserver jedoch in 99% der Fälle nicht einmal ins Schwitzen geraten, und sie sind noch nicht einmal mehrkernig oder eine der anderen unzähligen Verbesserungen der Systemarchitektur, die alle 6 Monate stattfinden. Allein aus diesem Grund wäre es kein Problem, wenn unsere Datenbanken die Sortierung übernehmen. Darüber hinaus sind Datenbanken sehrgut sortieren. Sie sind dafür optimiert und hatten Jahre Zeit, um es richtig zu machen. Die Sprache dafür ist unglaublich flexibel, intuitiv und einfach. Vor allem jeder SQL-Anfänger weiß, wie es geht, und was noch wichtiger ist, er weiß, wie man es bearbeitet. Nehmen Sie Änderungen vor, führen Sie Wartungsarbeiten durch usw. Wenn Ihre Datenbanken weit davon entfernt sind, besteuert zu werden, und Sie nur die Entwicklungszeit vereinfachen (und verkürzen!) möchten, scheint dies eine naheliegende Wahl zu sein.
Dann gibt es das Webproblem. Ich habe mit JavaScript herumgespielt, das das clientseitige Sortieren von HTML-Tabellen ermöglicht, aber sie sind unweigerlich nicht flexibel genug für meine Anforderungen, und da meine Datenbanken nicht übermäßig besteuert sind und das Sortieren wirklich sehr einfach durchführen können, bin ich es auch Es fällt mir schwer, die Zeit zu rechtfertigen, die erforderlich wäre, um meinen eigenen JavaScript-Sortierer neu zu schreiben oder zu rollen. Das Gleiche gilt im Allgemeinen für die serverseitige Sortierung, obwohl sie wahrscheinlich bereits JavaScript vorgezogen wird. Ich bin nicht einer, der den Overhead von DataSets besonders mag, also verklagen Sie mich.
Dies bringt jedoch den Punkt zurück, dass es nicht möglich ist - oder vielmehr nicht einfach. Ich habe mit früheren Systemen einen unglaublich hackigen Weg gefunden, um eine dynamische Sortierung zu erreichen. Es war weder hübsch noch intuitiv, einfach oder flexibel, und ein Anfänger-SQL-Writer würde innerhalb von Sekunden verloren gehen. Dies scheint bereits weniger eine "Lösung" als vielmehr eine "Komplikation" zu sein.
Die folgenden Beispiele sollen weder Best Practices noch einen guten Codierungsstil oder ähnliches aufzeigen, noch zeigen sie meine Fähigkeiten als T-SQL-Programmierer an. Sie sind das, was sie sind, und ich gebe voll und ganz zu, dass sie verwirrend sind, eine schlechte Form haben und einfach nur hacken.
Wir übergeben einen ganzzahligen Wert als Parameter an eine gespeicherte Prozedur (nennen wir den Parameter nur "sortieren") und bestimmen daraus eine Reihe anderer Variablen. Zum Beispiel ... Nehmen wir an, sort ist 1 (oder die Standardeinstellung):
DECLARE @sortCol1 AS varchar(20)
DECLARE @sortCol2 AS varchar(20)
DECLARE @dir1 AS varchar(20)
DECLARE @dir2 AS varchar(20)
DECLARE @col1 AS varchar(20)
DECLARE @col2 AS varchar(20)
SET @col1 = 'storagedatetime';
SET @col2 = 'vehicleid';
IF @sort = 1 -- Default sort.
BEGIN
SET @sortCol1 = @col1;
SET @dir1 = 'asc';
SET @sortCol2 = @col2;
SET @dir2 = 'asc';
END
ELSE IF @sort = 2 -- Reversed order default sort.
BEGIN
SET @sortCol1 = @col1;
SET @dir1 = 'desc';
SET @sortCol2 = @col2;
SET @dir2 = 'desc';
END
Sie können bereits sehen, wie ich, wenn ich mehr @ colX-Variablen deklariert habe, um andere Spalten zu definieren, mit den zu sortierenden Spalten basierend auf dem Wert von "sort" wirklich kreativ werden könnte ... um sie zu verwenden, sieht sie normalerweise wie folgt aus unglaublich unordentliche Klausel:
ORDER BY
CASE @dir1
WHEN 'desc' THEN
CASE @sortCol1
WHEN @col1 THEN [storagedatetime]
WHEN @col2 THEN [vehicleid]
END
END DESC,
CASE @dir1
WHEN 'asc' THEN
CASE @sortCol1
WHEN @col1 THEN [storagedatetime]
WHEN @col2 THEN [vehicleid]
END
END,
CASE @dir2
WHEN 'desc' THEN
CASE @sortCol2
WHEN @col1 THEN [storagedatetime]
WHEN @col2 THEN [vehicleid]
END
END DESC,
CASE @dir2
WHEN 'asc' THEN
CASE @sortCol2
WHEN @col1 THEN [storagedatetime]
WHEN @col2 THEN [vehicleid]
END
END
Offensichtlich ist dies ein sehr reduziertes Beispiel. Das eigentliche Zeug, da wir normalerweise vier oder fünf Spalten haben, um das Sortieren zu unterstützen, jede mit einer möglichen sekundären oder sogar einer dritten Spalte, um zusätzlich zu sortieren (zum Beispiel Datum absteigend, dann sekundär sortiert nach aufsteigendem Namen) und jede unterstützende Bi- Richtungssortierung, die die Anzahl der Fälle effektiv verdoppelt. Ja ... es wird sehr schnell haarig.
Die Idee ist, dass man die Sortierfälle "leicht" so ändern könnte, dass die Fahrzeug-ID vor der Speicherzeit sortiert wird ... aber die Pseudoflexibilität, zumindest in diesem einfachen Beispiel, endet wirklich dort. Im Wesentlichen wird in jedem Fall, in dem ein Test fehlschlägt (da unsere Sortiermethode diesmal nicht darauf angewendet wird), ein NULL-Wert ausgegeben. Und so erhalten Sie eine Klausel, die wie folgt funktioniert:
ORDER BY NULL DESC, NULL, [storagedatetime] DESC, blah blah
Du hast die Idee. Dies funktioniert, weil SQL Server Nullwerte in der Reihenfolge nach Klauseln effektiv ignoriert. Dies ist unglaublich schwer zu pflegen, wie wahrscheinlich jeder mit grundlegenden SQL-Kenntnissen sehen kann. Wenn ich einen von euch verloren habe, fühle dich nicht schlecht. Wir haben lange gebraucht, um es zum Laufen zu bringen, und wir sind immer noch verwirrt, wenn wir versuchen, es zu bearbeiten oder neue wie dieses zu erstellen. Zum Glück muss es nicht oft gewechselt werden, sonst würde es schnell "die Mühe nicht wert" werden.
Doch es tat Arbeit.
Meine Frage ist dann: Gibt es einen besseren Weg?
Ich bin mit anderen Lösungen als den gespeicherten Prozeduren einverstanden, da mir klar ist, dass dies möglicherweise nicht der richtige Weg ist. Am liebsten würde ich wissen, ob jemand es innerhalb der gespeicherten Prozedur besser machen kann, aber wenn nicht, wie gehen Sie alle damit um, dass der Benutzer Datentabellen mit ASP.NET dynamisch (auch bidirektional) sortiert?
Und danke, dass Sie eine so lange Frage gelesen (oder zumindest überflogen) haben!
PS: Seien Sie froh, dass ich mein Beispiel einer gespeicherten Prozedur nicht gezeigt habe, die dynamisches Sortieren, dynamisches Filtern / Textsuchen von Spalten, Paginierung über ROWNUMBER () OVER unterstützt, und versuchen Sie ... mit Transaktions-Rollbacking bei Fehlern zu fangen ... "gigantisch groß" beginnt nicht einmal, sie zu beschreiben.
Aktualisieren:
- Ich möchte dynamisches SQL vermeiden . Das Parsen eines Strings und das Ausführen einer EXEC darauf macht einen großen Teil des Zwecks zunichte, eine gespeicherte Prozedur überhaupt zu haben. Manchmal frage ich mich jedoch, ob sich die Nachteile eines solchen Vorgangs nicht lohnen würden, zumindest in diesen speziellen Fällen der dynamischen Sortierung. Trotzdem fühle ich mich immer schmutzig, wenn ich solche dynamischen SQL-Zeichenfolgen mache - als würde ich immer noch in der klassischen ASP-Welt leben.
- Viele der Gründe, warum wir gespeicherte Prozeduren überhaupt wollen, sind aus Sicherheitsgründen . Ich kann nicht auf Sicherheitsbedenken anrufen, sondern nur Lösungen vorschlagen. Mit SQL Server 2005 können wir Berechtigungen (bei Bedarf auf Benutzerbasis) auf Schemaebene für einzelne gespeicherte Prozeduren festlegen und dann alle Abfragen für die Tabellen direkt ablehnen. Die Vor- und Nachteile dieses Ansatzes zu kritisieren, ist vielleicht eine andere Frage, aber auch hier ist es nicht meine Entscheidung. Ich bin nur der Lead-Code-Affe. :) :)
quelle
Antworten:
Ja, es ist ein Schmerz und die Art, wie du es tust, sieht ähnlich aus wie das, was ich tue:
Für mich ist dies immer noch viel besser als das Erstellen von dynamischem SQL aus Code, was für DBAs zu einem Alptraum für Skalierbarkeit und Wartung wird.
Was ich aus Code mache, ist das Refactor des Paging und Sortierens, so dass ich dort zumindest nicht viele Wiederholungen mit dem Auffüllen von Werten für
@SortExpr
und habe@SortDir
.Behalten Sie in Bezug auf SQL das Design und die Formatierung zwischen verschiedenen gespeicherten Prozeduren bei, damit es zumindest ordentlich und erkennbar ist, wenn Sie Änderungen vornehmen.
quelle
Dieser Ansatz verhindert, dass die sortierbaren Spalten zweimal in der Reihenfolge von dupliziert werden, und ist IMO etwas besser lesbar:
quelle
Dynamisches SQL ist weiterhin eine Option. Sie müssen nur entscheiden, ob diese Option schmackhafter ist als die, die Sie derzeit haben.
Hier ist ein Artikel, der dies zeigt: http://www.4guysfromrolla.com/webtech/010704-1.shtml .
quelle
Meine Anwendungen tun dies häufig, aber sie alle bauen das SQL dynamisch auf. Wenn ich mich jedoch mit gespeicherten Prozeduren beschäftige, mache ich Folgendes:
select * from dbo.fn_myData() where ... order by ...
damit Sie dort die Sortierreihenfolge dynamisch angeben können.Dann befindet sich zumindest der dynamische Teil in Ihrer Anwendung, aber die Datenbank erledigt immer noch das schwere Heben.
quelle
Eine gespeicherte Prozedur (Hack?), Die ich verwendet habe, um dynamisches SQL für bestimmte Jobs zu vermeiden, besteht darin, eine eindeutige Sortierspalte zu haben. Dh
Dieser ist leicht zu übermitteln - Sie können Felder in Ihrer mySort-Spalte verknüpfen, die Reihenfolge mit mathematischen oder Datumsfunktionen umkehren usw.
Am besten verwende ich jedoch meine asp.net-Rasteransichten oder andere Objekte mit integrierter Sortierung, um die Sortierung für mich durchzuführen, nachdem ich die Daten von SQL-Server abgerufen habe. Oder auch wenn es nicht eingebaut ist - z. B. Datentabellen usw. in asp.net.
quelle
Es gibt verschiedene Möglichkeiten, wie Sie dies hacken können.
Voraussetzungen:
Dann in eine temporäre Tabelle einfügen:
Methode 2: Richten Sie einen Verbindungsserver wieder für sich selbst ein und wählen Sie ihn dann mit openquery aus: http://www.sommarskog.se/share_data.html#OPENQUERY
quelle
Es gibt möglicherweise eine dritte Option, da Ihr Server über viele freie Zyklen verfügt. Verwenden Sie eine Hilfsprozedur, um die Sortierung über eine temporäre Tabelle durchzuführen. Etwas wie
Vorsichtsmaßnahme: Ich habe dies nicht getestet, aber es sollte in SQL Server 2005 "funktionieren" (wodurch eine temporäre Tabelle aus einer Ergebnismenge erstellt wird, ohne die Spalten im Voraus anzugeben.)
quelle
Lohnt es sich nicht irgendwann, sich von gespeicherten Prozeduren zu entfernen und nur parametrisierte Abfragen zu verwenden, um diese Art von Hackery zu vermeiden?
quelle
Ich bin damit einverstanden, Client-Seite zu verwenden. Aber es scheint, dass dies nicht die Antwort ist, die Sie hören möchten.
Es ist also perfekt so wie es ist. Ich weiß nicht, warum Sie es ändern oder sogar fragen möchten: "Gibt es einen besseren Weg?" Eigentlich sollte es "The Way" heißen. Außerdem scheint es gut zu funktionieren und den Anforderungen des Projekts zu entsprechen und wird wahrscheinlich für die kommenden Jahre erweiterbar genug sein. Da Ihre Datenbanken nicht besteuert werden und das Sortieren wirklich sehr einfach ist , sollte dies auch in den kommenden Jahren so bleiben.
Ich würde es nicht schwitzen.
quelle
Wenn Sie sortierte Ergebnisse auslagern, ist dynamisches SQL eine gute Option. Wenn Sie in Bezug auf SQL-Injection paranoid sind, können Sie die Spaltennummern anstelle des Spaltennamens verwenden. Ich habe dies getan, bevor ich negative Werte für den Abstieg verwendet habe. Etwas wie das...
Dann müssen Sie nur noch sicherstellen, dass die Anzahl innerhalb von 1 bis # der Spalten liegt. Man könnte sogar diese zu einer Liste von Spaltennummern erweitern und analysiert , die in eine Tabelle von Ints eine Funktion wie mit dieser . Dann würden Sie die Order by-Klausel wie folgt erstellen ...
Ein Nachteil ist, dass Sie sich die Reihenfolge jeder Spalte auf der Clientseite merken müssen. Insbesondere, wenn Sie nicht alle Spalten oder in einer anderen Reihenfolge anzeigen. Wenn der Client sortieren möchte, ordnen Sie die Spaltennamen der Spaltenreihenfolge zu und generieren die Liste der Ints.
quelle
Ein Argument gegen die Sortierung auf der Clientseite sind große Datenmengen und Paginierung. Sobald Ihre Zeilenanzahl über das hinausgeht, was Sie leicht anzeigen können, sortieren Sie häufig als Teil eines Überspringens / Nehmens, das Sie wahrscheinlich in SQL ausführen möchten.
Für Entity Framework können Sie eine gespeicherte Prozedur verwenden, um Ihre Textsuche durchzuführen. Wenn Sie auf dasselbe Sortierproblem stoßen, besteht die Lösung darin, einen gespeicherten Prozess für die Suche zu verwenden und nur einen für die Übereinstimmung festgelegten ID-Schlüssel zurückzugeben. Als nächstes fragen Sie (mit der Sortierung) erneut nach der Datenbank ab, indem Sie die IDs in einer Liste (enthält) verwenden. EF handhabt dies ziemlich gut, selbst wenn der ID-Satz ziemlich groß ist. Ja, dies sind zwei Roundtrips, aber Sie können Ihre Sortierung immer in der Datenbank behalten, was in bestimmten Situationen wichtig sein kann, und verhindern, dass Sie eine Schiffsladung Logik in die gespeicherte Prozedur schreiben.
quelle
Wie wäre es mit der Sortierung nach dem Material, das die Ergebnisse anzeigt - Raster, Berichte usw. anstatt nach SQL?
BEARBEITEN:
Um die Dinge zu klären, da diese Antwort früher abgelehnt wurde, werde ich ein wenig näher darauf eingehen ...
Sie gaben an, dass Sie sich mit clientseitiger Sortierung auskennen, sich aber davon fernhalten wollten. Das ist natürlich dein Anruf.
Ich möchte jedoch darauf hinweisen, dass Sie auf der Clientseite EINMAL Daten abrufen und dann nach Belieben damit arbeiten können - anstatt jedes Mal mehrere Fahrten zum Server hin und her zu machen Die Sortierung wird geändert.
Ihr SQL Server wird momentan nicht besteuert und das ist großartig. Es sollte nicht sein. Aber nur weil es noch nicht überladen ist, heißt das nicht, dass es für immer so bleiben wird.
Wenn Sie eines der neueren ASP.NET-Elemente für die Anzeige im Web verwenden, ist ein Großteil dieses Materials bereits direkt eingebrannt.
Lohnt es sich, jeder gespeicherten Prozedur so viel Code hinzuzufügen, um die Sortierung zu übernehmen? Wieder dein Anruf.
Ich bin nicht derjenige, der letztendlich dafür verantwortlich sein wird, es zu unterstützen. Denken Sie jedoch darüber nach, worum es beim Hinzufügen / Entfernen von Spalten in den verschiedenen Datensätzen geht, die von den gespeicherten Prozeduren verwendet werden (Änderungen an den CASE-Anweisungen erforderlich), oder wenn der Benutzer plötzlich statt nach zwei Spalten zu sortieren, entscheidet, dass drei - erforderlich sind. Sie müssen jetzt alle gespeicherten Prozeduren aktualisieren, die diese Methode verwenden.
Für mich lohnt es sich, eine funktionierende clientseitige Lösung zu finden und sie auf die wenigen benutzerbezogenen Datenanzeigen anzuwenden und damit fertig zu sein. Wenn eine neue Spalte hinzugefügt wird, wird diese bereits behandelt. Wenn der Benutzer nach mehreren Spalten sortieren möchte, kann er nach zwei oder zwanzig Spalten sortieren.
quelle
Es tut mir leid, dass ich zu spät zur Party komme, aber hier ist eine weitere Option für diejenigen, die dynamisches SQL wirklich vermeiden möchten, aber die Flexibilität wünschen, die es bietet:
Anstatt das SQL im laufenden Betrieb dynamisch zu generieren, schreiben Sie Code, um für jede mögliche Variation einen eindeutigen Prozess zu generieren. Anschließend können Sie eine Methode in den Code schreiben, um die Suchoptionen anzuzeigen und den entsprechenden aufrufenden Prozess auszuwählen.
Wenn Sie nur wenige Variationen haben, können Sie die Prozesse einfach von Hand erstellen. Wenn Sie jedoch viele Variationen haben, müssen Sie nicht alle warten, sondern nur Ihren Proc-Generator warten, damit er sie neu erstellt.
Als zusätzlichen Vorteil erhalten Sie auch auf diese Weise bessere SQL-Pläne für eine bessere Leistung.
quelle
Diese Lösung funktioniert möglicherweise nur in .NET, ich weiß es nicht.
Ich rufe die Daten mit der anfänglichen Sortierreihenfolge in der SQL-Reihenfolge nach Klausel in das C # ab, füge diese Daten in eine DataView ein, speichere sie in einer Sitzungsvariablen zwischen und erstelle damit eine Seite.
Wenn der Benutzer auf eine Spaltenüberschrift klickt, um sie zu sortieren (oder eine Seite oder einen Filter), gehe ich nicht zurück zur Datenbank. Stattdessen kehre ich zu meiner zwischengespeicherten DataView zurück und setze die Eigenschaft "Sortieren" auf einen Ausdruck, den ich dynamisch erstelle, genau wie ich es mit dynamischem SQL tun würde. (Ich filtere auf die gleiche Weise mit der Eigenschaft "RowFilter").
Sie können es in einer Demo meiner App BugTracker.NET unter http://ifdefined.com/btnet/bugs.aspx sehen / fühlen
quelle
Sie sollten die SQL Server-Sortierung vermeiden, sofern dies nicht erforderlich ist. Warum nicht auf App-Server- oder Client-Seite sortieren? Auch .NET Generics führt außergewöhnliche Sortierungen durch
quelle