合并条件不同的查询
Merge queries with difference in conditions
我有 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;
它们 select table 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;
但在这种情况下,我的 table 数据太大,我不想 select 太多次。有人可以给我一个构建返回相同结果的查询的最佳方法的想法吗?
如果 X3='c'
如果你真的需要 2 行,一个用 Q1,另一个用 Q1,那么联合查询是最好的。
我尝试用 X1=a and X2=b
创建一个 CTE,后来做了一个联合。成本比普通联合查询多一点。
所以继续 union
。
此外,如果这些列未编入索引,请尝试为其编入索引。性能会提高。
我们可以获取所有需要的行,在第一个和第二个过滤器之间添加 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;
仍有代码重复的缺陷,但这是一个代价
用于查询 table 大数据。换句话说,对于一个小 table
我们可以使用带有子查询的简洁代码,这会占用更多内存:
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;
我有 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;
它们 select table 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;
但在这种情况下,我的 table 数据太大,我不想 select 太多次。有人可以给我一个构建返回相同结果的查询的最佳方法的想法吗?
如果 X3='c'
如果你真的需要 2 行,一个用 Q1,另一个用 Q1,那么联合查询是最好的。
我尝试用 X1=a and X2=b
创建一个 CTE,后来做了一个联合。成本比普通联合查询多一点。
所以继续 union
。
此外,如果这些列未编入索引,请尝试为其编入索引。性能会提高。
我们可以获取所有需要的行,在第一个和第二个过滤器之间添加 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;
仍有代码重复的缺陷,但这是一个代价 用于查询 table 大数据。换句话说,对于一个小 table 我们可以使用带有子查询的简洁代码,这会占用更多内存:
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;