Benutzer mit den meisten Antworten
Suche einen Tipp zur Optimierung eine SQL abfrage

Frage
-
Hallo,
ich brauche mal eure Hilfe. Es geht um eine Abfrage, die sich Werte aus eine anderen Abfrage holt um die in einer CASE WHEN zu verwenden.
Hier mal ein einfacher Testaufbau:
--Test Tabellen erstellen create table dbo.Tabelle_A (ArtikelNr int primary Key) create table dbo.Tabelle_B (ArtikelNr int, Preis smallmoney, primary Key(ArtikelNr)) --Abfrage select *, case when (select top 1 Preis from Tabelle_B b where b.ArtikelNr=a.ArtikelNr) > 0 then (select top 1 Preis from Tabelle_B b where b.ArtikelNr=a.ArtikelNr) else 999 end from Tabelle_A a
Gehen wir mal davon aus, dass die Tabelle_A die Artikel enthält. Über eine deutlich komplexere Struktur müssen jetzt aus Tabelle_B die Preise ermittelt werden. Vorweg: Die Struktur ist sehr komplex. JOINs sind nicht möglich, da es 0 bis x Möglichkeiten gibt.
Dazu wird eine Unterabfrage gemacht. Diese ermittelt den Preis. Wenn der Preis gültig ist, wird der Preis neu ermittelt und in der Abfrage zurückgegeben - sonst der Listenpreis. So wie ich das in der CASE WHEN abfrage geschrieben habe, wird die Unterabfrage aber zweimal ausgeführt (s.Ablaufplan). Das ist bei so großen, sehr komplexen Abfragen ein Problem.
Die Frage ist also, ob ich die Abfrage irgendwie so ändern kann, dass ich den Wert aus Tabelle_B nur einmal abfragen muss.
In einer gespeicherten Prozedure würde ich das über eine Variable machen... Die Frage ist ob das in einer Abfrage auch geht?
- Bearbeitet Joachim Siebert Donnerstag, 7. Januar 2016 17:26
Antworten
-
Hallo Joachim,
verwende doch eine CTE vorab. Hier ein Beispiel dazu. Das Statement mit CTE macht nur halb so viel IO wie das erste. In der CTE kannst Du auch sehr übersichtlich Deine komplexeren Abfragen unterbringen.--Test Tabellen erstellen create table dbo.Tabelle_A (ArtikelNr int primary Key) create table dbo.Tabelle_B (ArtikelNr int, Preis smallmoney, primary Key(ArtikelNr)) go Insert into Tabelle_A(ArtikelNr) values (1), (2), (3); Insert into Tabelle_B(ArtikelNr, Preis) values (1, 10), (2, 20), (3, 30); Set Statistics IO ON; --Abfrage select *, case when (select top 1 Preis from Tabelle_B b where b.ArtikelNr=a.ArtikelNr) > 0 then (select top 1 Preis from Tabelle_B b where b.ArtikelNr=a.ArtikelNr) else 999 end from Tabelle_A a ; -- CTE With Vorberechnung as (select Preis, ArtikelNr from Tabelle_B ) select *, case when b.Preis > 0 then b.Preis else 999 end as MeinPreis from Tabelle_A a Inner Join Vorberechnung b on a.ArtikelNr = b.ArtikelNr ; go drop table dbo.Tabelle_A; drop table dbo.Tabelle_B;
Einen schönen Tag noch,
Christoph
--
Data Platform MVP - http://www.insidesql.org/blogs/cmu- Als Antwort markiert Joachim Siebert Freitag, 8. Januar 2016 11:34
Alle Antworten
-
Hallo Joachim,
ich sehe nicht ganz, warum die Struktur von Tabelle B zu komplex für einen Join sein soll. Du müsstest die Query für deine Case-When auch formulieren.
Warum kannst Du das vereinfachte Resultset so wie in der Abfrage oben nicht anjoinen?
Aber okay, gehen wir mal davon aus, dass Joinen wirklich nicht geht. Du hast oben eine "TOP" clause ohne order by verwendet, bei der Möglichkeit von X Preisen, wie stellst du sicher, dass es der richtige Preis ist, der ermittelt wird?Du kannst auch in Abfragen Variblen benutzen. Wenn du eine Lösung hast, um es in einer Procedure zu lösen, schreib die Prozedure und lass das "Create Procedure as" weg....
Vielleicht vereinfache ich das Thema jetzt auch zu sehr. Wenn du dir eine Logik überlegen kannst, um eine Variable mit (genau) einem Wert zu füllen, denke ich, dass dieses Query, was den Wert ermittelt, auch als join funktionieren müsste:
--Abfrage select *, case when b.Preis > 0 then b.Preis else 999 end from Tabelle_A a join (select top 1 Preis from Tabelle_B) b on b.ArtikelNr=a.ArtikelNr
- Bearbeitet Stefan Grigat Donnerstag, 7. Januar 2016 19:26
-
Hallo Joachim,
verwende doch eine CTE vorab. Hier ein Beispiel dazu. Das Statement mit CTE macht nur halb so viel IO wie das erste. In der CTE kannst Du auch sehr übersichtlich Deine komplexeren Abfragen unterbringen.--Test Tabellen erstellen create table dbo.Tabelle_A (ArtikelNr int primary Key) create table dbo.Tabelle_B (ArtikelNr int, Preis smallmoney, primary Key(ArtikelNr)) go Insert into Tabelle_A(ArtikelNr) values (1), (2), (3); Insert into Tabelle_B(ArtikelNr, Preis) values (1, 10), (2, 20), (3, 30); Set Statistics IO ON; --Abfrage select *, case when (select top 1 Preis from Tabelle_B b where b.ArtikelNr=a.ArtikelNr) > 0 then (select top 1 Preis from Tabelle_B b where b.ArtikelNr=a.ArtikelNr) else 999 end from Tabelle_A a ; -- CTE With Vorberechnung as (select Preis, ArtikelNr from Tabelle_B ) select *, case when b.Preis > 0 then b.Preis else 999 end as MeinPreis from Tabelle_A a Inner Join Vorberechnung b on a.ArtikelNr = b.ArtikelNr ; go drop table dbo.Tabelle_A; drop table dbo.Tabelle_B;
Einen schönen Tag noch,
Christoph
--
Data Platform MVP - http://www.insidesql.org/blogs/cmu- Als Antwort markiert Joachim Siebert Freitag, 8. Januar 2016 11:34
-
Hallo Stefan,
den Gedanken hatte ich auch schon aber eine Abfrage über den Join ist nur dann möglich, wenn die Werte die Du abfragst im ResultSet enthalten sind.
Im Test von Deinem Beispiel bekomme ich:
select *, case when b.Preis > 0 then b.Preis else 999 end from Tabelle_A a join (select top 1 Preis from Tabelle_B) b on b.ArtikelNr=a.ArtikelNr
Den FehlerUngültiger Spaltenname 'ArtikelNr'.
das ist auch Logisch ... wie gesagt, ist die eigentliche Abfrage viel komplizierter. Es geht mir um die generelle Vorgehensweise ...