【问题标题】:How to select first 'N' records from a database containing million records?如何从包含百万条记录的数据库中选择前“N”条记录?
【发布时间】:2010-11-27 11:00:53
【问题描述】:

我有一个包含数百万条记录的 oracle 数据库。我正在尝试编写一个 SQL 查询,该查询根据特定条件从数据库中返回前“N”条排序记录(比如 100 条记录)。

SELECT * 
FROM myTable 
Where SIZE > 2000 
ORDER BY NAME DESC

然后以编程方式选择前 N 条记录。

这种方法的问题是:

  • 查询结果成50万 记录和“按名称排序”原因 所有要在 NAME 上按降序排序的记录。这种排序需要很多时间。 (将近 30-40 秒。如果我省略 ORDER BY,则只需 1 秒)。
  • 在我感兴趣的排序之后 只有前 N (100) 条记录。所以完整记录的排序是没有用的。

我的问题是:

  1. 是否可以在 查询自己? (这样排序只适用于 N 条记录,查询变得更快)。
  2. 在 SQL 中改进查询排序的任何更好的方法 只有 N 个元素并快速返回 时间。

【问题讨论】:

    标签: sql oracle sorting sql-order-by


    【解决方案1】:

    仅供参考,在Oracle 12c 中,可以使用FETCH 子句完成此任务。您可以查看 here 以获取有关此问题的示例和其他参考链接。

    【讨论】:

      【解决方案2】:

      您的问题是每次运行查询时都会进行排序。您可以通过使用索引来消除排序操作 - 优化器可以使用索引来消除排序操作 - 如果已排序的列被声明为 NOT NULL。

      (如果该列可以为空,仍然可以通过 (a) 向查询添加 NOT NULL 谓词,或 (b) 添加基于函数的索引并相应地修改 ORDER BY 子句)。

      【讨论】:

        【解决方案3】:

        如果您的目的是找到 100 个随机行并在之后对它们进行排序,那么 Lasse's solution 是正确的。如果我认为您希望前 100 行按名称排序,而丢弃其他行,您将构建如下查询:

        SELECT * 
          FROM (SELECT * 
                  FROM myTable 
                 WHERE SIZE > 2000 ORDER BY NAME DESC) 
         WHERE ROWNUM <= 100
        

        优化器将理解这是一个 TOP-N 查询,并且将能够使用 NAME 上的索引。它不必对整个结果集进行排序,它只会从索引的末尾开始并向后读取并在 100 行后停止。

        您还可以在原始查询中添加提示,让优化器了解您只对第一行感兴趣。这可能会生成类似的访问路径:

        SELECT /*+ FIRST_ROWS*/* FROM myTable WHERE SIZE > 2000 ORDER BY NAME DESC
        

        编辑: 仅将 AND rownum &lt;= 100 添加到查询中是行不通的,因为在 Oracle 中,rownum 被归因于 before 排序:这就是您必须使用子查询的原因。如果没有子查询,Oracle 将随机选择 100 行然后对它们进行排序。

        【讨论】:

          【解决方案4】:

          This 显示如何根据您的 Oracle 版本选择前 N 行。

          从 Oracle 9i 开始,RANK() 和 DENSE_RANK() 函数可用于 确定前 N 行。例子:

          根据以下条件获取前 10 名员工 他们的工资

          选择名称,sal FROM ( SELECT ename, sal, RANK() OVER (ORDER BY Sal DESC)sal_rank FROM emp ) WHERE sal_rank

          选择进入前 10 名的员工 工资

          选择名称,sal FROM ( SELECT 姓名,萨尔,DENSE_RANK() OVER (ORDER BY 萨尔 DESC) sal_dense_rank FROM emp ) WHERE sal_dense_rank

          两者的区别解释here

          【讨论】:

            【解决方案5】:

            添加这个:

             AND rownum <= 100
            

            到你的 WHERE 子句。

            但是,这不会满足您的要求。

            如果您想选择 100 个随机行,对它们进行排序,然后返回它们,您必须先制定一个没有 ORDER BY 的查询,然后将其限制为 100 行,然后从中选择并排序。

            可以工作,但不幸的是我没有可用于测试的 Oracle 服务器:

            SELECT *
            FROM (
                SELECT *
                FROM myTable
                WHERE SIZE > 2000
                  AND rownum <= 100
                ) x
            ORDER BY NAME DESC
            

            但请注意那里的“随机”部分,您是说“给我 100 行 SIZE > 2000,我不在乎哪 100 行”。

            这真的是你想要的吗?

            不,您实际上不会得到随机结果,因为每次查询服务器时它都会改变,但您受查询优化器的支配。如果该表的数据加载和索引统计信息随时间发生变化,则在某些时候您可能会获得与上一个查询不同的数据。

            【讨论】:

            • 感谢您的回答。我的查询不是随机获得 100。我想获得前 100 条排序记录。例如:如果记录是 1、5、8、2、14、3、6、7。并且如果我想要 3 条记录,那么答案将是 (1,2,3)
            • 那么您确实希望它们首先排序,如果对百万行进行排序需要很多时间,那将无济于事。您所做的只是避免通过网络检索所有行,排序仍然需要运行。
            • 但是,Oracle 足够聪明,可以保住前 100 名的结果。如果下一行在 100 之外,则将其丢弃。这样,它就不必对整个事物进行排序。这是 O(n) 而不是 O(n log n)
            猜你喜欢
            • 2016-03-06
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2019-02-28
            • 2011-11-29
            • 1970-01-01
            • 2023-01-07
            相关资源
            最近更新 更多