【问题标题】:how to avoid duplicate on Joining two tables如何避免在连接两个表时出现重复
【发布时间】:2011-11-11 20:46:03
【问题描述】:
Student Table

 SID    Name
 1      A
 2      B
 3      C

 Marks Table

 id mark    subject 
 1  50  physics
 2  40  biology
 1  50  chemistry
 3  30  mathematics



SELECT distinct(std.id),std.name,m.mark, row_number() over() as rownum FROM 

student std JOIN marks m ON std.id=m.id AND m.mark=50

即使在使用 disticnt 之后,这个结果也是 A 的 2 倍。我的预期结果将只有一个 A。如果我将 row_number() over() 删除为 rownum,它的工作正常。为什么会这样?如何解决。 AM 使用 DB2!!

【问题讨论】:

  • 如果你不想在物理和化学方面都获得 A 的成绩,那么加入 Student and Marks 有什么意义?
  • 我只想要至少一科50分的学生

标签: sql join db2 row-number


【解决方案1】:

marks 表中有两行 id = 1 和 mark = 50.. 因此,对于学生表中的每一行,您将在输出中得到两行... 如果你只想要一个,你必须做一个group By

 SELECT std.id, std.name, m.mark, row_number() 
  over() as rownum 
 FROM student std 
    JOIN marks m 
       ON m.id=std.id AND m.mark=50 
 Group By std.id, std.name, m.mark

【讨论】:

  • 总是将连接谓词放在连接中。直到生成整个结果集之后才会评估 where 子句谓词,因此在整个处理过程中都会携带不需要的行,并且在某些外部连接场景中,将谓词放在 where 子句中会产生不正确的结果。最后,将连接谓词放在连接中,将它们放在它们所涉及的表附近,而不是放在最后,这增加了查询的清晰度。
  • 另外,有时您想多次加入同一个表,每次加入都有不同的谓词。你将如何在 Where 子句中做到这一点?
  • @CharlesBretana 每个主要的关系数据库都足够聪明,可以在实际评估查询时将过滤条件放在JOIN 条件之前;他们相当擅长减少他们必须执行的操作数量(也就是优化)。一般而言,无论您在WHEREON 子句中放置m.mark = 50 之类的内容,都不会影响查询计划。因此,美学改进是唯一真正的考虑因素(至少对于内部连接),将这些条件放在 WHERE 子句中要直观得多。
  • 不,它们不是,因为将谓词放在这两个地方的结果是(或可以是)不同的,优化器不能被设计成读懂查询作者的想法.在一种情况下,谓词在连接之前进行评估,而在另一种情况下,它在连接之后进行评估。
  • @CharlesBretana 结果只能与非INNER 连接不同,您的答案中的查询不是其中之一。使用您最喜欢的数据库在两个地方尝试一下,并检查查询计划是否不同。他们不会的。像这样重新安排步骤是优化的主要部分。事实上,对于非INNER 连接,将过滤器放在ON 子句中会使查询变慢,因为它必须包含来自未过滤表的更多行。是的,这是一个不同的结果集,但也很少需要它。想要过滤掉与过滤器不匹配的任何内容更为常见。
【解决方案2】:

现在您已将问题澄清为:

我想找到至少一门科目成绩为 50 分的所有学生。我会使用查询:

SELECT student.id, '50'
FROM student 
WHERE EXISTS (SELECT 1 FROM marks WHERE marks.id = student.id AND marks.mark = 50)

这也使您可以灵活地更改标准,例如至少 1 分不超过 50 分。

【讨论】:

  • +1 EXISTS (semi-join) 是你真正想做的。您不需要完全联接,因为您不需要所有额外的数据。与执行完全联接的额外工作然后执行更多额外工作以将数据集缩减为仅您想要的内容相比,此查询更有效。
  • +1 用于使用 WHERE EXISTS,但您忘记了他的 rownum 列。
  • @orbfish 我真的不明白 OP 想用row_number() over () 做什么。 @zod 也许你可以澄清一下?
  • row_number() over () 用于分页,就像mysql中的LIMIT
  • 阅读row_number() over () 它只是为结果集的每一行提供了一个连续的行号,因此它当然可以被包含在内。 FROM 和 WHERE 是解决方案的重要方面,但感谢您推动我学习新知识。
【解决方案3】:

与 Charles 的回答类似,但您总是希望将谓词 (mark=50) 放在 WHERE 子句中,因此您在加入之前进行过滤。如果这只是家庭作业,那可能没关系,但如果您遇到任何真实数据,您会想要记住这一点。

SELECT std.sid,
       std.name,
       m.mark,
       row_number() over() AS rownum 
 FROM student std 
      JOIN marks m 
        ON std.sid=m.id
WHERE m.mark=50
GROUP BY std.sid, std.name, m.mark

【讨论】:

  • 我发现大多数查询优化器通常可以找出应用过滤器的最佳时间,而不管子句的位置如何。这是 DB2 的怪癖吗?
  • @M_M - 否 - DB2(至少在 iSeries 上)似乎生成了相同的解释计划,并且似乎将两个查询视为等效(使用相同的访问路径);至少对于像这样简单的例子。 @All - 在WHERE 子句与JOIN 子句中放置条件将总是返回相同的结果(这在使用LEFT 和/或EXCEPTION 连接时最相关) ,所以条件语句应该放在它们会产生正确结果的地方,而不是出于性能原因(DB2 优化器也很不错)。
  • @M_M - 我在 Oracle 中很熟悉它。有时它会弄明白,但有时它会认为你真的是在用 SQL 说的话,最后却要花很长时间。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-12-06
  • 2013-01-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多