【问题标题】:Counting null values as unique value将空值计数为唯一值
【发布时间】:2013-02-09 00:57:58
【问题描述】:

我需要统计一列的不同值,比如:

Hours
1
1
2
null
null
null

结果必须是:3。我的查询是:

select count(distinct hour) from hours;

但它返回:2。我也测试过:

select count(*) from hours group by hour

但它返回三行:

(1) 3
(2) 2
(3) 1

如何将 null 值计为 1 个值并使用 distinct 来避免计数重复值?

我正在学习高级 SQL,他们希望我满足所有解决方案的这些要求:

尽量减少解决查询所需的子查询数量。 此外,您不得使用以下结构:

  • 在 FROM 或 SELECT 中选择。您可以有子查询(在 WHERE 或 HAVING 中选择)
  • COUNT (COUNT...))、SUM (COUNT...)) 等聚合函数的组合。
  • 如果可以避免,请使用 UNION。
  • 非标准函数(如 NVL)
  • 案例

【问题讨论】:

  • 使用 coalesce(col,0)
  • @Strawberry:这会将0null 视为相同的值
  • 所以使用 coalesce(col,-1)

标签: sql oracle count null


【解决方案1】:
select  count(distinct col1) + count(distinct case when col1 is null then 1 end)
from    YourTable

【讨论】:

  • 工作正常,但我正在尝试不使用“案例”,并且效率最高。
  • @user2076284 最大效率与您提出的所有任意限制都不匹配。
【解决方案2】:

如果小时是一个数字,那么如果它只能是一个整数:

select count(distinct coalesce(hour, 0.1)) cnt from test;

否则,如果它可以是任何浮点,则将 NULL 更改为 char 字符串。

例如

select count(distinct coalesce(to_char(hour), 'a')) cnt from test;

【讨论】:

    【解决方案3】:
    select 
       count(0) 
    from
      (
          select distinct hour from hours
      )
    

    SqlFiddle

    【讨论】:

    • +1 是一个不错的解决方案——不过我更喜欢 count(*)。
    • @DavidAldridge count(0) 比 count(*) 效率高
    • @DRAM 在什么数据库上?不是甲骨文——你已经迷上了一个在其他地方被广泛反驳的神话。
    • @DavidAldridge - 嗯,这很有趣。请提供一个反驳链接。
    • @DavidAldridge .. 你是对的.. 这是一个神话。感谢您的洞察力。参考:stackoverflow.com/questions/1221559/count-vs-count1
    【解决方案4】:
    SELECT
          ( SELECT COUNT(DISTINCT hour)
            FROM hours
          )
        + CASE WHEN EXISTS
               ( SELECT *
                 FROM hours
                 WHERE hour IS NULL
               )
            THEN 1 
            ELSE 0
          END
       AS result
    FROM dual ;
    

    【讨论】:

    • 这种 CASE WHEN EXISTS 在 Oracle 中是否合法?
    【解决方案5】:

    也许

        select count(distinct hour||' ') from hours;
    

    会吗?

    【讨论】:

    • 欢迎来到 Stack Overflow!您能否编辑您的答案以添加解释?例如,什么会导致您的答案是正确的?
    【解决方案6】:
    select count(distinct nvl(hour,0)) from hours;
    

    【讨论】:

      【解决方案7】:

      我想说你的要求很奇怪,因为你几乎可以肯定只需使用NVL()COALESCE()CASE 就能获得更高效的查询。但是,我设法仅使用子查询来获得正确的结果(并处理NULL 值的存在或不存在)。如果没有在 FROM 子句中使用子查询,我还没有设法做到这一点。

      SQL Fiddle

      查询 1

      SELECT nnh.not_null_hours + nh.null_hours
      FROM (
        SELECT COUNT(DISTINCT t.hour) not_null_hours
        FROM example_table t
      ) nnh
      CROSS JOIN (
        SELECT 1 null_hours
        FROM dual
        WHERE EXISTS (
          SELECT 1
          FROM example_table t
          WHERE t.hour IS NULL
        )
        UNION ALL
        SELECT 0 null_hours
        FROM dual
        WHERE NOT EXISTS (
          SELECT 1
          FROM example_table t
          WHERE t.hour IS NULL
        )
      ) nh
      

      Results

      | NNH.NOT_NULL_HOURS+NH.NULL_HOURS |
      ------------------------------------
      |                                3 |
      

      这将花费大量精力来应对要求。一个更简单的选择是使用NVL,然后是两个简单的选择之一......要么:

      1. 使用TO_CHAR 将非NULL 值转换为数据类型VARCHAR2 和NVLNULL 值转换为VARCHAR2 'NULL'
      2. 只需将 NVL 与您知道不会出现在结果集中的幻数一起使用(即由于表的限制)。

      Query 1

      SELECT 
        COUNT(DISTINCT NVL(TO_CHAR(hour), 'NULL')) using_to_char_null
      , COUNT(DISTINCT NVL(hour, -1)) using_magic_number
      FROM example_table
      

      Results

      | USING_TO_CHAR_NULL | USING_MAGIC_NUMBER |
      -------------------------------------------
      |                  3 |                  3 |
      

      【讨论】:

      • 谢谢。我认为不打破这些要求是不可能的,一定是错的。
      • 我可以用外连接做点什么吗?
      • 我想不出任何有用的连接条件可以在不违反“FROM 子句中没有子查询”规则的情况下使用,但我已经尽我最大的努力为 Oracle 添加了另一个答案。不知道我是否可以单独使用纯 ANSI SQL。
      【解决方案8】:

      安德烈斯的回答是完美满足要求的回答,除了COUNT之外根本不使用任何功能:

      select count(distinct hour||' ') from hours;
      

      我一直在为另一个目的寻找相同的东西(我可以使用任何东西)但在我看到这个之前它似乎并不正确或有效,谢谢 Andres,这样一个简单但功能强大的解决方案。

      【讨论】:

        【解决方案9】:

        我能得到的最接近指定标准的是: (SQL Fiddle)

        查询 1

        SELECT COUNT(*)
        FROM example_table t1
        WHERE t1.ROWID IN (
          SELECT MAX(t2.ROWID)
          FROM example_table t2
          GROUP BY t2.hour
        )
        

        Results

        | COUNT(*) |
        ------------
        |        3 |
        

        不确定是否允许 ROWID 伪列,考虑到其他限制,但它可以正常工作并优雅地处理 NULL 值。我认为 ROWID 不存在于 Oracle 之外,因此这可能与问题的精神背道而驰,但它至少符合 列出的标准

        【讨论】:

          【解决方案10】:

          可能最简单的方法是使用DUMP

          SELECT COUNT(DISTINCT DUMP(hour)) AS distinct_count
          FROM hours;
          

          输出:3

          DBFiddle Demo

          【讨论】:

            【解决方案11】:

            啊.. 作业。是不是就这么简单?

            SELECT COUNT(hour) 
              FROM hours
            

            NULLS 不被计算在内。

            知道了!没有正确阅读要求是我的坏事。

            SELECT COUNT(DISTINCT COALESCE(hour,-1)) 
              FROM hours
            

            【讨论】:

            • 但如果它们存在,他想将它们算作一个。
            猜你喜欢
            • 1970-01-01
            • 2015-04-25
            • 2020-02-25
            • 1970-01-01
            • 1970-01-01
            • 2017-05-18
            • 2014-04-16
            • 1970-01-01
            • 2011-12-05
            相关资源
            最近更新 更多