2016-11-11 68 views
0

採取以下兩個模擬表:有效的替代子查詢的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 

我需要查詢ag根據不同的排序結果,根據animal_food的多個子查詢,執行animal。像這樣的事情:

select name, 
     ( select top 1 food_id 
       from animal_food 
      where animal_id = animal.id 
      order by food_id desc) as max_food_id, 
     ( 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, 
    from animal 

這顯然非常低效---我需要加快速度。但是,我無法弄清楚如何將此重構爲可提高性能的聯接。

+1

有關查詢的性能問題,這是一個好主意,包括實際的執行計劃。你說*「顯然非常低效」*但查詢優化器可能會做一些*效率高(至少足夠高效)的東西。 –

回答

0

我不確定性能,但這裏是使用連接和聚合的查詢。這是你正在尋找或者你已經嘗試過嗎?

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 

我使用外連接的情況下,有沒有一種食品上市的動物,如果你想那些省略你可以改變它內在的加入,但無論哪種方式,它可能對性能幾乎沒有(如果有的話)的影響。

+1

不會對animal_food進行單次掃描更好嗎? – SlimsGhost

+0

@SlimsGhost - 它可能取決於數據和現有的索引,但你可能是正確的。最好的辦法是將你的答案和我的答案以及OP的內容和SSMS中的基準標記在一起,並比較執行計劃。 – Igor

1

如果你只是想讓你指定的兩個food_id值,你可以使用joingroup by,什麼叫做有條件聚集,像這樣:

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

謝謝。我現在正在嘗試這個。 – dthree

+0

這會減慢查詢速度:( – dthree

+0

您是否檢查了兩個查詢的解釋計劃?這是唯一真正知道的方法。如果您的原始查詢比這個查詢更快,也許您的前提是「這是顯然非常低效「應該被證明出來... – SlimsGhost

1

這看起來像一個簡單的彙總查詢,唯一的扭曲的是,第三欄只考慮最大food_id其中active = 1。如果是這樣的話,這將做的工作沒有子查詢:

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 

MAX(CASE WHEN active = 1 THEN food_id END)將返回NULL如果active不等於1,並且將空值像MAX聚集忽略。

1

您還可以使用跨應用功能,並檢查其執行方式更好

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