V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
zzyphp111
V2EX  ›  数据库

求助 一个简单 sql 查询问题;就拿简单学生选课来举例

  •  1
     
  •   zzyphp111 · 2022-05-20 09:17:19 +08:00 · 2190 次点击
    这是一个创建于 916 天前的主题,其中的信息可能已经有所发展或是发生改变。

    需求: 查出 X 个学生选择相同科目,且这个科目只被这个 X 个学生选,得到科目 id 集合。

    数据表

    uid obj_id
    A 数学
    B 数学
    A 语文
    B 语文
    C 语文
    D 语文
    A 英语
    C 英语
    A 体育
    C 体育

    举例:

    比方 A,B,C,D 学生选了且仅选 的结果集:  [语文课] 
    比方 A 学生 选了且仅选 的结果集是空 [] (因为 A 所选科目还有其他学生。)
    比方 A,B 学生 选了且仅选 的结果集是 [数学]  (因为语文课还有 C 学生所以不算)
    比方 D 学生 选了且仅选 的结果集是空 []  (因为语文课还有 ABC 学生所以不算) 
    比方 A ,C 学生 选了且仅选 的结果集是 [英语,体育] 
    

    提问: 如何表达这个 sql ?

    12 条回复    2022-06-08 17:51:25 +08:00
    VersionGod1
        1
    VersionGod1  
       2022-05-20 09:47:34 +08:00
    我处理这个问题可能会先查询出 X 个学生选择的相同科目,再查出科目对应的 X 个同学,用 Java 进行匹配
    dqzcwxb
        2
    dqzcwxb  
       2022-05-20 09:54:47 +08:00
    有没有可能在代码里写好逻辑?
    zzyphp111
        3
    zzyphp111  
    OP
       2022-05-20 09:58:16 +08:00
    主要考虑的点在于中间数据量巨大, 类似 A 学生这种 ,实际中间流程数据量巨大, 因为他雨露均沾, 最后算出来是空结果, 就感觉代码逻辑写的太邋遢了。

    想想有没有什么好的 sql 妙招。
    nuanshen
        4
    nuanshen  
       2022-05-20 10:07:46 +08:00
    # 给个 mysql 的,不过不知道数据量多的情况下性能如何
    ```mysql
    SELECT
    t.uids,
    GROUP_CONCAT( t.obj_id ) obj_ids
    FROM (
    SELECT
    t.obj_id,
    GROUP_CONCAT( t.uid ) uids
    FROM (
    # 以下可替换成数据表
    SELECT 'A' uid, '数学' obj_id UNION
    SELECT 'B' uid, '数学' obj_id UNION
    SELECT 'A' uid, '语文' obj_id UNION
    SELECT 'B' uid, '语文' obj_id UNION
    SELECT 'C' uid, '语文' obj_id UNION
    SELECT 'D' uid, '语文' obj_id UNION
    SELECT 'A' uid, '英语' obj_id UNION
    SELECT 'C' uid, '英语' obj_id UNION
    SELECT 'A' uid, '体育' obj_id UNION
    SELECT 'C' uid, '体育' obj_id
    # end
    ) t
    GROUP BY t.obj_id
    ) t
    GROUP BY t.uids
    ```
    klo424
        5
    klo424  
       2022-05-20 10:19:05 +08:00
    按逻辑写的,不保证效率,假设表是 test ,common 是 A/C 两个人共选的课程,HAVING COUNT(1) = 2 代表有 2 条(有几个人就=几,目的是排除 A/C 有一个没有选这课),exc 排除掉其他人选的课。

    WITH common AS (
    SELECT obj_id
    FROM test
    WHERE uid IN ('A','C')
    GROUP BY obj_id
    HAVING COUNT(1) = 2
    ), exc AS (
    SELECT obj_id
    FROM test
    WHERE uid NOT IN ('A','C')
    )
    SELECT *
    FROM test
    WHERE obj_id IN (SELECT obj_id FROM common)
    AND obj_id NOT IN (SELECT obj_id FROM exc)
    zzyphp111
        6
    zzyphp111  
    OP
       2022-05-20 10:21:05 +08:00   ❤️ 1
    找到解决方案了 ,在这里多谢 :"flw" 大佬,以及各位想出来的好办法

    答案为:
    select tmp_group.obj_id from (select obj_id, group_concat(uid) as uids from group_tag group by obj_id order by obj_id) as tmp_group where tmp_group.uids = 'A,B';

    一个 sql 搞定,非常漂亮!
    finull
        7
    finull  
       2022-05-20 10:24:48 +08:00
    这题是有参数输入的啊,输入是 uid 列表

    select obj_id from ( select obj_id from `table` group by object_id having count({input}) = 4 ) t where not exists ( select * from `table` where obj_id = t.obj_id and uid not in ( {input} ) );
    eason1874
        8
    eason1874  
       2022-05-20 10:37:05 +08:00
    我的写法:

    SELECT `obj_id` FROM `test` WHERE `obj_id` IN (SELECT `obj_id` FROM `test` WHERE `uid` IN ('A','B','C','D') GROUP BY `obj_id` HAVING COUNT(`obj_id`) = 4) GROUP BY `obj_id` HAVING COUNT(`obj_id`) = 4;

    有两个 HAVING COUNT 4 ,是查询 uid 的数量,需要随着输入一起变。帖子里有几种写法了,楼主可以测测哪种更快
    ColinZeb
        9
    ColinZeb  
       2022-05-20 13:58:28 +08:00
    @zzyphp111 uid 没排序直接 group-concat 不会出意外吗
    zzyphp111
        10
    zzyphp111  
    OP
       2022-05-20 18:29:55 +08:00
    @ColinZeb #9 嗯,group_concat 函数要求 对入参需要先排序, 我这边用 go 的 sort 比较方便快捷:

    ```go
    sort.Slice(ids, func(i, j int) bool {
    return ids[i] < ids[j]
    })
    ```

    其次是要注意 mysql 的配置 group_concat_max_len 默认是 1024 长度,如果 uids 的 长度超了需要额外考虑, 或者提前配置 数据库配置参数。

    进入 mysql 状态,输入:show variables like 'group_concat_max_len';
    l00t
        11
    l00t  
       2022-06-08 16:30:06 +08:00
    @zzyphp111 #10 18 天过去了,楼主还没掉坑吗?
    zzyphp111
        12
    zzyphp111  
    OP
       2022-06-08 17:51:25 +08:00
    @l00t #11 已经解决了啊,上面给出结论了
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1253 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 17:56 · PVG 01:56 · LAX 09:56 · JFK 12:56
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.