合并具有条件差异的查询

时间:2015-10-17 07:21:26

标签: sql oracle query-optimization union

我有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; 

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

2 个答案:

答案 0 :(得分:1)

如果对于X3='c',如果你真的需要2行,一行是Q1,另一行是Q1,那么联合查询是最好的。

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

继续union

此外,如果这些列未编入索引,请尝试将其编入索引。表现会有所改善。

答案 1 :(得分: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;