每天,请求变得更奇怪,更奇怪。
我被要求组合一个查询来检测表中哪些列包含所有行的相同值。我说“这需要通过程序来完成,这样我们才能在表格的一次传递中完成,而不是N次传递。”
我被推翻了。
故事很长。我有这个非常简单的查询来演示这个问题。它在测试集上进行了4次传递。我正在寻找SQL Magery的想法,这些想法不涉及在每一列上添加索引,或编写程序,或者花费整个人的生命周期来运行。
叹息它需要能够在任何桌面上工作。
提前感谢您的建议。
WITH TEST_CASE AS
(
SELECT 'X' A, 5 B, 'FRI' C, NULL D FROM DUAL UNION ALL
SELECT 'X' A, 3 B, 'FRI' C, NULL D FROM DUAL UNION ALL
SELECT 'X' A, 7 B, 'TUE' C, NULL D FROM DUAL
),
KOUNTS AS
(
SELECT SQRT(COUNT(*)) S, 'Column A' COLUMNS_WITH_SINGLE_VALUES
FROM TEST_CASE P, TEST_CASE Q
WHERE P.A = Q.A OR (P.A IS NULL AND Q.A IS NULL)
UNION ALL
SELECT SQRT(COUNT(*)) S, 'Column B' COLUMNS_WITH_SINGLE_VALUES
FROM TEST_CASE P, TEST_CASE Q
WHERE P.B = Q.B OR (P.B IS NULL AND Q.B IS NULL)
UNION ALL
SELECT SQRT(COUNT(*)) S, 'Column C' COLUMNS_WITH_SINGLE_VALUES
FROM TEST_CASE P, TEST_CASE Q
WHERE P.C = Q.C OR (P.C IS NULL AND Q.C IS NULL)
UNION ALL
SELECT SQRT(COUNT(*)) S, 'Column D' COLUMNS_WITH_SINGLE_VALUES
FROM TEST_CASE P, TEST_CASE Q
WHERE P.D = Q.D OR (P.D IS NULL AND Q.D IS NULL)
)
SELECT COLUMNS_WITH_SINGLE_VALUES
FROM KOUNTS
WHERE S = (SELECT COUNT(*) FROM TEST_CASE)
答案 0 :(得分:10)
WITH
TEST_CASE AS
(
SELECT 'X' A, 5 B, 'FRI' C, NULL D FROM DUAL UNION ALL
SELECT 'X' A, 3 B, 'FRI' C, NULL D FROM DUAL UNION ALL
SELECT 'X' A, 7 B, 'TUE' C, NULL D FROM DUAL
)
select case when min(A) = max(A) THEN 'A'
when min(B) = max(B) THEN 'B'
when min(C) = max(C) THEN 'C'
when min(D) = max(D) THEN 'D'
else 'No one'
end
from TEST_CASE
修改强> 这有效:
WITH
TEST_CASE AS
(
SELECT 'X' A, 5 B, 'FRI' C, NULL D FROM DUAL UNION ALL
SELECT 'X' A, 3 B, 'FRI' C, NULL D FROM DUAL UNION ALL
SELECT 'X' A, 7 B, 'TUE' C, NULL D FROM DUAL
)
select case when min(nvl(A,0)) = max(nvl(A,0)) THEN 'A ' end ||
case when min(nvl(B,0)) = max(nvl(B,0)) THEN 'B ' end ||
case when min(nvl(C,0)) = max(nvl(C,0)) THEN 'C ' end ||
case when min(nvl(D,0)) = max(nvl(D,0)) THEN 'D ' end c
from TEST_CASE
奖励:我还添加了空值检查,现在的结果是:A和D
SQLFiddle demo适合你。
答案 1 :(得分:8)
优化工具统计信息可以轻松识别具有多个不同值的列。收集统计数据后,对数据字典的简单查询几乎会立即返回结果。
如果您使用ESTIMATE_PERCENT = 100,结果将仅在10g上准确。如果您使用ESTIMATE_PERCENT = 100或AUTO_SAMPLE_SIZE,结果将在11g +上准确。
<强>代码强>
create table test_case(a varchar2(1), b number, c varchar2(3),d number,e number);
--I added a new test case, E. E has null and not-null values.
--This is a useful test because null and not-null values are counted separately.
insert into test_case
SELECT 'X' A, 5 B, 'FRI' C, NULL D, NULL E FROM DUAL UNION ALL
SELECT 'X' A, 3 B, 'FRI' C, NULL D, NULL E FROM DUAL UNION ALL
SELECT 'X' A, 7 B, 'TUE' C, NULL D, 1 E FROM DUAL;
--Gather stats with default settings, which uses AUTO_SAMPLE_SIZE.
--One advantage of this method is that you can quickly get information for many
--tables at one time.
begin
dbms_stats.gather_schema_stats(user);
end;
/
--All columns with more than one distinct value.
--Note that nulls and not-nulls are counted differently.
--Not-nulls are counted distinctly, nulls are counted total.
select owner, table_name, column_name
from dba_tab_columns
where owner = user
and num_distinct + least(num_nulls, 1) <= 1
order by column_name;
OWNER TABLE_NAME COLUMN_NAME
------- ---------- -----------
JHELLER TEST_CASE A
JHELLER TEST_CASE D
<强>性能强>
在11g上,此方法可能与mucio的SQL语句一样快。像cascade => false
这样的选项可以通过不分析索引来提高性能。
但这种方法的优点在于它还可以生成有用的统计数据。如果系统已经定期收集统计数据,则可能已经完成了艰苦的工作。
有关AUTO_SAMPLE_SIZE算法的详细信息
AUTO_SAMPLE_SIZE在11g中完全改变了。它不使用抽样来估计不同值(NDV)的数量。相反,它扫描整个表并使用基于散列的不同算法。该算法不需要大量内存或临时表空间。读取整个表格比快速排序要快得多。 Oracle Optimizer博客对算法here有很好的描述。有关更多详细信息,请参阅Amit Podder的this presentation。 (如果您想在下一节中验证详细信息,您需要浏览该PDF文件。)
错误结果的可能性
虽然新算法不使用简单的采样算法,但它仍然不能正确计算100%的不同值的数量。很容易找到估计的不同值的数量与实际值不同的情况。但是,如果不同值的数量显然不准确,那么它们如何在此解决方案中受到信任?
潜在的不准确性来自两个来源 - 哈希冲突和概要分裂。概要拆分是不准确的主要来源,但不适用于此。只有当有13864个不同的值时才会发生这种情况。并且它永远不会抛出所有值,最终估计肯定会远大于1.
唯一真正关心的是有两个不同的值与哈希冲突的可能性。使用64位散列,机会可能低至18,446,744,073,709,551,616中的1。不幸的是,我不知道他们的哈希算法的细节,也不知道真正的概率。我无法通过一些简单的测试和以前的现实测试产生任何碰撞。 (我的一个测试是使用大值,因为一些统计操作只使用前N个字节的数据。)
现在还要考虑只有当表中的所有的不同值发生冲突时才会发生这种情况。有一个只有 两个值恰好碰撞的表的可能性有多大?可能远远低于赢得彩票并同时受到陨石撞击的机会。
答案 2 :(得分:3)
如果您可以将结果放在一行,则此 只能扫描一次;
WITH TEST_CASE AS
(
SELECT 'X' A, 5 B, 'FRI' C, NULL D FROM DUAL UNION ALL
SELECT 'X' A, 3 B, 'FRI' C, NULL D FROM DUAL UNION ALL
SELECT 'X' A, 7 B, 'TUE' C, NULL D FROM DUAL
)
SELECT
CASE WHEN COUNT(DISTINCT A) +
COUNT(DISTINCT CASE WHEN A IS NULL THEN 1 END) = 1
THEN 1 ELSE 0 END SAME_A,
CASE WHEN COUNT(DISTINCT B) +
COUNT(DISTINCT CASE WHEN B IS NULL THEN 1 END) = 1
THEN 1 ELSE 0 END SAME_B,
CASE WHEN COUNT(DISTINCT C) +
COUNT(DISTINCT CASE WHEN C IS NULL THEN 1 END) = 1
THEN 1 ELSE 0 END SAME_C,
CASE WHEN COUNT(DISTINCT D) +
COUNT(DISTINCT CASE WHEN D IS NULL THEN 1 END) = 1
THEN 1 ELSE 0 END SAME_D
FROM TEST_CASE
答案 3 :(得分:1)
这将在一次扫描中完成
WITH
TEST_CASE AS
(
SELECT 'X' A, 5 B, 'FRI' C, NULL D FROM DUAL UNION ALL
SELECT 'X' A, 3 B, 'FRI' C, NULL D FROM DUAL UNION ALL
SELECT 'X' A, 7 B, 'TUE' C, NULL D FROM DUAL
)
select decode(count(distinct nvl(A,0)),1,'SINGLE','MULTP') COL_A,
decode(count(distinct nvl(B,0)),1,'SINGLE','MULTP') COL_B,
decode(count(distinct nvl(C,0)),1,'SINGLE','MULTP') COL_C,
decode(count(distinct nvl(D,0)),1,'SINGLE','MULTP') COL_D
from TEST_CASE