【问题标题】:Merge queries with difference in conditions合并条件不同的查询
【发布时间】:2015-10-17 07:21:25
【问题描述】:

我有 2 个 SQL (Oracle 11g) 查询:

select x1,x2,x3
from X
where x1 = a and x2 = b;

select x1,x2,x3
from X
where x1 = a and x2 = b and x3 = c; 

他们在表 X 中选择相同的列,但条件不同。我使用 UNION 来合并结果:

select x1,x2,x3,'Q1' as QueryCode
from X
where x1 = a and x2 = b
  UNION
select x1,x2,x3,'Q2' as QueryCode
from X
where x1 = a and x2 = b and x3 = c; 

但在这种情况下,我的表格数据太大,我不想选择太多次。有人可以给我一个想法,以构建返回相同结果的查询的最佳方式吗?

【问题讨论】:

  • 万一,x1=a,x2=b,x3=c,你要两行还是单行?
  • 我想为每个 QueryCode 获得 1 行 => 2rows

标签: sql oracle query-optimization union


【解决方案1】:

我们可以获取所有需要的行,在第一个和第二个过滤器之间添加OR,然后将它们分隔在UNION 中。并且使用提示/*+ materialize */,我们确保来自original_table 的数据仅被选择一次,并将过滤结果作为sub_table 存储在内存中以供当前查询执行。

是的,复制代码 (x1 = a AND x2 = b)(x1 = a AND x2 = b AND x3 = c) 并不好,但是在这种数据太大的情况下,我们做出了另一个很好的权衡:为了出色的性能而进行少量复制。

WITH
  sub_table AS (SELECT /*+ materialize */ x1, x2, x3
                  FROM original_table
                 WHERE (x1 = a AND x2 = b)             -- first filter
                    OR (x1 = a AND x2 = b AND x3 = c)  -- second filter
  )
SELECT  x1, x2, x3, 'Q1' AS querycode
  FROM sub_table
 WHERE x1 = a AND x2 = b              -- first filter (repeated)

UNION

SELECT x1, x2, x3, 'Q2' AS querycode
  FROM sub_table
 WHERE x1 = a AND x2 = b AND x3 = c;  -- second filter (repeated)

如果我们不关心行顺序,还有另一种方法除了UNION

SELECT x1, x2, x3,
       CASE
       WHEN x1 = a AND x2 = b THEN 'Q1'
       WHEN x1 = a AND x2 = b AND x3 = c THEN 'Q2'
       END AS marker
  FROM original_table
 WHERE CASE
       WHEN x1 = a AND x2 = b THEN 'Q1'
       WHEN x1 = a AND x2 = b AND x3 = c THEN 'Q2'
       END IS NOT NULL;

仍然有代码重复的不完善,但这是一个代价 用于查询具有大数据的表。换句话说,对于一张小桌子 我们可以使用带有子查询的简洁代码,这会更占用内存:

SELECT *
  FROM (SELECT x1, x2, x3,
               CASE
               WHEN x1 = a AND x2 = b THEN 'Q1'
               WHEN x1 = a AND x2 = b AND x3 = c THEN 'Q2'
               END AS marker
          FROM original_table) t
 WHERE t.marker IS NOT NULL;

最后,在 Oracle 12c 中,我们可以将这个重复的 CASE 封装到一个函数中:

WITH
  FUNCTION get_marker(x1 CHAR, x2 CHAR, x3 CHAR) RETURN CHAR DETERMINISTIC
  IS
    BEGIN

      RETURN CASE
             WHEN x1 = a AND x2 = b THEN 'Q1'
             WHEN x1 = a AND x2 = b AND x3 = c THEN 'Q2'
             END;

    END
SELECT x1, x2, x3,
       get_marker(x1, x2, x3) AS marker
  FROM original_table
 WHERE get_marker(x1, x2, x3) IS NOT NULL;

【讨论】:

  • 谢谢@diziaq。你真是太好了。这对我真的很有用。
【解决方案2】:

如果对于X3='c',如果您确实需要 2 行,一个带有 Q1,另一个带有 Q1,那么联合查询是最好的。

我尝试使用X1=a and X2=b 创建一个 CTE,然后再进行联合。成本比正常的联合查询要高一些。

所以继续使用union

此外,如果这些列未编入索引,请尝试为它们编入索引。性能会提高。

【讨论】:

  • 是的,早期我认为联盟是最好的选择。但就我而言,这张表的数据太大了。每个查询调用这个表 3 次,如果使用 UNION,这意味着我为此调用了 6 次。
  • 是的。您可以尝试索引 x1、x2 和 x3 列。会提高性能。如果我的回答对你有用,请采纳。
  • 其实我没有更改表索引的权限。此外,我必须选择太多行(〜50),正如你所说,我不能为所有行编制索引(当然,如果我有权利)
猜你喜欢
  • 1970-01-01
  • 2017-07-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-12-12
  • 1970-01-01
  • 2020-03-22
相关资源
最近更新 更多