我应该在子查询中使用 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)的主要内容,如果未能解决你的问题,请参考以下文章

错误:语法错误:应为“)”,但在子查询中得到语句结束语句

在子查询中引用外部查询

在子查询 JOIN 中引用外部查询

在子查询的FROM中使用Postgres窗口语句

在子查询中使用聚合和窗口函数

MySQL 未在子查询中使用 INDEX