多个INNER JOIN子查询sql

Posted

技术标签:

【中文标题】多个INNER JOIN子查询sql【英文标题】:Multiple INNER JOIN subqueries sql 【发布时间】:2014-02-24 01:24:24 【问题描述】:

我有以下查询来自这篇帖子count number of items in a row in mysql,它计算了一个学生连续出现/缺席的次数。

    SELECT
        classlist.studentid,
        student.name,
        classStatus.name status,
        COUNT(*) presentcnt
    FROM
        classlist 
        INNER JOIN student ON classlist.studentid=student.id
        INNER JOIN classstatus ON classlist.presentid=classstatus.id
        INNER JOIN (
            SELECT
                studentid,
                max(CASE WHEN presentid=0 THEN id END)  max_0,
                max(CASE WHEN presentid=1 THEN id END)  max_1
            FROM classlist
            GROUP BY studentid
        ) s
        ON coalesce(classlist.id>least(max_0,max_1) AND classlist.id<=greatest(max_0,max_1),1) AND s.studentid=classlist.studentid
    GROUP BY classlist.studentid

这按预期工作,

    STUDENTID   NAME    STATUS  PRESENTCNT
    111         John    Present     1
    222         Kate    Absent      2
    333         Matt    Present     5

我想扩展查询,以便有一列显示学生是否参加了课堂。

如果我运行一个独立的查询,我会得到我想要的结果

    SELECT
        classlist.studentid,
        student.name,
        participatedStatus.name status,
        COUNT(*) participatedcnt
    FROM
        classlist 
        INNER JOIN student ON classlist.studentid=student.id
        INNER JOIN participatedStatus ON classlist.participatedid=participatedStatus.id
        INNER JOIN (
            SELECT
                studentid,
                max(CASE WHEN participatedid=0 THEN id END)  max_0,
                max(CASE WHEN participatedid=1 THEN id END)  max_1
            FROM classlist
            group by studentid
        ) s
        ON coalesce(classlist.id>least(max_0,max_1) 
        AND classlist.id<=greatest(max_0,max_1),1)
        AND s.studentid=classlist.studentid
    group by classlist.studentid

    STUDENTID   NAME    STATUS  PARTICIPATEDCNT
    111         John    Yes     1
    222         Kate    No      2
    333         Matt    Yes     2

但是我想将它们合并到一个查询中,这样我就得到了

    STUDENTID   NAME    STATUS  PRESENTCNT  STATUS2     PARTICIPATEDCNT
    111         John    Present     1       Yes         1
    222         Kate    Absent      2       No          2
    333         Matt    Present     5       Yes         2

当我选择 count * 时,我对如何实现这一点感到困惑,我该如何实现这一点?

我使用的数据样本位于this fiddle 及以下

    CREATE TABLE classlist
        (`id` int, `studentid` int, `subjectid` int, `presentid` int, `participatedid` int);

    CREATE TABLE student
        (`id` int, `name` varchar(4));

    CREATE TABLE subject
        (`id` int, `name` varchar(4));

    CREATE TABLE classStatus
        (`id` int, `name` varchar(8));

    CREATE TABLE participatedStatus
        (`id` int, `name` varchar(8));

    INSERT INTO classlist   (`id`, `studentid`, `subjectid`, `presentid`, `participatedid`)
    VALUES  (1, 111, 1, 1, 0),  (2, 222, 3, 0, 0),  (3, 333, 2, 1, 0),  (4, 111, 4, 0, 0),  (5, 111, 1, 1, 0),  (6, 222, 3, 0, 0),  (7, 333, 2, 1, 1),  (8, 111, 4, 0, 0),  (9, 111, 4, 0, 0),  (10, 111, 4, 0, 0), (11, 111, 1, 1, 1), (12, 333, 3, 1, 0), (13, 333, 2, 1, 1), (14, 333, 3, 1, 1);

    INSERT INTO student (`id`, `name`)
    VALUES  (111, 'John'),(222, 'Kate'),(333, 'Matt');

    INSERT INTO subject (`id`, `name`)
    VALUES  (1, 'MATH'),(2, 'ENG'),(3, 'SCI'),(4, 'GEO');

    INSERT INTO classStatus (`id`, `name`)
    VALUES  (0, 'Absent'),  (1, 'Present');

    INSERT INTO participatedStatus  (`id`, `name`)
    VALUES  (0, 'No'),(1, 'Yes');

【问题讨论】:

我不确定我是否跟随。您想知道学生既在场又专心的连续课程数量?因此,例如,如果一个学生连续上了 5 节课,但在第 4 节课中注意力不集中,那将注册为“3,1”? 【参考方案1】:
SELECT
        studid,
        studname,
        status,
        presentcnt,
        status1,
        participatedcnt FROM
(SELECT
        classlist.studentid studid,
        student.name studname,
        classStatus.name status,
        COUNT(*) presentcnt
    FROM
        classlist 
        INNER JOIN student ON classlist.studentid=student.id
        INNER JOIN classstatus ON classlist.presentid=classstatus.id
        INNER JOIN (
            SELECT
                studentid,
                max(CASE WHEN presentid=0 THEN id END)  max_0,
                max(CASE WHEN presentid=1 THEN id END)  max_1
            FROM classlist
            GROUP BY studentid
        ) s
        ON coalesce(classlist.id>least(max_0,max_1) AND classlist.id<=greatest(max_0,max_1),1) AND s.studentid=classlist.studentid
    GROUP BY classlist.studentid)x
JOIN

(SELECT
        classlist.studentid,
        student.name,
        participatedStatus.name status1,
        COUNT(*) participatedcnt
    FROM
        classlist 
        INNER JOIN student ON classlist.studentid=student.id
        INNER JOIN participatedStatus ON classlist.participatedid=participatedStatus.id
        INNER JOIN (
            SELECT
                studentid,
                max(CASE WHEN participatedid=0 THEN id END)  max_0,
                max(CASE WHEN participatedid=1 THEN id END)  max_1
            FROM classlist
            group by studentid
        ) s
        ON coalesce(classlist.id>least(max_0,max_1) 
        AND classlist.id<=greatest(max_0,max_1),1)
        AND s.studentid=classlist.studentid
    group by classlist.studentid)y
ON x.studid=y.studentid

Fiddle

【讨论】:

(1) 由 INNER JOIN 的子查询生成的两个表 s 完全相同。那么,在性能方面,RDBMS 是否以某种方式对其进行了优化,只运行一次并存储结果,还是这个子查询运行两次? (2)如果它运行两次,以某种方式将这个子查询的结果存储在某个地方,在两个地方都使用它并优化查询性能是否有意义?实际上,有没有办法实际存储s 表? 好的,刚刚在this other *** answer 中找到了我对问题 (2) 的回答。总之,可以使用 WITH 语句来“存储”子查询的结果。问题 (1) 暂时没有答案

以上是关于多个INNER JOIN子查询sql的主要内容,如果未能解决你的问题,请参考以下文章

INEXISTS的相关子查询用INNER JOIN 代替--性能优化

流分析:Inner Join 两个子查询

Sql查询left join

MySQL INNER JOIN 查询中包含子查询的问题

表的基本查询语句及使用连表(inner joinleft join)子查询

Postgresql中的Inner Join子查询导致错误