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

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

    @老虎会游泳,我测试了下,Python 生成 1KW 个数字(0~1KW-1),然后变成 json 字符串,给 sqlite 计算出 min、max、count,

    我的渣笔记本用时 3 秒

    import time
    begin = time.time()
    
    import json
    import sqlite3
    
    db = sqlite3.connect(':memory:')
    print(db.execute(
        'SELECT count(*), min(value), max(value) FROM json_each(?)',
        (json.dumps(list(range(10000000))),),
    ).fetchone())
    db.close()
    
    print(time.time() - begin)
    

    结果:

    (10000000, 0, 9999999)
    2.8264474868774414
    
  • @Ta / 2022-08-11 / /

    @老虎会游泳,看来表值函数实现不算差,有瓶颈也不会出现在这儿

  • @Ta / 2022-08-11 / /

    @无名啊,哦,看起来它有一些非关系型数据库的功能了。如果JSON_CONTAINS可以利用索引,那么它确实是最佳解决方案。

    不过,这个解决方案不仅依赖其他编程语言(需要其他编程语言进行JSON拼接),还依赖SQL拼接(需要对条件1 AND 条件2 AND 条件3...进行拼接)

  • @Ta / 2022-08-11 / /

    @老虎会游泳,我觉得拼接参数优于拼接SQL。后者的解析可能很耗时,对于 执行计划 的计算可能也有影响(不是说是用到动态规划么。。O(N^2)那种?)

  • @Ta / 2022-08-11 / /

    @老虎会游泳,可如果你去看下『多值索引』的底层存储,发现还是关系数据库。。

    这里有个解析:https://zhuanlan.zhihu.com/p/115802841

    发现还是展开来存。。比如 (id: 1, has: ["aa", "bb"]) 就存成 ("aa" -> 1)("bb" -> 1)。。

    估计这也解释了为何一个索引,不能有多个多值字段吧(否则就数量爆炸了:一行记录,对应 count(多值字段一) * count(多值字段二) * ... 行索引)

  • @Ta / 2022-08-11 / /

    @tasy5kg,如果是要找 哪些用户听过了(歌曲1,歌曲2,……歌曲n),关注了(歌手1,歌手2,……,歌手n),咋写呀

  • @Ta / 2022-08-11 / /

    @无名啊

    拼接参数优于拼接SQL

    确实。不过经典的关系型数据库没办法用一个参数传递多个值,拼接SQL是必然选择。MySQL的JSON_CONTAINS已经超出关系型数据库的范畴了。

    而且就算用JSON_CONTAINS,想同时匹配多个字段,还是绕不过SQL拼接。JSON_CONTAINS(字段1, ...) AND JSON_CONTAINS(字段2, ...),这里的AND字段1字段2似乎必须在SQL里,没有办法仅靠参数就进行传递。

  • @Ta / 2022-08-11 / /

    @老虎会游泳,一般字段数在设计表时就定了吧(也意味着,代码里一般也写死SQL了)

  • @Ta / 2022-08-11 / /

    @无名啊

    可如果你去看下『多值索引』的底层存储,发现还是关系数据库。。

    我不这么认为。

    一个关系型数据库就是由二维表及其之间的关系组成的一个数据组织。

    包含JSON字段的MySQL表已经超过二维了。

  • @Ta / 2022-08-11 / /

    @无名啊,但是用户的查询条件不确定啊。为什么我一定要搜索技能+手机呢?我不能只搜索技能,或者只搜索手机吗?你不拼接SQL,难道你要写三个函数来分别处理不同的查询条件组合吗?

  • @Ta / 2022-08-11 / /

    @老虎会游泳

    我不这么认为。

    你瞅了刚才发的那篇文章地址了吗?别人分析了多值索引底层存储结构:

    原表:

    [root@yejr.me]> select id, custinfo->'$.zipcode' from customers;
    +----+-----------------------+
    | id | custinfo->'$.zipcode' |
    +----+-----------------------+
    |  1 | [94582, 94536]        |
    |  2 | [94568, 94507, 94582] |
    |  3 | [94477, 94507]        |
    |  4 | [94536]               |
    |  5 | [94507, 94582]        |
    +----+-----------------------+
    

    二级索引的底层存储:

    +---------+------+
    | zipcode | id   |
    +---------+------+
    |   94477 |    3 |
    |   94507 |    2 |
    |   94507 |    3 |
    |   94507 |    5 |
    |   94536 |    1 |
    |   94536 |    4 |
    |   94568 |    2 |
    |   94582 |    1 |
    |   94582 |    2 |
    |   94582 |    5 |
    +---------+------+
    
  • @Ta / 2022-08-11 / /

    @老虎会游泳,然后你就发现,这不就是『员工技能表』『员工手机表』么。。

  • @Ta / 2022-08-11 / /

    @老虎会游泳,好吧,『技能员工表』『手机员工表』

    所以我前面也说了:

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

  • @Ta / 2022-08-11 / /

    @无名啊,这和索引有什么关系,那只是具体实现,而关系型数据库是一个抽象概念,不需要考虑具体实现。

    关系型数据库是二维表,也就是说,表中每个单元格要么没有值(为NULL),要么只有唯一一个值,这个值能和数据库中其他单元格的值直接建立联系。

    但是JSON字段显然打破了这个假设,这个单元格有多个值,和其他单元格中的值也没办法直接建立联系,必须先提取JSON的某个值才能和其他非JSON字段建立联系。

    也就是说,带JSON字段的表不是二维的,取决于JSON的结构,它可能是三维、四维、五维……的。我不认为这依然属于关系型数据库。

  • @Ta / 2022-08-11 / /

    @老虎会游泳

    但是用户的查询条件不确定啊。为什么我一定要搜索技能+手机呢?我不能只搜索技能,或者只搜索手机吗?你不拼接SQL,难道你要写三个函数来分别处理不同的查询条件组合吗?

    改写下 SQL 就好了啊

    SELECT *
      FROM 员工表
     WHERE (?1 IS NULL OR JSON_CONTAINS(这个员工会的技能, CAST(?1 AS JSON)))
       AND (?2 IS NULL OR JSON_CONTAINS(这个员工有的手机, CAST(?2 AS JSON)))
    
  • @Ta / 2022-08-11 / /

    @无名啊,当然,如果你说“JSON索引其实相当于一个独立的表”、“JSON字段只是INNER JOIN的语法糖”,那也确实成立,那可以把这个模型继续视为关系型数据库。

  • @Ta / 2022-08-11 / /

    @老虎会游泳,我觉得,现阶段的 json 字段还是当字符串看待的(至少 SQLite 是这样)

  • @Ta / 2022-08-11 / /

    @无名啊,相比于IS NULL OR,我更喜欢拼接AND语句,因为这是把命运掌握在自己手中,而不是交给SQL优化器。

    而且,你生成了单个复杂SQL,初次解析需要更久时间,并且每次跑优化器匹配IS NULL也需要一段时间(因为参数是否为NULL不能提前确定)。

    我虽然最终会生成三个不同的SQL,但是没有多余条件,不会在优化器里浪费时间。而且预处理+查询缓存,也只是首次解析三个SQL需要一点时间而已。因为SQL更简单,解析起来不见得比单个复杂SQL慢。

  • @Ta / 2022-08-11 / /

    @无名啊,当然,你可以说我在拼接SQL时浪费了时间。所以哪个更快依然不好说。这只能属于个人选择。

  • @Ta / 2022-08-11 / /

    @老虎会游泳,MySQL 的执行优化,真的那么蠢吗。。

    是不是要衬托出甲骨文的商业产品 Oracle 的强大。。

    这就是收购 MySQL 的考虑?(别发展太强?)

添加新回复
回复需要登录