求助 一个简单 sql 查询问题;就拿简单学生选课来举例 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
zzyphp111
V2EX    数据库

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

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

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

    数据表

    uid obj_id
    A 数学
    数学
    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     Solana     933 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 25ms UTC 19:58 PVG 03:58 LAX 11:58 JFK 14:58
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86