【问题标题】:What is optimal MySQL statement to select rows from same table that have some ids but not others?什么是从同一个表中选择具有某些 id 而没有其他 id 的行的最佳 MySQL 语句?
【发布时间】:2016-01-16 16:54:09
【问题描述】:

我们需要从下表mytable 中选择所有segments_id 为5 或8 但不是1 或7 的segments_id, em_id。为了在下面的cmets 之后更清楚,结果将是一个集合,其中包括segments_id, em_id 的所有行,其中 em_idsegments_id 为 5 或 8,但不是 1 或 7。

 +-------------+-------+
 | segments_id | em_id |
 +-------------+-------+
 |    1        |   8   |
 |    1        |  17   |
 |    5        |   2   |
 |    5        |   4   |
 |    5        |   5   |
 |    5        |  16   |
 |    5        |  17   |
 |    7        |   4   |
 |    7        |   5   |
 |    7        |   8   |
 |    7        |  16   |
 |    8        |   4   |
 |    8        |   6   |
 |    8        |   8   |
 |    8        |  18   |
 |    18       |   6   |
 |    18       |  99   |
 +-------------+-------+

结果应该是:

 +-------------+-------+
 | segments_id | em_id |
 +-------------+-------+
 |    5        |   2   |
 |    8        |   6   |
 |    8        |  16   |
 +-------------+-------+

我们需要避免使用IN 子句,因为它可以扩展到数百万行。

我知道这将涉及连接自身和/或子查询,但我没有看到它。我看到了这个帖子Stackoverflow: Selecting rows from a table that have the same value for one field,但看不到解决方案。

【问题讨论】:

  • 为什么你认为in 不能扩展到一百万行?
  • 是否还有其他规则说明为什么某些记录会被丢弃,例如(8, 4) ?没关系。我认为您的预期输出是(令人困惑的)缩写。
  • 嗨,Ed,事实是我不知道,根据经验,但我在这里看到了一些强烈的意见How many values in an “in” clause is too many in a SQL query?

标签: mysql


【解决方案1】:

您可以使用LEFT JOIN 消除所有em_id,其中segment_id 是1 OR 7:

SELECT m.segments_id, m.em_id
FROM myTable as m
LEFT JOIN (SELECT * FROM myTable WHERE (segments_id=1 OR segments_id=7)) as n
ON m.em_id=n.em_id
WHERE n.segments_id IS NULL
GROUP BY m.em_id;

请参阅 SQLfiddle here

如果您想要保留 5 AND 8 并且应该消除所有其余部分,那么您可以像这样调整您的 WHERE 子句之一:

SELECT m.segments_id, m.em_id
FROM myTable as m
LEFT JOIN (SELECT * FROM myTable WHERE NOT (segments_id=5 OR segments_id=8)) as n
ON m.em_id=n.em_id
WHERE n.segments_id IS NULL
GROUP BY m.em_id;

第二个查询的 SQLfiddle 是 here

如果你只想要保留 5 AND 8 而想要消除 1 AND 7,你可以使用这个(但是在这种情况下,@lad2025 给出的答案可能是更好的选择):

SELECT m.segments_id, m.em_id
FROM myTable as m
LEFT JOIN (SELECT * FROM myTable WHERE (segments_id=1 OR segments_id=7)) as n
ON m.em_id=n.em_id
WHERE (n.segments_id IS NULL AND (m.segments_id=5 OR m.segments_id=8))
GROUP BY m.em_id;

请。使用改进的集合检查第三个 SQLfiddle here

【讨论】:

  • Arman,选择在你的答案和@lad2025 之间。你对两者有什么看法?两个大的SUM 调用会比你的加入/检查NULL 慢吗?
  • 在性能方面,我认为在测试它们之前我们不能肯定地说什么。但是,我很确定“不在查询中写入所有剩余的 segment_id 数字”的实际用途。想想有一千个 id 而你只想消除两个的情况。恕我直言,那么将所有这些都写在查询中将是无稽之谈。
  • 嗨,Arman,实际上,该示例不包括除 1、5、7、8 之外具有不同 segments_id 的其他行。一旦我有更多的segment_ids,你的陈述就太贪婪了,并且也会返回所有其他的。您可以重写以准确指定哪些段 ID 应该包括 - 以及排除吗?我将尝试编辑我的问题以添加具有不同 segments_id 的另一行
  • 我已经更新了答案。我认为它对应于您的最后一个问题。
  • 嗨,Arman,我正在尝试接受您的回答,但我的代表低于 15。感谢您的指导。
【解决方案2】:

使用聚合:

SELECT segments_id,em_id   -- GROUP_CONCAT(segments_id) AS segments_ids
FROM mytable
GROUP BY em_id
HAVING SUM(segments_id IN (8,5)) > 0
   AND SUM(segments_id IN (1,7)) = 0;

SqlFiddleDemo

输出:

╔══════════════╦═══════╗
║ segments_id  ║ em_id ║
╠══════════════╬═══════╣
║           5  ║     2 ║
║           8  ║     6 ║
║           8  ║    18 ║
╚══════════════╩═══════╝

【讨论】:

  • 我想你想要group by segments_id
  • 据我了解的问题,应该是正确的。 @Marc 你能确认/否认这是你想要的吗?请准备更大的样本,因为现在可能会产生误导。
  • @lad2025,这行得通。您能提醒一下为什么在此声明中使用GROUP_CONCAT-- 而不是,
  • @Marc 我添加了GROUP_CONCAT 作为评论。关键是当你使用GROUP BY 时,任何表达式都是SELECT 应该用聚合函数包装以避免不确定的结果。 SELECT em_id, GROUP_CONCAT(segments_id) AS segments_ids FROM ...
【解决方案3】:

不确定您在这里要求什么。您告诉我们您想要全选,但您发布的结果并未显示满足您条件的所有记录。如果您要排除一些 segment_id = 5 或 8 而不是 1 或 7 的记录,请详细说明条件。

SELECT segments_id,em_id 
FROM mytable 
WHERE ( segments_id = 5 OR segments_id = 8 ) 
AND segments_id != 1 
AND segments_id != 7;

这行得通!

【讨论】:

  • 这行不通! Demo 与所需的结果集进行比较。无论如何,OP想要什么有点不清楚
  • 是的,这将满足问题标题中解释的条件。 "我们需要从下表 mytable 中选择所有 segment_id 和 em_ids 的 segment_id 为 5 或 8 但不是 1 或 7" 。正如我在答案上方的标题中所述.....
  • 但根本不匹配所需的输出
  • 因为 (8 | 16) 不存在。我认为可以肯定地说期望的结果是错误的。
  • 您好,Kennedy,感谢您的尝试。您的语句没有返回结果。
猜你喜欢
  • 2010-10-11
  • 2022-01-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多