【问题标题】:Mandatory condition matching in ´where-in´ clause'where-in' 子句中的强制条件匹配
【发布时间】:2019-12-13 19:20:31
【问题描述】:

考虑一个简单的 where 子句

select * from table_abc where col_a in (1,2,3)

我知道现在的情况

  • 如果 1,2,3 不存在,我将不会得到任何结果
  • 如果存在 1,2,3,我将获得与 1,2,3 相关的所有结果
  • 如果 1 存在而 2,3 不存在,我将只得到与 1 相关的结果。

我的问题是我们是否可以执行条件查询

  • 如果 1 存在而 2,3 不存在,我仍然应该得到与 1,2,3 相关的所有结果

  • 但是,如果没有1,2,3,我不会得到任何结果

    换句话说,我可以将 where-in 子句中的特定值设置为强制吗?我们如何更改当前查询?

编辑:正如评论中指出的,我忘了添加表结构。我最好也解释一下用例。

表 1:管理员

ID  admin_id
-------------
1      001      
2      002 

表 2:事件

ID  event_id
-------------
1      110      
2      220 

表 3:Admins_Events

admin_id  event_id
-------------
001        110
001        220
002        220 

现在,作为过滤的一部分,假设我有查询

SELECT "admins"."admin_id", "events"."event_id" FROM "admins" 
LEFT JOIN "admins_events" ON "admins_events"."admin_id" = "admins"."admin_id" 
LEFT JOIN "events" ON "events"."event_id" = "admins_events"."event_id" 
WHERE (events.event_id IN (110) AND admins.admin_id IN (001))

目前,我得到的结果是

admin_id  event_id
-------------
001        110

我想要的东西在哪里

admin_id  event_id
-------------
001        110
001        220

即使我没有在 where-in 子句中传递它,我仍然必须显示与管理员相关的其他事件。我想每次都传递所有 event_id 并匹配强制 event_id 并匹配剩余的 event_ids,以防找到强制 event_id。

SELECT "admins"."admin_id", "events"."event_id" FROM "admins" 
LEFT JOIN "admins_events" ON "admins_events"."admin_id" = "admins"."admin_id" 
LEFT JOIN "events" ON "events"."event_id" = "admins_events"."event_id" 
WHERE (events.event_id IN (mandatory[110], 220) AND admins.admin_id IN (001))

如何更改查询?

【问题讨论】:

  • 数据不存在时如何获取数据?
  • 缺席什么?整张桌子? col_a 中只有一个值,因此它们中的两个或更多将始终不存在于一行中。
  • 将 events.event_id 条件从 WHERE 移动到 ON 以获得真正的 LEFT JOIN 结果。 (现在你会得到常规的内部连接结果。)
  • @jarlh 你能详细说明你的答案吗?
  • 您使用的是 Postgres 还是 MySQL?请只标记您真正使用的数据库。

标签: sql postgresql where-clause where-in


【解决方案1】:

WHERE 子句中使用EXISTS 添加另一个条件:

SELECT a.admin_id, e.event_id 
FROM Admins a 
LEFT JOIN Admins_Events ae ON ae.admin_id = a.admin_id 
LEFT JOIN Events e ON e.event_id = ae.event_id 
WHERE (e.event_id IN (110, 220) AND a.admin_id IN (001))
AND EXISTS (
  SELECT 1 FROM Admins_Events
  WHERE event_id = 110 AND admin_id = a.admin_id
)

请参阅demo
结果:

| admin_id | event_id |
| -------- | -------- |
| 1        | 110      |
| 1        | 220      |

【讨论】:

    【解决方案2】:

    首先,您只需要admin_events 表。

    Then 方法使用窗口函数:

    SELECT ae.*
    FROM (SELECT ae.*,
                 SUM(CASE WHEN ae.event_id IN (110) THEN 1 ELSE 0 END) OVER (PARTITION BY ae.admin_id) as num_110
          FROM admins_events ae
         ) ae
    WHERE ae.admin_id IN ('001') AND -- assume this is a string
          num_110 > 0 AND
          ae.event_id IN (110, 220);
    

    【讨论】:

      【解决方案3】:

      听起来在您的示例中,您希望所有事件都与与事件 110 关联的管理员相关联。在 Mysql 中,我将使用以下查询来执行此操作,该查询将管理员加入事件两次:一次过滤您需要的事件, 一次获取所有其他事件。

      但是,在您的示例中,您根本不需要查询中的 admins 表,因为您只需要可以直接从 admins_events 表中获取的管理员 ID。我把它留在了,以防你真正的“管理员”表还有你想要的其他属性(名称、位置等),这些属性在 admins_events 连接表中不可用。

      具体查询为:

      SELECT "admins"."admin_id", "events"."event_id" FROM "admins" 
      JOIN "admins_events" ON "admins_events"."admin_id" = "admins"."admin_id" 
      JOIN "events" ON "events"."event_id" = "admins_events"."event_id"
      JOIN "admins_events" AS "specific_admins_events" ON "specific_admins_events"."admin_id" = "admins"."admin_id" 
      JOIN "events" AS "specific_events" ON "specific_events"."event_id" = "specific_admins_events"."event_id"
      WHERE (specific_events.event_id IN (110))
      

      【讨论】:

      • 很棒的方法。谢谢你。但是如果我添加另一个 event_id,它会重复,例如 specific_events.event_id IN (110, 220)
      • 您可以将第一部分更改为“选择不同”以删除重复项
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-03-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多