我应该在子查询中使用 Top(1)
Posted
技术标签:
【中文标题】我应该在子查询中使用 Top(1)【英文标题】:Should I use Top(1) in a SubQuery 【发布时间】:2009-01-30 16:15:35 【问题描述】:示例查询:
select *
from A join B on A.ID = B.SOMEVALUE
where A.VALUE="something" and
B.ID =
(select ID from B where SOMEVALUE = A.ID and
THISDATE = (select max(SOMEDATE) from B where ...))
所以,如果您可以阅读 SQL,您应该会看到我正在执行几个相关的子查询来缩小 join 的结果。 (是的,这太简单了)。
在某些情况下子查询:
select ID from B where SOMEVALUE = A.ID and
THISDATE = (select max(SOMEDATE) from B where ...)
可以返回超过1个值,这会导致错误
"子查询返回超过 1 个值。 这是不允许的,当 子查询遵循 =、!=、、>= 或者当子查询被用作 表达。”
我完全期待。这显然不是一件好事,我有代码(希望)首先防止这些重复项进入数据库(即表 B 应该只有 1 行匹配
SOMEVALUE = A.ID and max(SOMEDATE)
标准),但最终用户如果没有创造性地寻找我想不出的破坏软件的方法,那将一事无成。
现在回答我的问题:
将第一个子查询改为
会更好吗select top 1 * from B ...
防止用户在/如果(希望永远不会)出现这种情况时看到错误,或者让错误发生。我倾向于不添加顶部语句并让错误出现,而不是让用户看到可能不正确的数据。我想知道在这种情况下是否有人对最佳实践有任何想法......
【问题讨论】:
【参考方案1】:通常 TOP 1 是个好主意。
考虑一个包含数百万行的大表,在您匹配的列上没有索引,但您只查找单行。
SELECT TOP 1 意味着一旦找到一项,表扫描就会停止。
没有 TOP 1,表扫描将一直持续到最后。
与任何涉及扫描(或蛮力)进行搜索的事情一样。使用 TOP 1,平均应该比不使用 TOP 1 快 50%。
但是,根据您需要返回的内容,通常可以通过使用 EXISTS 来获得真正的性能提升。
而不是写
SELECT * FROM table t
WHERE t.id = (SELECT TOP 1 foreignid from table2)
你可以使用
SELECT * FROM table t
WHERE EXISTS (SELECT 1 from table2 WHERE foreignid = t.id)
【讨论】:
【参考方案2】:你为什么要加入表 A 和 B...然后在子查询中从 B 中选择...并与 A 中的列进行比较???
这不是等效的吗:
select *
from A join B on A.ID = B.SOMEVALUE
where A.VALUE="something" and
THISDATE = (select max(SOMEDATE) from B where ...))
另外,如果您希望从整个查询中得到一共有一行...这行不通:
select top 1 *
from A join B on A.ID = B.SOMEVALUE
where A.VALUE="something"
Order by B.SOMEDATE DESC
【讨论】:
正如我所说,这个例子过于简单化了,你确实让我停下来测试这个想法,但是按照你的建议更改(真实)查询不会产生等效的结果 第二个查询不起作用,因为从最外面的查询我确实想要多行。但是在让它慢了一会儿之后,我相信我可能已经过火了,第一个查询可能会起作用。唯一的区别是我会得到多行,而我的查询会产生错误 在这两个示例中,您都可以将“A.VALUE='something'”作为 ON 加入条件的一部分,通过提前过滤而不是先进行加入,您可以节省几毫秒的时间,然后过滤【参考方案3】:编码实践:将top1放在需要返回单个值的子查询中。
优点:
允许继续执行。 允许查询忽略无关紧要的额外值。 找到第一个值后停止寻找新值(高性能)。缺点:
防止查询抱怨重要的额外值。 如果查询中未指定 order,则无法控制 top1 将选择哪个元素。这可能会在第二次执行查询时导致不同的结果。【讨论】:
【参考方案4】:我推荐 TOP 1 方法。它可能会帮助性能(不太可能伤害它)。
没有它你会发现错误的想法是光荣的,但有点错位。如果从现在起几个月后这里发生错误,那么它发生的原因或正在发生的事情根本不会直观。相反,我会专注于在其他地方强制执行数据完整性。
【讨论】:
【参考方案5】:为什么不在子选择的末尾使用LIMIT 1
?
【讨论】:
这和加TOP 1一模一样,只是sql(mysql等)的另一种风味 同意 Michael 的观点,这是一种不同的风格,所以 LIMIT 1 可能在这种语法中实际上不起作用。 没有回答这个问题,但它确实阐明了这个问题对我们这些不熟悉 TOP 1 语法的人的建议。【参考方案6】:如果您只想返回一行,则可以执行以下两项操作之一。首先是改变你的平等检查,而不是像这样检查遏制
select ID from B where SOMEVALUE = A.ID and
THISDATE IN (select max(SOMEDATE) from B where ...)
(注意输入)
其次你可以这样做
select TOP 1 ID from B where SOMEVALUE = A.ID and
THISDATE IN (select max(SOMEDATE) from B where ...)
如果您只寻找一个值。
或者如您所说,您可以将子查询更改为 SELECT TOP 1,这在我看来是可以的,因为只要 WHERE 子句不依赖于外部查询,它可能会执行嵌套查询只需要一次,然后像这样依赖静态存储的值
select ID from B where SOMEVALUE = A.ID and
THISDATE = (select TOP 1 * from B where ...)
所以有一些选择,但我不完全确定它们的效率。
【讨论】:
我不能使用 in 因为我希望子查询只返回一行,在我试图描述的场景中,它会返回多行以上是关于我应该在子查询中使用 Top(1)的主要内容,如果未能解决你的问题,请参考以下文章