【问题标题】:SQL Select records from Mutli million recordsSQL 从数百万条记录中选择记录
【发布时间】:2016-05-27 07:39:18
【问题描述】:

我需要根据日期范围从包含 500 万条记录的表中选择记录。返回结果需要 19 秒。只有 ID 字段是主键,日期字段不被索引。

选择的记录数只有500条。可以做些什么来优化查询?

 select * 
 from productdetail 
 where createddate >= '01 Jan 2016' 
      and createddate <= '05 mar 2016'

任何建议/帮助表示赞赏。

【问题讨论】:

  • 是否有特定原因阻止您将createddate 编入索引?
  • 您可能想要使用您的搜索技能并了解更多关于查询优化、索引、分区以及如何处理大型表的一般信息。您所提供信息的问题过于广泛,无法提供一个好的答案。每个数据库服务器都有些不同(考虑到硬件、设置、其他运行的应用程序、基础设施)。我们可以提出解决方案,但您必须自行研究才能找到最佳解决方案。
  • 只用19秒搜索500万条无索引记录!?!这一点也不坏。

标签: sql sql-server sql-server-2008


【解决方案1】:

一般来说,没有更好的方法,搜索非索引列必须执行线性搜索。 但是在您的情况下,如果主键是自动递增的并且createddate 表示记录创建日期(这意味着更大的主键,稍后createddate 日期/时间记录),您可以手动对主键执行二进制搜索。

下面是一个例子,ID 是一个从 0 开始的自增主键(索引)。每个选择语句只涉及ID,因此查询速度很快。 请注意,这只有在 createddate 相对于主键 ID 排序时才有效:

DECLARE @lowerBoundID INT
DECLARE @upperBoundID INT
DECLARE @maxID INT
SELECT @maxID = MAX(ID) FROM productdetail
SET @lowerBoundID = 0
SET @upperBoundID = @maxID

IF (SELECT createddate FROM productdetail WHERE ID = @lowerBoundID) > '05 mar 2016' OR (SELECT createddate FROM productdetail WHERE ID = @upperBoundID) < '01 Jan 2016'
BEGIN
    DECLARE @first INT
    DECLARE @last INT
    DECLARE @middle INT

    SET @first = 0
    SET @last = @maxID
    SET @middle = (@first + @last) / 2
    WHILE @last - @first > 1
    BEGIN
        IF (SELECT createddate FROM productdetail WHERE ID = @middle) < '01 Jan 2016'
            SET @first = @middle
        ELSE
            SET @last = @middle

        SET @middle = (@first + @last) / 2
    END

    IF (SELECT createddate FROM productdetail WHERE ID = @first) < '01 Jan 2016'
        SET @lowerBoundID = @last
    ELSE
        SET @lowerBoundID = @first


    SET @first = 0
    SET @last = @maxID
    SET @middle = (@first + @last) / 2
    WHILE @last - @first > 1
    BEGIN
        IF (SELECT createddate FROM productdetail WHERE ID = @middle) > '05 mar 2016'
            SET @last = @middle
        ELSE
            SET @first = @middle

        SET @middle = (@first + @last) / 2
    END

    IF (SELECT createddate FROM productdetail WHERE ID = @last) > '05 mar 2016'
        SET @upperBoundID = @first
    ELSE
        SET @upperBoundID = @last

    SELECT * FROM productdetail WHERE ID >= @lowerBoundID AND ID <= @upperBoundID
END

【讨论】:

  • 二分查找是什么意思?如何在 SELECT 语句中进行二分查找?
  • 有一个很大的假设是createdate 的顺序必须与主键相同。如果是这样,可以使用T-SQLWhile语句对主键进行二分查找,找到createdate在句点内的主键值的边界。生成的主键范围也是 createdate 的主键范围。
  • @MangoWong 你能详细说明一下吗?二进制搜索适用于内存中的数组(您需要随机访问以“跳跃”并在每次迭代中将范围减半)。在不将整个表加载到内存中的情况下,您将如何进行二分搜索(并首先否定这样做的原因)?
  • @BrankoDimitrijevic 如果主键自增1,则可以像在数组中搜索一样用作索引(按索引列查询接近O(logN)操作)。添加了一个 T-SQL 示例。
  • @MangoWong 很酷,但是......在真正的生产服务器上你有多少?为了好玩 - 是的,干得好。
猜你喜欢
  • 2016-03-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多