2016-11-11 2 views
0

Nehmen Sie die folgenden zwei mock Tabellen:effiziente Alternative zu Unterabfragen in SQL Server

dbo.animal

id name 
1 dog 
2 cat 

dbo.animal_food

id animal_id food_id active 
1 1   4  1 
2 1   5  1 

I muss abfragen ag ainst animal mit mehreren Unterabfragen von animal_food basierend auf verschiedenen sortierten Ergebnissen. So etwas wie das:

Dies ist offensichtlich sehr ineffizient --- Ich muss es beschleunigen. Ich kann jedoch nicht herausfinden, wie ich dies in einen Join umwandeln könnte, der die Leistung verbessern würde.

+1

Für Fragen zu Leistung von Abfragen besser abschneidet es ist eine gute Idee, einen tatsächlichen Ausführungsplan aufzunehmen. Sie sagen * "offensichtlich sehr ineffizient" *, aber der Abfrageoptimierer könnte etwas kochen, das * effizient * (oder zumindest effizient genug) ist. –

Antwort

0

Ich bin mir nicht sicher über die Leistung, aber hier ist die Abfrage mit Joins und Aggregaten. Ist das wonach Sie suchen oder haben Sie das schon probiert?

select animal.name 
    , max(animal_food_all.food_id) as max_food_id 
    , max(animal_food_active.food_id) as max_active_food_id 
from animal 
    left outer join animal_food as animal_food_all on animal.id = animal_food_all.animal_id 
    left outer join animal_food as animal_food_active on animal.id = animal_food_active.animal_id and animal_food_active.active = 1 
GROUP BY animal.name 

Ich benutzte Außen im Falle tritt es gibt Tiere, die ein Lebensmittel nicht aufgeführt haben, wenn Sie diejenigen weggelassen möchten, können Sie es innere ändern kommen aber so oder so wird es wahrscheinlich wenig (wenn überhaupt) Auswirkungen auf die Leistung .

+1

wäre nicht ein einziger Scan von animal_food vorzuziehen? – SlimsGhost

+0

@SlimsGhost - Es kann von den Daten und vorhandenen Indizes abhängen, aber Sie haben wahrscheinlich Recht. Das Beste, was Sie tun müssen, ist, Ihre Antwort und die meiner Meinung nach zu nehmen, was das OP hat und in SSMS zu benchmarken und die Ausführungspläne zu vergleichen. – Igor

1

Wenn Sie nur die zwei food_id Werte wollen Sie angegeben haben, können Sie join mit group by verwenden und was heißt bedingte Aggregation, wie folgt aus:

select 
    name, 
    max(animal_food.food_id) as max_food_id, 
    max(case when animal_food.active = 1 then animal_food.food_id else null end) as max_active_food_id, 
from animal 
inner join animal_food on animal.animal_id = animal_food.animal_id 
group by animal.animal_id, animal.name 
+0

Danke. Ich probiere das gerade aus. – dthree

+0

Dies verlangsamte die Abfrage nach unten :( – dthree

+0

Haben Sie den EXPLAIN-Plan für beide Abfragen überprüft ???? Das ist der einzige wirkliche Weg, um sicher zu wissen. Wenn für Ihre ursprüngliche Abfrage ist schneller als diese, vielleicht Ihre Prämisse von "das ist offensichtlich sehr ineffizient "sollte nachgewiesen werden ... – SlimsGhost

1

Das sieht aus wie eine einfache Aggregatabfrage, mit der einzigen Twist ist, dass die dritte Spalte nur die maximale food_id wo active = 1 berücksichtigt. Wenn das der Fall ist, wird dies den Job ohne Subqueries:

SELECT 
    name, 
    MAX(food_id) AS max_food_id, 
    MAX(CASE WHEN active = 1 THEN food_id END) AS max_active_food_id 
FROM animal 
JOIN animal_food = animal.id = animal_food.id 
GROUP BY name 

Die MAX(CASE WHEN active = 1 THEN food_id END) kehrt NULL wenn active nicht gleich 1, und Nullen werden für Aggregate wie MAX ignoriert.

1

Sie können auch Quer anwenden verwenden Funktion und prüfen, welche Art und Weise

select name, 
     max_food_id.food_id AS max_food_id, 
     max_active_food_id.food_id AS max_active_food_id, 
    from animal 
    cross apply (
    select top 1 food_id 
       from animal_food 
      where animal_id = animal.id 
      order by food_id desc 
) AS max_food_id 
cross apply 
( select top 1 food_id 
       from animal_food 
      where animal_id = animal.id 
       and active = 1 
      order by food_id desc) as max_active_food_id 
Verwandte Themen