【问题标题】:Counting zero-values when joining in T-SQL在 T-SQL 中加入时计算零值
【发布时间】:2013-09-21 03:52:36
【问题描述】:

我有几个不同的 SQL 表需要执行一些计数。这是为了我的工作,所以显然数据是机密的,但我会尝试将其抽象为类似的东西。想象一下大学注册办公室...

我们的第一个表格包含学生已完成的所有课程的列表。

表:student_courses

student_id | course_id
----------------------
123456     | MATH101
123456     | MATH203
785426     | PHYS305
  ...      |   ...

我们还为我们大学的不同部门提供表格,列出(除其他外)课程 ID 和课程年级:

表:math_courses

course_id | year
--------------------
MATH101   |  1
MATH201   |  2
MATH202   |  2
CALC103   |  1
STAT402   |  4
 ...      |  ...

想象一下类似的science_coursesgeneral_courses 表。该大学还有其他部门,但我们目前只考虑这三个。

我们正在寻找的是达到每个年级/部门组合的学生人数。

例如,如果学生同时参加了 MATH101 和 MATH201,则其数学系的“年”将被视为“2”。

并非所有学生都完成了所有系的课程,也并非所有学生都必须修读这三个系的任何课程。

基本上,我希望生成下表:

max_math_year | max_science_year | max_general_year | student_count
---------------------------------------------------------------------
NULL          | NULL             | NULL             | 39847
NULL          | NULL             | 1                | 172
NULL          | NULL             | 2                | 0
 ...          |  ...             |  ...             |  ...
4             | 4                | 4                | 4986

我已经建立了一个临时表来存储每个学生的最高级别,并且它似乎工作正常,所以为了这个练习,假设我们有下表:

表:#student_maximums

student_id | max_math_year | max_science_year | max_general_year
----------------------------------------------------------------
123465     | 2             | NULL             | 1
782514     | 3             | 1                | NULL
874252     | NULL          | NULL             | NULL
 ...       |  ...          |  ...             |  ...

当一行没有符合特定组合的学生时,我想返回0,但如果我对上表进行计数,它不会返回零值。

大多数情况下,我会使用LEFT OUTER JOIN 来包含零,但在这种情况下,我们会根据最初来自四个不同表的数据进行计数。我考虑过使用CROSS JOIN,但这不会在行中包含NULL 值,这也是我需要的。

谢谢!

【问题讨论】:

    标签: sql sql-server database join null


    【解决方案1】:

    基于这两个建议,我提出了以下建议。我对合并后的值执行了连接,因此空行实际上会跨表排列。

    注意,我能够在课程表中插入行。如果不是,我会 UNION 查询中的零值行。

    INSERT INTO math_courses (0, NULL)
    INSERT INTO science_courses (0, NULL)
    INSERT INTO general_courses (0, NULL)
    
    SELECT
        COALESCE(m.max_math_year, 0)
      , COALESCE(s.max_science_year, 0)
      , COALESCE(g.max_general_year, 0)
      , COUNT(DISTINCT student_id)
    FROM
        math_courses m
    CROSS JOIN
        science_courses s
    CROSS JOIN
        general_courses g
    LEFT OUTER JOIN
        student_maximums sm
      ON COALESCE(sm.max_math_year, 0) = COALESCE(m.year, 0)
      AND COALESCE(sm.max_science_year, 0) = COALESCE(s.year, 0)
      AND COALESCE(sm.max_general_year, 0) = COALESCE(g.year, 0)
    GROUP BY
        COALESCE(m.max_math_year, 0)
      , COALESCE(s.max_science_year, 0)
      , COALESCE(g.max_general_year, 0)
    ORDER BY
        COALESCE(m.max_math_year, 0)
      , COALESCE(s.max_science_year, 0)
      , COALESCE(g.max_general_year, 0)
    

    【讨论】:

      【解决方案2】:

      我认为这就是您所需要的(尽管我不确定——如果它不能解释问题所在):

      UPDATE #student_maximums SET max_math_year = 0 WHERE max_math_year is NULL
      UPDATE #student_maximums SET max_science_year = 0 WHERE max_science_year is NULL
      UPDATE #student_maximums SET max_general_year = 0 WHERE max_general_year is NULL
      
      SELECT max_math_year, max_science_year, max_general_year, count(*) as student_count
      FROM #student_maximums
      GROUP BY max_math_year, max_science_year, max_general_year
      ORDER BY 1, 2, 3
      

      【讨论】:

      • 这提供了与 dnoeth 的答案相同的结果,所以我会投票赞成,但同样,我错过了计数为零的行。
      【解决方案3】:

      如果我理解正确,我不确定,但这就是你想要的吗?

      select
         coalesce(max_math_year, 0)
        ,coalesce(max_science_year, 0)
        ,coalesce(max_general_year, 0)
        ,count(*)
      from #student_maximums
      group by 
         coalesce(max_math_year, 0)
        ,coalesce(max_science_year, 0)
        ,coalesce(max_general_year, 0)
      

      【讨论】:

      • ...也许不是。再次查看结果,这并没有提供计数为零的行。例如。如果没有学生参加数学 1 年、科学年 2 和普通年 2,那么我希望该行以零或 NULL 计数出现在结果中。
      • 好的,现在来点完全不同的东西 :-) 它不是(仅)用 0 替换 NULL,而是返回不存在的数据。有两种方法可以解决它:LEFT JOIN 到具有所有可能变化的表/结果集,或者(更容易)将每个可能变化的虚拟行添加到 #student_maximums 中,学生为 0,其他列为 NULL。最后将计数更改为 COUNT(NULLIF(student_id,0))
      猜你喜欢
      • 1970-01-01
      • 2015-09-29
      • 2016-07-12
      • 2011-02-20
      • 1970-01-01
      • 1970-01-01
      • 2016-10-28
      • 1970-01-01
      • 2014-03-29
      相关资源
      最近更新 更多