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

回复列表(165|隐藏机器人聊天)
  • @Ta / 2022-08-11 / /

    试试这样写

    SELECT name, uid
      FROM hu60_user
     WHERE EXISTS (SELECT * FROM hu60_addin_chat_data WHERE uid = hu60_user.uid AND room = '公共聊天室')
       AND EXISTS (SELECT * FROM hu60_addin_chat_data WHERE uid = hu60_user.uid AND room = '文件上传')
       AND EXISTS (SELECT * FROM hu60_bbs_topic_meta WHERE uid = hu60_user.uid AND forum_id = (
                SELECT id FROM hu60_bbs_forum_meta WHERE name = '超级灌水'
            ))
       AND EXISTS (SELECT * FROM hu60_bbs_topic_meta WHERE uid = hu60_user.uid AND forum_id = (
                SELECT id FROM hu60_bbs_forum_meta WHERE name = '网页插件'
            ));
    

    和上面的写法没有差别,可能最终的执行方式是一样的。

    截图_选择区域_20220811184940.png(409.13 KB)

  • @Ta / 2022-08-11 / /

    用关系除法试试

    CREATE TEMPORARY TABLE 要求聊天室表(room varchar(255));
    INSERT INTO 要求聊天室表 VALUES('公共聊天室'), ('文件上传');
    
    CREATE TEMPORARY TABLE 要求论坛表(forum_id int);
    INSERT INTO 要求论坛表 SELECT id FROM hu60_bbs_forum_meta WHERE name IN ('超级灌水', '网页插件');
    
    SELECT hu60_user.uid, hu60_user.name
      FROM (
        SELECT DISTINCT uid
          FROM hu60_addin_chat_data AS ES1
         WHERE NOT EXISTS (
                   SELECT *
                     FROM 要求聊天室表
                    WHERE NOT EXISTS (
                              SELECT *
                                FROM hu60_addin_chat_data AS ES2
                               WHERE ES2.uid = ES1.uid
                                 AND ES2.room = 要求聊天室表.room ))
      ) AS 除以要求聊天室表
      JOIN (
        SELECT DISTINCT uid
          FROM hu60_bbs_topic_meta AS ES1
         WHERE NOT EXISTS (
                   SELECT *
                     FROM 要求论坛表
                    WHERE NOT EXISTS (
                              SELECT *
                                FROM hu60_bbs_topic_meta AS ES2
                               WHERE ES2.uid = ES1.uid
                                 AND ES2.forum_id = 要求论坛表.forum_id ))
      ) AS 除以要求论坛表 USING(uid)
      JOIN hu60_user USING(uid)
     GROUP BY uid;
    

    并不快

    截图_选择区域_20220811184653.png(652.95 KB)

  • @Ta / 2022-08-11 / /

    @无名啊,现在我可以大胆的说,对于

    找出在公共聊天室文件上传发过言,并且在超级灌水网页插件版块发过帖的用户

    这个问题,拼接WHERE条件的方法是最快的,比你的书上记载的两个方法快得多。而且关系除法是所有方法中最慢的。

  • @Ta / 2022-08-11 / /

    @老虎会游泳,我本地创建点儿大表试试(一千万用户ID x 15~25 个技能ID = 2亿行 员工技能表,足够吗?)

  • @Ta / 2022-08-11 / /

    @无名啊,我不知道,我没有大型数据集测试经验。

  • @Ta / 2022-08-11 / /

    @老虎会游泳,我也没有。。

    PHP 能指定随机数种子不?我用 PHP 生成 csv,再导入 SQLite / MySQL

    有这个种子,你那也能生成同样数据集

  • @Ta / 2022-08-11 / /

    @无名啊,mt_srand/mt_rand

  • @Ta / 2022-08-11 / /

    @老虎会游泳,用这个 php 随机生成两张约 2亿行的大表吧

    分别输出至 STDOUT 和 STDERR,可以在 shell 中从这两处地方导入至两表

    mt_srand(strtotime('2022-08-11 20:00:00'));
    
    foreach ([STDOUT, STDERR] as $fp)
        for ($uid = 1; $uid <= 10000000; ++$uid)
            for ($i = mt_rand(15, 25); $i > 0; --$i)
                fprintf($fp, "%d,%d\n", $uid, mt_rand(0, 255));
    

    可按索引需要调换字段顺序(反正数字一致):

    fprintf($fp, "%d,%d\n", mt_rand(0, 255), $uid);
    
  • @Ta / 2022-08-11 / /

    @老虎会游泳,是不是还需要个 fclose($fp),来结束第一阶段的导入。。

    另外,我这儿运行好慢啊,是不是哪儿写错了

    光是这样都很慢:

    php main.php 2>/dev/null | wc -c
    
  • @Ta / 2022-08-11 / /

    @无名啊,嗯,等你测试。我不能在hu60.cn进行此类测试,我没有其他测试环境。

  • @Ta / 2022-08-11 / /

    @无名啊,生成那么多行本来就不会快。

  • @Ta / 2022-08-11 / /

    @老虎会游泳,可以用 sqlite 试一试

  • @Ta / 2022-08-11 / /

    @老虎会游泳,我吃个饭,等会儿来

  • @Ta / 2022-08-11 / /

    @老虎会游泳,不行啊,运行了 15分钟,还没结束。。

    肯定哪儿写错了

  • @Ta / 2022-08-11 / /

    @老虎会游泳

    PID USER       PRI  NI  VIRT   RES   SHR S  CPU% MEM%   TIME+  Command△
    435 user        20   0 86604 14316  9456 R 101.0  0.2 17:10.31       ├─ php /mnt/c/Users/wuxun/PhpstormProjects/untitled/main.php
    436 user        20   0 13464   780   652 R  98.3  0.0 16:44.55       └─ wc -c
    
  • @Ta / 2022-08-11 / /

    @无名啊,只是慢而已

    Screenshot_20220811_210045_com.termux.jpg(420.50 KB)

  • @Ta / 2022-08-11 / /

    @无名啊,并行生成再合并文件可能是个好主意。多开几个进程,每个生成一部分。

  • @Ta / 2022-08-11 / /

    @老虎会游泳,我换其他语言试试了,要不生成一次太久,想换下范围(如 0~255 -> 0~ 65535),重新生成一次估计都得半个钟

    Python 花了三分多钟

    from datetime import datetime
    from random import seed, randint
    
    seed(int(datetime.fromisoformat('2022-08-11 20:00:00').timestamp()))
    
    for uid in range(1, 10000000 + 1):
        for i in range(randint(15, 25)):
            print(f'{uid},{randint(0, 255)}')
    

    运行:

    $ time python3 /mnt/c/Users/wuxun/PycharmProjects/pythonProject/main.py | wc -c
    2291797342
    
    real    3m42.934s
    user    3m42.000s
    sys     0m2.875s
    
  • @Ta / 2022-08-11 / /

    @老虎会游泳,卧槽!!tcc牛逼啊!直接运行 C 源码,速度都能这么快!

    randint 范围有点问题,修复了

    $ time tcc -run - <<EOF | wc -c
    #include <time.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    static inline int randint(int min, int max) {
        return rand() % (max - min + 1) + min;
    }
    
    int main() {
    
        srand(mktime(&(struct tm){
            .tm_year = 2022 - 1900,
            .tm_mon = 8 - 1,
            .tm_mday = 11,
            .tm_hour = 20,
            .tm_min = 0,
            .tm_sec = 0,
        }));
    
        for (int uid = 1; uid <= 10000000; ++uid)
            for (int i = randint(15, 25); i > 0; --i)
                printf("%d,%d\n", uid, randint(0, 255));
    }
    EOF
    

    结果:

    2291774656
    
    real    0m22.888s
    user    0m22.547s
    sys     0m4.516s
    
  • @Ta / 2022-08-11 / /

    @老虎会游泳,我记得tcc是边解释边运行的啊,测下来 20秒 生成 2亿行。。

    又不用编译,又不生成临时文件,以后就用 tcc 来解决脚本中的性能敏感部分了

    $ time tcc -run main.c | wc -l
    199993434
    
    real    0m22.912s
    user    0m24.016s
    sys     0m4.422s
    
添加新回复
回复需要登录