如何避免重复冗长的相关子查询?

Posted

技术标签:

【中文标题】如何避免重复冗长的相关子查询?【英文标题】:How to avoid repeating a lengthy CORRELATED subquery? 【发布时间】:2009-08-05 19:27:34 【问题描述】:

这是帮助您理解我的问题的代码:

create table con ( content_id number);
create table mat ( material_id number, content_id number, resolution number, file_location varchar2(50), file_size number);
create table con_groups (content_group_id number, content_id number);

insert into con values (99);
insert into mat values (1, 99, 7, 'C:\foo.jpg', 1024);
insert into mat values (2, 99, 2, '\\server\xyz.mov', 350000);
insert into mat values (3, 99, 5, '\\server2\xyz.wav', 175000);
insert into con values (100);
insert into mat values (4, 100, 5, 'C:\bar.png', 2048);
insert into mat values (5, 100, 3, '\\server\xyz.mov', 27400);
insert into mat values (6, 100, 7, '\\server2\xyz.wav', 400);

insert into con_groups values (10, 99);
insert into con_groups values (10, 100);

SELECT m.material_id, 
       m.content_id,
       (SELECT max(file_location) keep (dense_rank first order by resolution desc)                  
          FROM mat
         WHERE mat.content_id = m.content_id
        /* AND ...
           AND ...
           AND ... */) special_mat_file_location,
       (SELECT max(file_size) keep (dense_rank first order by resolution desc)
          FROM mat
         WHERE mat.content_id = m.content_id
        /* AND ...
           AND ...
           AND ... */) special_mat_file_size
  FROM mat m
 WHERE m.material_id IN (select material_id
                           from mat
                     inner join con on con.content_id = mat.content_id
                     inner join con_groups on con_groups.content_id = con.content_id
                          where con_groups.content_group_id = 10);

我将注释 AND 用于强调这是一个简化的示例;我的真实查询中的子查询更复杂,条件更多。

我的问题是:我想避免在子查询中为两列 (file_location and file_size) 重复所有条件,因为条件完全相同。我很乐意使用公用表表达式(即使用 WITH 子句的子查询分解),但我不能因为子查询中的“WHERE mat.content_id = m.content_id”,这使它成为一个相关的子查询。我的理解是,不能使用 WITH 子句来考虑相关子查询。出于同样的原因,我也不能将此子查询作为内联视图(又名派生表)放在 FROM 子句中。

如何将条件包含一次并通过相关子查询将多个列注入结果集中?

【问题讨论】:

你担心的仅仅是查询文字大小还是查询性能? 这是个好问题。如果子查询的标准发生变化(这是一个真正的可能性),我担心以后维护这个查询。所以,是的,我担心查询文本的大小。 【参考方案1】:

使用子查询分解(这就是 Oracle 所说的 - 它在 SQL Server 中称为公用表表达式)。支持 9i+:

WITH file AS (
  SELECT t.content_id,
         MAX(t.file_location) keep (DENSE_RANK t.first ORDER BY t.resolution DESC) 'fileLocation',
         MAX(t.file_size) keep (DENSE_RANK t.first ORDER BY t.resolution DESC) 'fileSize'
    FROM mat t
GROUP BY t.content_id)
SELECT m.material_id, 
       m.content_id,
       f.fileLocation,
       f.fileSize
  FROM mat m
  JOIN file f ON f.content_id = m.content_id

它旨在用于内联视图重用。您可以概括视图,并在 JOIN 子句中为不同的实例定义特定的过滤。您需要在 SELECT 子句中公开列以加入 - 以 content_id 为例。

相关子查询意味着它可以重写为 JOIN - 相关性是 JOIN 标准。

您可以在子查询分解中定义多个视图 - 如果您提供更多详细信息,我可以更好地定制答案。

【讨论】:

谁能解释为什么在 FROM 子句中简单加入更好?或者甚至将子查询逻辑放在单独的视图中? 回复:“相关子查询意味着它可以重写为 JOIN”——这是一个有趣的想法。不过,我对您的查询有点麻烦。它说文件是一个无效的表名,所以我重命名了它。然后它不喜欢聚合列的命名方式,所以我改变了它。现在我收到 content_id 的消息“ORA-00937: not a single-group group function”。我完全误解了什么吗? @jva:谷歌“子查询分解”。 @Nate:我忘记了子查询分解中的“GROUP BY t.content_id”。 @Nate:我更新了查询以包含丢失的 GROUP BY - 现在试试吧。【参考方案2】:

我想我明白了。我能够抓住“特殊”材料的主键并将主查询包装在另一层中,如下所示:

         SELECT x.material_id,
                x.content_id,
                sp_m.file_location,
                sp_m.file_size
           FROM (SELECT m.material_id, 
                        m.content_id,
                        (SELECT max(material_id) keep (dense_rank first order by resolution desc)                  
                           FROM mat
                          WHERE mat.content_id = m.content_id
                         /* AND ...
                            AND ...
                            AND ... */) special_mat_primary_key
                   FROM mat m
                  WHERE m.material_id IN (select material_id
                                            from mat
                                      inner join con on con.content_id = mat.content_id
                                      inner join con_groups on con_groups.content_id = con.content_id
                                           where con_groups.content_group_id = 10)) x
LEFT OUTER JOIN mat sp_m ON sp_m.material_id = x.special_mat_primary_key;

让我知道你的想法。

【讨论】:

关联子选择意味着它将针对返回的每一行执行。 IN 子句是性能第二差的查询方式。您正在创建一个包含比您需要的更多信息的内联视图。

以上是关于如何避免重复冗长的相关子查询?的主要内容,如果未能解决你的问题,请参考以下文章

避免用于派生选择中的列的多个重复子查询

当相关子查询不起作用时,压缩子查询

避免重复与慢子查询

在 Oracle 中避免相关子查询

避免“不支持这种类型的相关子查询模式”的方法

如何减少许多相似的相关子查询?