【问题标题】:Mysql: selecting relative to second tableMysql:选择相对于第二个表
【发布时间】:2017-08-01 09:47:31
【问题描述】:

我有 2 张桌子:

实体:

id

状态:

id
entity_id
status

每个实体可以有许多状态,我只需要选择具有“已付款”状态且没有“已取消”状态的实体。我该如何正确地加入呢?

例子:

entities: id 7
entities: id 8
entities: id 9
entities: id 10
statuses: id 1, entity_id 7, status 'paid'
statuses: id 2, entity_id 7, status 'canceled'
statuses: id 3, entity_id 8, status 'paid'
statuses: id 4, entity_id 10, status 'onhold'
statuses: id 5, entity_id 8, status 'whatever'

只有 ID 为 8 的条目会被选中一次。

【问题讨论】:

    标签: mysql sql database


    【解决方案1】:

    可能类似于以下内容:

    SELECT e.* FROM entities as e INNER JOIN statuses as spaid ON e.id = spaid.entity_id and spaid.status = 'paid' LEFT JOIN statuses as scanceled ON e.id = scanceled.entity_id and scanceled.status = 'canceled' WHERE scanceled.id IS NULL

    第一次加入需要具有“付费”状态的实体。

    第二个连接将为具有“已取消”状态的实体添加第二个状态,或为没有已取消状态的实体添加 NULL

    where 子句然后过滤条目,仅将具有 NULL 的行作为“已取消”状态。

    【讨论】:

    • 它似乎做到了。谢谢!
    【解决方案2】:

    类似的东西。我们按实体 ID 对状态进行分组,并在分组后将它们过滤为仅具有 MIN 和 MAX 已付费的状态。实体 7 的 MIN 为“已取消”,MAX 为“已支付”,因此被排除在外。

    SELECT e.* 
    FROM 
    entities e
    INNER JOIN
    (
      SELECT entity_id FROM statuses s 
      GROUP BY entity_id
      HAVING 
        MIN(CASE WHEN status = 'canceled' THEN status ELSE 'not canceled' END) = 'not canceled' AND 
        MAX(CASE WHEN status = 'paid' then 'paid' ELSE 'not paid' END) = 'paid'
    ) a
    ON 
      a.entity_id = e.id
    

    根据您的评论,关于 MIN 和 MAX 的更多信息:

    您说事物可以有多种状态,但实际上我们只对 PAID 事物感兴趣,然后我们只对那些从未有 CANCELED 的 PAID 事物感兴趣。我们做什么,然后状态是:

    • 将“已取消”以外的所有内容都变为“未取消”
    • 将“付费”以外的所有内容都变成“未付费”
    • 链接行条目并查找“已付款”/“未取消”的配对

    要明白我的意思,请看:

    SELECT 
      entity_id,
      CASE WHEN status = 'paid' then 'paid' ELSE 'not paid' END as is_paid,
      CASE WHEN status = 'canceled' THEN status ELSE 'not canceled' END as is_cancelled
    FROM
      status
    

    现在看看:

    SELECT 
      entity_id,
      MAX(CASE WHEN status = 'paid' then 'paid' ELSE 'other' END) as is_paid,
      MIN(CASE WHEN status = 'canceled' THEN status ELSE 'not canceled' END) as is_cancelled
    FROM
      status
    GROUP BY
      entity_id
    

    这是一个“枢轴”操作;它在概念上将行变成列。项目 7 的多行变为具有多列的单行。最小值和最大值可以计算出来,因为按字母顺序,“已付款”在“未付款”之后,“已取消”在“”之前。

    这就是我们寻找“付费”/“未取消”配对的方式。此时我们可以这样说:

    SELECT * FROM entities INNER JOIN
    (
      SELECT 
        entity_id,
        MAX(CASE WHEN status = 'paid' then 'paid' ELSE 'other' END) as is_paid,
        MIN(CASE WHEN status = 'canceled' THEN status ELSE 'not canceled' END) as is_cancelled
      FROM
        status
      GROUP BY
        entity_id
    ) finder
    ON 
      entities.id = finder.entity_ID
    WHERE
      finder.is_paid = 'paid' and finder.is_canceled = 'not canceled'
    

    使用 HAVING 进行过滤比稍后使用 WHERE 进行过滤要短一些

    【讨论】:

    • 谢谢。你能解释一下这个有最小值和最大值吗?
    • 对不起。这将仅选择具有支付状态且没有其他状态的实体。但是实体可以有多种状态。我关心的所有支付都存在并且取消不存在。我通过添加另一个示例行来更新我最初的问题。
    • 修改答案
    猜你喜欢
    • 1970-01-01
    • 2022-06-28
    • 2013-07-16
    • 2011-03-17
    • 2021-04-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-24
    • 2014-01-30
    相关资源
    最近更新 更多