有没有更好的方法来排序这个查询?

Posted

技术标签:

【中文标题】有没有更好的方法来排序这个查询?【英文标题】:Is there a better way to sort this query? 【发布时间】:2009-12-16 16:47:20 【问题描述】:

我们以程序方式生成大量 SQL,而 SQL Server 正在扼杀我们。由于elsewhere 记录的一些问题,我们基本上会选择 TOP 2 ** 32 而不是 TOP 100 PERCENT。

注意:我们必须使用子查询。

这是我们的查询:

SELECT * FROM ( 
    SELECT [me].*, ROW_NUMBER() OVER( ORDER BY (SELECT(1)) ) 
    AS rno__row__index FROM (
       SELECT [me].[id], [me].[status] FROM (
          SELECT TOP 4294967296 [me].[id], [me].[status] FROM 
          [PurchaseOrders] [me] 
          LEFT JOIN [POLineItems] [line_items] 
          ON [line_items].[id] = [me].[id] 
          WHERE ( [line_items].[part_id] = ? ) 
          ORDER BY [me].[id] ASC
       ) [me]
   ) [me] 
) rno_subq 
WHERE rno__row__index BETWEEN 1 AND 25 

有没有更好的方法可以做到这一点,任何人都可以看到?

更新:这里是对整个子查询问题的一些说明:

我的问题的关键词是“程序上”。我需要能够可靠地封装结果集,以便它们可以像构建块一样堆叠在一起。例如,我想获取按制作它们的艺术家的姓名排序的前 10 张 cd,并获取每张 cd 的相关艺术家。我所做的是组装一个整体子选择,表示按加入的艺术家姓名排序的 cd,然后对其应用限制,然后将嵌套的子选择连接到艺术家表,然后才执行结果查询。隔离是必要的,因为请求订购的 cd 的代码与选择前 10 名 cd 的代码无关且忽略了,而后者又与请求相关艺术家的代码无关且忽略了。

现在您可能会说我可以将内部 ORDER BY 移动到 OVER() 子句中,但随后我破坏了封装,因为我必须选择连接表的列,以便稍后按它们排序。另一个问题是在一个别名下合并两个表。如果我在两个表中都有相同名称的列,则 select me.* 将在此停止并出现列名不明确的错误。

我愿意牺牲一点优化器的性能,但 2**32 对我来说似乎太过分了。所以我正在寻找中间立场。

【问题讨论】:

您使用 TOP 有什么特别的原因吗? IIRC,内部 ORDER BY 需要 TOP 才能工作。否则 SQL Server 会引发关于 ORDER BY 在子查询中无效的错误。 好的。当你说你必须使用子查询时,这是否意味着你必须使用两个嵌套查询,或者至少 1 个?因为你绝对可以消除其中一个。 我复制了您的代码并对我们的一些表运行了等效的代码,在 SQL Server 2005 下,它运行得非常高效 - 它在一秒钟内返回了 190 万条记录中的前 25 条。那么这个问题只是关于代码,而不是性能? ORDER BY only 如果应用于 final(即 outer) 选择。内部 ORDER BY 仅适用于 TOP 计数,它们的 ORDER NOT 保证保留在外部 SELECT 中。 【参考方案1】: 如果您想要 me.id 的前几行,只需在 ROW_NUMBER 的 ORDER BY 中询问即可。不要在子查询和 TOP 周围追逐你的尾巴。 如果连接表字段上有 WHERE 子句,则可以有外部 JOIN。所有外部字段都将为 NULL 并被 WHERE 过滤掉,因此实际上是内部连接。

.

WITH cteRowNumbered AS (
  SELECT [me].id, [me].status 
  ROW_NUMBER() OVER (ORDER BY me.id ASC) AS rno__row__index 
  FROM [PurchaseOrders] [me]           
  JOIN [POLineItems] [line_items] ON [line_items].[id] = [me].[id]          
  WHERE [line_items].[part_id] = ?)
SELECT me.id, me.status 
FROM cteRowNumbered
WHERE rno__row__index BETWEEN 1 and 25

我使用 CTE 而不是子查询只是因为我发现它们更具可读性。

【讨论】:

@Remus:谢谢 - 匆忙中,我没有更正加入。尽管如此,给定 where 子句 null line_item 行将被过滤掉。 @Ponies:是的,这正是我的观点。我想指出 WHERE 子句和 LEFT JOIN 不能混合使用,它们相互抵消以形成普通的内部 JOIN。 至于为什么我发布了一个与你几乎相同的查询,没有先阅读你的,也许只是添加评论......好吧,第一步是承认:“你好,我的名字是 Remus,我我是个 SO 代表上瘾者” 请注意,虽然这会返回前 25 行(根据 PurchaseOrders.id),但它不能保证这些行在结果集中出现的顺序。这比问题中给出的代码“更强”,它甚至不能保证将返回哪 25 行。 (OP 代码中的唯一保证是将从顶部 4294967296 的某处选择 25 行。)【参考方案2】:

用途:

SELECT x.*
  FROM (SELECT po.id, 
               po.status,
               ROW_NUMBER() OVER( ORDER BY po.id) AS rno__row__index
          FROM [PurchaseOrders] po
          JOIN [POLineItems] li ON li.id = po.id
         WHERE li.pat_id = ?) x
 WHERE x.rno__row__index BETWEEN 1 AND 25
ORDER BY po.id ASC

除非您为了简化示例而省略了详细信息,否则您提供的内容中不需要所有子查询。

【讨论】:

OP 确实说“我们必须 使用子查询”,但不清楚原因。 为了引用 ROW_NUMBER() 输出,这是正确的 - 保留在答案中。 我也没有理由看到有 TOP 子句,只需将 ORDER BY 移动到外部查询。我认为您实际上是通过将查询优化器添加到内部查询来弄乱查询优化器-默认情况下,他们不允许您在子查询中指定 ORDER 是有原因的。 @OMG Ponies:如果是这种情况,那么您的查询就可以完成这项工作,尽管我会从子查询中删除 TOP/ORDER BY 并在最外面的查询中添加一个 ORDER BY x.rno__row__index .但正如我所说,OP 不清楚 为什么 需要子查询;也许是因为它们是以某种疯狂的方式自动生成的。 @Luke:您至少有一个子查询,以便通过 row_number 函数调用使用分页。【参考方案3】:

向唯一一个看穿反对意见并在我们无权访问的大表上实际尝试查询的人表示敬意。对于其他所有人来说,这根本行不通(将返回随机行)-我们知道手册上说的是什么,并且我们知道这是一个hack-这就是我们首先提出问题的原因。然而,甚至不尝试就直接驳回查询是相当肤浅的。有人可以为我们提供一个真实的示例(带有前面的 CREATE/INSERT 语句)来证明上述查询有问题吗?

【讨论】:

【参考方案4】:

您的更新让事情变得更加清晰。我认为您使用的方法存在严重缺陷。虽然能够在您的应用程序中封装、可重用的代码是件好事,但前端应用程序与数据库完全不同。它们通常处理小型结构和针对这些结构运行的小型离散过程。另一方面,数据库通常处理数百万行甚至更多行的表。使用相同的方法通常会导致代码执行得非常糟糕以至于无法使用。即使它现在可以工作,它也很可能无法扩展,并且会在未来造成重大问题。

祝你好运,但我认为除了最小的数据库之外,这种方法在所有数据库中都不会有好结果。

【讨论】:

我将投反对票,因为它对我们的问题没有真正的帮助,但您也可以随意投下我有缺陷的问题 :-) 我不太担心帮助你。我希望它可以帮助看到这个问题的人,这样他们就不会落入和你一样的陷阱。这对我来说值得 -2 代表,所以我想我只能忍受它。 “愚人鄙视良知,而聪明人却把它铭记于心。” - 孔子

以上是关于有没有更好的方法来排序这个查询?的主要内容,如果未能解决你的问题,请参考以下文章

有没有更好的方法来编写这个查询

子选择或连接?有没有更好的方法来编写这个 mysql 查询?

有没有比多个子查询更好的方法来获得这个,也许有更多的连接?

有没有更好的方法来做这些 mysql 查询?

有没有更有效的方法来执行这个嵌套的 SQL 查询?

更好的查询策略,按文件哈希频率和文件大小对文件进行排序