2016-11-11 52 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