Wie würden Sie diese Abfrage schreiben?
-
20-08-2019 - |
Frage
Ich suche die unten stehende Abfrage etwas besser lesbar und modifizierbar Refactoring. Die erste Hälfte ist identisch mit dem zweiten, mit Ausnahme der Datenbank abgefragt aus (Tabellennamen gleich sind though.)
SELECT
Column 1 AS c1,
...
Column N AS cN
FROM
database1.dbo.Table1
UNION
SELECT
'Some String' as c1,
...
NULL as cN
FROM
database1.dbo.Table2
UNION
SELECT
Column 1 AS c1,
...
Column N AS cN
FROM
database2.dbo.Table1
UNION
SELECT
'Some String' as c1,
...
NULL as cN
FROM
database2.dbo.Table2
Diese Abfrage ist die Definition von DRY und ruft mich werden wieder -written, aber ich habe keine Ahnung, wie es geht!
Bearbeiten : Wir nicht Linq verwenden können, und wir wünschen unterschiedliche Ergebnisse; Ich suche die Abfrage kleiner in physischer Dateigröße, nicht in Treffer zu machen.
Bearbeiten : Die Datenbank, die ich Abfragen aus bin ist eine proprietäre ERP-Datenbank. Restrukturierung ist keine Option.
Lösung
Das ist ein ziemlich Standard-SQL-Muster. Manchmal ist es einfach OOP / Procedural Code Prinzipien wie DRY zu SQL inadvisedly zu übertragen, aber sie sind nicht unbedingt übertragbar Konzepte.
Beachten Sie, wie leicht Sie die gesamte logische Gestaltung der Abfrage grok, gegen die Jagd durch Submodule. Wenn einer der Teilausdrücke eine zusätzliche Spalte hat, oder Spalten umgekehrt, würde es durchhalten. Dies ist im Grunde eine ziemlich einfache SQL-Anweisung als eine Ausführungseinheit grok, wo Aufschließschritt es wäre es durcheinander.
Und wenn Sie das Debuggen, ist es praktisch, den Text des Editor hervorgehoben Option verwenden zu können, Teile der Anweisung selektiv auszuüben - eine Technik, die nicht in prozeduralen Code vorhanden ist. OTOH, kann es chaotisch zu versuchen, die Stücke alle aufzuspüren, wenn sie in Ansichten verstreut sind etc. Auch CTEs kann diese unbequem machen.
Andere Tipps
Ich werde auf einem Glied hier gehen und sagen: auf der Grundlage der Informationen, die Sie uns gegeben haben;
Das ist so gut wie es geht zu erhalten
Eine Leistungsspitze, die ich von der Fledermaus zu sehen ist, mit UNION ALL
statt UNION
es sei denn, Sie absichtlich verschiedene Datensätze wollen. Eine einfache UNION
Duplikate eliminieren, die Zeit in Anspruch nimmt. UNION ALL
macht das nicht.
Sie könnte es umschreiben mit dynamischem SQL und einer Schleife, aber ich denke, das Ergebnis schlechter wäre. Wenn es genug doppelte Code ist der dynamischen SQL-Ansatz zu rechtfertigen, dann denke ich, es gerechtfertigt sein könnte.
Alternativ haben Sie darüber nachgedacht, die Logik aus der gespeicherten Prozedur in so etwas wie LINQ zu verschieben? Für viele ist dies keine Option, so ich frage nur.
Eine letzte Bemerkung: der Drang widerstehen zu beheben, was nur kaputt ist nicht es sauberer aussehen zu lassen. Wenn die Bereinigung aide in Wartung, Überprüfung usw., dann gehen sie.
Was ist das Problem? Zu lang? Zu repetitiv?
Manchmal hat man hässlich SQL - nicht viel können Sie dagegen tun können.
Ich sehe keine Möglichkeit, es zu bereinigen, wenn Sie separate Ansichten verwenden möchten, und dann Vereinigung sie zusammen.
Ich stimme für Ansichten, die in der Nähe genug, um ohne Overhead verhängen (Kosten OK, vielleicht eine kleine Compile-Zeit, aber das sollte alles sein). Dann wird Ihr Procs wird etwas von der Form
SELECT * FROM database1.view1
UNION
SELECT * FROM database1.view2
UNION
SELECT * FROM database2.view1
UNION
SELECT * FROM database2.view2
Ich bin mir nicht sicher, ob ich es noch weiter verdichten wollen würde, obwohl ich die meisten Plattformen erwarten würden toleriert es.
Auf dem dynamischen SQL-Thema - hier ist ein Beispiel - nicht sicher, ob es nicht besser ist. Der Vorteil ist, man muss nur einmal die SELECT-Liste schreiben.
DECLARE @Select1 varchar(1000)
DECLARE @Select2 varchar(1000)
DECLARE @SQL varchar(4000)
SET @Select1 = 'SELECT
Column 1 AS c1,
...
Column N AS cN'
SET @Select2 = 'SELECT
''Some String'' as c1,
...
NULL as cN'
SET @SQL = @Select1 + ' FROM database1.dbo.Table1 '
SET @SQL = @SQL + ' UNION ' + @Select2 + ' FROM database1.dbo.Table2 '
SET @SQL = @SQL + ' UNION ' + @Select1 + ' FROM database2.dbo.Table1 '
SET @SQL = @SQL + ' UNION ' + @Select2 + ' FROM database2.dbo.Table2 '
EXEC @SQL
Wenn alle Ihre Procs so aussehen - Sie haben wahrscheinlich ein architektonisches Problem
.Sind alle Ihre Anrufe nur table2 ein Nutzfeld haben? (Und wegen der UNION, nur am Ende einer Zeile mit?)
ich total zweite die Idee mit parametrisierte dynamischer SQL und / oder Code-Generierung für diesen Job geht, sogar so weit geht die Spaltenliste dynamisch mit INFORMATION_SCHEMA
zu erzeugen. Dies ist nicht genau das, was Sie brauchen, aber es ist ein Anfang (man könnte eine Tabelle von Datenbanken und Tabellen erzeugt aus):
DECLARE @template AS varchar(MAX)
SET @template = 'SELECT {@column_list} FROM {@database_name}.dbo.{@table_name}'
DECLARE @column_list AS varchar(MAX)
SELECT @column_list = COALESCE(@column_list + ',', '') + COLUMN_NAME
FROM database1.dbo.INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = @table_name
ORDER BY ORDINAL_POSITION
DECLARE @sql AS varchar(MAX)
SET @sql = @template
SET @sql = REPLACE(@sql, '{@column_list}', @column_list)
SET @sql = REPLACE(@sql, '{@database_name}', @database_name)
SET @sql = REPLACE(@sql, '{@table_name}', @table_name)
Abhängig von der Anzahl der zurückgegebenen Zeilen, können Sie am besten UNION mit ALL auf der wählt mit einer ausgewählten verschiedene Abfrage um ihn herum. Ich habe vor ein ähnliches Problem gesehen und hatten unterschiedliche Ausführungspläne für die beiden unterschiedlichen Arten
SELECT DISTINCT subquery.c1, subquery.cN FROM ( SELECT Column 1 AS c1, Column N AS cN FROM database1.dbo.Table1 UNION ALL SELECT 'Some String' as c1, NULL as cN FROM database1.dbo.Table2 UNION ALL SELECT Column 1 AS c1, Column N AS cN FROM database2.dbo.Table1 UNION ALL SELECT 'Some String' as c1, NULL as cN FROM database2.dbo.Table2 ) subquery