如何用 SQL 求出「都关注了某100人」和「都收藏了某100帖子」的所有用户?

想用普通 SQL 语句解决此类『某表有多列「多值属性」(可存为「数组」或「独立表」)。现给定过滤条件,找出包含所有这些条件的行记录』问题。如:

  • 找出所有『既会MySQLPHP』『又购买过iPhone 13红米 K50』的虎友
  • 找出所有『既会说粤语上海话』『考试又过了英语专八日语 N1』的学生
  • 找出所有『既看过《明日战记》《独行月球》』『又看过《当尘埃落尽》《血清素》』的网友(我随便豆瓣首页找的啊)

注意:

  1. 尽量不要使用存储过程/函数、其他编程语言,以及 MySQL 8.0.17+ 的多值索引。
  2. 尽量使用 B+ 树索引(好像PostgreSQL有针对json的高效索引)
回复列表(165|隐藏机器人聊天)
  • @Ta / 2022-08-10 / /

    目前只能做到想法子求出各条件的主键,再求它们的交集。也不知能不能直接合并成多个where条件之类的

  • @Ta / 2022-08-11 / /

    WHERE IN (Sub Query)

  • @Ta / 2022-08-11 / /

    @无名啊

    『既看过《明日战记》《独行月球》』『又看过《当尘埃落尽》《血清素》』

    那不就是

    看过《明日战记》、《独行月球》、《当尘埃落尽》《血清素》

    吗?

    SELECT * FROM hu60_user WHERE
            uid IN (SELECT uid FROM hu60_bbs_topic_content WHERE content LIKE '%电影A%')
        AND uid IN (SELECT uid FROM hu60_bbs_topic_content WHERE content LIKE '%电影B%')
        AND uid IN (SELECT uid FROM hu60_bbs_topic_content WHERE content LIKE '%电影C%')
        AND uid IN (SELECT uid FROM hu60_bbs_topic_content WHERE content LIKE '%电影D%');
    
  • @Ta / 2022-08-11 / /

    @读书顶个鸟用WHERE IN (Sub Query)是至少出现一个即可,题目要求『所有条件的所有值,同时满足』

    你可能至少还要在GROUP BY后指定HAVING COUNT(*) = xxx

    @老虎会游泳,表结构应该是(用户ID,用户名,看过啥电影 JSON,看过啥书 JSON,……),或者那俩 JSON 字段独立成(用户ID,看过电影ID),(用户ID,看过书籍ID)(或者字段顺序反过来,看索引需要而定)

  • @Ta / 2022-08-11 / /

    @无名啊

    SELECT * FROM hu60_user WHERE
            uid IN (SELECT uid FROM hu60_用户看过的电影 WHERE 电影id = 12345)
        AND uid IN (SELECT uid FROM hu60_用户看过的电影 WHERE 电影id = 67890)
        AND uid IN (SELECT uid FROM hu60_用户看过的电影 WHERE 电影id = 78901)
        AND uid IN (SELECT uid FROM hu60_用户看过的电影 WHERE 电影id = 89012);
    
  • @Ta / 2022-08-11 / /

    @老虎会游泳,这语法结构。。是不是相当于:

    SELECT *
      FROM hu60_user
     WHERE uid IN (SELECT uid FROM hu60_用户看过的电影 WHERE 电影id = 12345)
       AND EXISTS (SELECT uid FROM hu60_用户看过的电影 WHERE 电影id = 67890)
       AND EXISTS (SELECT uid FROM hu60_用户看过的电影 WHERE 电影id = 78901)
       AND EXISTS (SELECT uid FROM hu60_用户看过的电影 WHERE 电影id = 89012);
    
  • @Ta / 2022-08-11 / /

    @无名啊,我忘记写AND后面的uid IN,已补充

  • @Ta / 2022-08-11 / /

    实际试一下

    SELECT uid, name FROM hu60_user WHERE
            uid IN (SELECT uid FROM hu60_bbs_topic_content WHERE content LIKE '%mysql%')
        AND uid IN (SELECT uid FROM hu60_bbs_topic_content WHERE content LIKE '%php%')
        AND uid IN (SELECT uid FROM hu60_bbs_topic_content WHERE content LIKE '%iphone%13%')
        AND uid IN (SELECT uid FROM hu60_bbs_topic_content WHERE content LIKE '%红米%k50%');
    

    Screenshot_20220811_120754.jpg(267.42 KB)

    完全正确

  • @Ta / 2022-08-11 / /

    @老虎会游泳,这种问题,肯定是希望能走索引提速的

    你 8楼的肯定是扫全表了

    我瞅瞅你5楼的

  • @Ta / 2022-08-11 / /

    @老虎会游泳,还有至少另外一个问题,不使用其他编程语言 (『注意』中的第一点提到了),怎么拼凑这种 SQL 呢?

  • @Ta / 2022-08-11 / /

    @无名啊,“不使用其他编程语言”没有现实意义,“不使用其他编程语言”的话,你怎么把参数拼接到SQL里?

    既然你都能把参数拼接到SQL里了,把条件拼接到SQL里不是轻轻松松吗?

    不要作茧自缚。

    这是个变参SQL语句(参数个数不固定),必然是要用其他编程语言进行SQL拼接的。

    就算是简单的WHERE uid IN (1, 2, 3)IN里面的uid个数不一样的时候,还不是需要用其他编程语言进行拼接。

  • @Ta / 2022-08-11 / /

    @老虎会游泳,我用了『关系除法』,来解决『满足一个条件的所有值』

    但我不会改写成『多个条件』的……

  • @Ta / 2022-08-11 / /

    @无名啊,5楼给出了我的方案,8楼证明了它确实可以运行并且给出了正确的结果(它们是同一个SQL语句,只是根据虎绿林的实际情况,=变成LIKE了而已。虎绿林帖子表的实际情况就是需要全表扫描。如果你的实际情况并非如此,当然可以继续使用=以便使用索引)。

    所以,我暂时没有其他考虑。

  • @Ta / 2022-08-11 / /

    @老虎会游泳,这个是课本上的『关系除法』,用于解决『从「员工技能表」中,找出(会「要求技能表」里所有技能的)所有员工』

    SELECT DISTINCT	员工ID
      FROM 员工技能表 AS ES1
     WHERE NOT EXISTS (
               SELECT *
                 FROM 要求技能表
                WHERE NOT EXISTS (
                          SELECT *
                            FROM 员工技能表 AS ES2
                           WHERE ES2.员工ID = ES1.员工ID
                             AND ES2.技能 = 要求技能表.技能 ))
    
  • @Ta / 2022-08-11 / /

    @老虎会游泳,但我不会改写成满足两个条件的(除非搞两次,再求交集了)

  • @Ta / 2022-08-11 / /

    @无名啊,里面没有参数(由用户输入的查询条件)。当存在参数并且参数个数可能不同时,必然需要其他编程语言进行SQL拼接。

    就算是简单的WHERE uid IN (1, 2, 3),IN里面的uid个数不一样的时候,还不是需要用其他编程语言进行拼接。

  • @Ta / 2022-08-11 / /

    @老虎会游泳,可以变成:

    FROM json_table(?, '$[*]' COLUMNS(技能ID INT PATH '$')) 要求技能表
    

    然后提供 JSON:

    ["MySQL", "PHP"]
    

    这个法子连 SQLite 都支持,算是『普通SQL』了

  • @Ta / 2022-08-11 / /

    @老虎会游泳SQLite 大概长这样:

    select * from json_each('["MySQL", "PHP"]')
    
  • @Ta / 2022-08-11 / /

    @无名啊,请考虑一下现实情况,MySQLPHPiPhone 13红米 K50必然是用户输入的查询条件,这些条件肯定既不在程序里也不在表里,SQL语句必然是根据用户输入条件的个数动态生成的。

    当用户输入三个查询条件(MySQLPHPiPhone 13)的时候,SQL语句必然和四个查询条件(MySQLPHPiPhone 13红米 K50)不同。

    所以“不使用其他编程语言”有什么意义吗?

    如果真的要死抠字眼,

    SELECT uid, name FROM hu60_user WHERE
            uid IN (SELECT uid FROM hu60_bbs_topic_content WHERE content LIKE '%mysql%')
        AND uid IN (SELECT uid FROM hu60_bbs_topic_content WHERE content LIKE '%php%')
        AND uid IN (SELECT uid FROM hu60_bbs_topic_content WHERE content LIKE '%iphone%13%')
        AND uid IN (SELECT uid FROM hu60_bbs_topic_content WHERE content LIKE '%红米%k50%');
    

    这使用其他编程语言了吗?并没有!它就是一个SQL语句,你怎么能说它使用了其他编程语言呢?它的查询条件固定了,我当然就不需要使用其他编程语言。但现实是查询条件的内容和数量都必然不固定。

添加新回复
回复需要登录