Sql server SQL Server中子查询的有效替代方案

Sql server SQL Server中子查询的有效替代方案,sql-server,tsql,join,subquery,query-performance,Sql Server,Tsql,Join,Subquery,Query Performance,以以下两个模拟表为例: 动物 动物性食物 我需要根据不同的排序结果,使用多个子查询animal\u 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,

以以下两个模拟表为例:

动物

动物性食物

我需要根据不同的排序结果,使用多个子查询
animal\u 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
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

这显然效率很低——我需要加快速度。但是,我不知道如何将其重构为一个连接以提高性能。

我不确定性能,但下面是使用连接和聚合的查询。这是你想要的还是你已经试过了

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

如果有动物没有列出食物,我会使用外部联接,如果你想省略这些,你可以将其更改为内部联接,但无论哪种方式,它都可能对性能几乎没有影响。

我不确定性能,但下面是使用联接和聚合的查询。这是你想要的还是你已经试过了

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

如果有动物没有列出食物,我会使用外部连接,如果你想省略这些,你可以更改它的内部连接,但无论哪种方式,它都可能对性能几乎没有影响。

如果你只想要指定的两个食物id值,您可以使用
join
groupby
以及所谓的条件聚合,如下所示:

 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
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

如果您只需要指定的两个食物id值,可以使用
join
groupby
以及所谓的条件聚合,如下所示:

 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
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

这看起来像一个简单的聚合查询,唯一的问题是第三列只考虑max
food\u 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

如果
active
不等于1,则
MAX(如果active=1,则食物id结束)
将返回
NULL
,并且对于像
MAX

这样的聚合将忽略NULL。这看起来像一个简单的聚合查询,唯一的问题是第三列只考虑最大
食物id
,其中
活动=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

如果
active
不等于1,则
MAX(如果active=1,则食物id结束)
将返回
NULL
,并且对于像
MAX

这样的聚合,将忽略空值。您还可以使用交叉应用函数,并检查哪种方式性能更好

 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

您还可以使用交叉应用功能并检查哪种方式性能更好

 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

对于有关查询性能的问题,最好包含一个实际的执行计划。您的意思是“显然非常低效”,但查询优化器可能会编造一些高效的东西(或者至少足够高效)。对于查询性能的问题,最好包含一个实际的执行计划。你说的是“显然非常低效”,但查询优化器可能会编造一些高效的东西(或者至少足够高效)。对动物食物进行一次扫描不是更好吗?@SlimsGhost-这可能取决于数据和现有索引,但你可能是对的。最好的办法是把你的答案和我的答案,以及OP的内容,在SSMS中做基准点,并比较执行计划。对动物食物进行一次扫描不是更好吗?@SlimsGhost-这可能取决于数据和现有索引,但你可能是对的。最好的办法是将你的答案和我的答案以及OP的内容记录在SSMS中,并将它们作为基准点,同时比较执行计划。这会减慢查询速度:(你是否检查了这两个查询的解释计划???这是唯一可以确定的真正方法。如果你最初的查询速度比这一个快,也许你的前提是“这显然是非常低效的”应该被证明…我的印象是子查询一般都比较慢。我想我可能是错的。这减慢了查询的速度:(你有没有检查两个查询的解释计划???这是唯一可以确定的真实方法。如果你的原始查询比这个查询快,也许你的前提“这显然是非常低效的”应该被证明…我的印象是子查询一般都慢。我想我可能是错的。