【问题标题】:SQL query records within a range of boundaries and max/min outside the rangeSQL查询记录范围内的边界和范围外的最大值/最小值
【发布时间】:2011-06-10 20:10:01
【问题描述】:

我有以下三个简单的 T-SQL 查询。第一个是获取一定范围内的记录(DATETIME类型):

 SELECT value, timestamp 
 FROM myTable
 WHERE timestamp BETWEEN @startDT AND @endDT

第二个是获取最接近@startDT(DATETIME类型)的记录

 SELECT TOP 1
   value, timestamp
 FROM myTable
 WHERE timestamp > @startDT
 ORDER BY timestamp DESC

最后一个是获取@endDT之后最接近的记录:

 SELECT TOP 1
   value, timestamp
 FROM myTable
 WHERE timestamp < @endDT
 ORDER BY timestamp ASC

我想将以上三个查询的所有记录作为一组记录。我尝试使用 UNION,但似乎 UNION 中的子查询不允许使用 ORDER BY 子句。有没有有效的方法来获得我的结果?

 . .  * | * * * * * | * . . .
      start        end

上图只是将*s的记录显示为我需要的记录,而|...|是边界。

顺便说一句,myTable 中的数据量很大。我的理解 UNION 不是从 UNION 获取数据的有效方法。没有 UNION 获取数据的任何有效方法?

【问题讨论】:

  • 您确定通过ORDER BY value, timestamp 从这些查询中得到想要的结果吗?根据您的描述,我认为您只想按时间戳订购。否则,您将获得最接近边界的最小值。
  • 你说得对,我只用时间戳改了。
  • 很高兴看到这么多答案来满足我的需求。 @Piotr Auguscik 提到 min/max 在性能方面优于 Order + top 1。是对的吗?根据答案,我想出了一个“最小/最大+分组依据”的解决方案。不确定哪个更好。请记住,在我的案例中,myTable 包含大量记录。

标签: sql tsql


【解决方案1】:

如你所愿,没有 UNION。

MySQL(已测试

SELECT 
     dv1.timestamp, dv1.values
FROM 
     myTable AS dv1
WHERE 
    dv1.timestamp 
    BETWEEN (
           SELECT dv2.timestamp 
           FROM myTable AS dv2
           WHERE dv2.timestamp < '@START_DATE' 
           ORDER BY dv2.timestamp DESC 
           LIMIT 1
           )
    AND ( SELECT dv3.timestamp 
          FROM myTable AS dv3
          WHERE dv3.timestamp > '@END_DATE' 
          ORDER BY dv3.timestamp ASC 
          LIMIT 1
        )

编辑抱歉,我忘了注意 T-SQL。

T-SQL(未测试

SELECT 
     dv1.timestamp, dv1.values
FROM 
     myTable AS dv1
WHERE 
    dv1.timestamp 
    BETWEEN (
           SELECT TOP 1 dv2.timestamp 
           FROM myTable AS dv2
           WHERE dv2.timestamp >  @START_DATE 
           ORDER BY dv2.timestamp DESC
           )
    AND ( SELECT TOP 1 dv3.timestamp 
          FROM myTable AS dv3
          WHERE dv3.timestamp <  @END_DATE 
          ORDER BY dv3.timestamp ASC
        )

注意如果结果不正确,您可以只交换子查询(即运算符和 ASC/DESC)。

跳出框框思考:)

【讨论】:

  • 不错。我不是 100% 确定 UNION 是否效率较低。这是真的吗?
  • 顺便说一下,T-SQL 不支持 LIMIT 1。我觉得应该是TOP 1吧?
  • 有人说是的。事先,您可以使用EXPLAIN the_query 比较查询执行时间:) 但是,UNION 在某些情况下似乎很有用,但我更愿意避免使用它。
  • 是的,很抱歉。我在 MySQL 中进行了测试。但是,它也应该在 T-SQL 中工作。
  • 您当然意识到这毫无意义。如您所写,您的 where 子句等效于 WHERE dv1.timestamp btween @START_DATE and @END_DATE
【解决方案2】:

你可以使用 max/min 来获得你需要的值。 Order by +top 1 不是获得最大值的最佳方法,我可以在您的查询中看到。要对 n 个项目进行排序,其 O(n 到 2 次方),得到最大值应该只有 O(n)

【讨论】:

  • 如果我使用 max/min,我必须使用 GROUP BY。我的查询是简化的。实际上,一条记录中的字段超过2个,例如value1,value2,...,timestamp。
【解决方案3】:
SELECT value, timestamp 
 FROM myTable
 WHERE timestamp BETWEEN @startDT AND @endDT
union
select A.Value, A.TimeStamp
From (
 SELECT TOP 1
   value, timestamp
 FROM myTable
 WHERE timestamp > @startDT
 ORDER BY value, timestamp DESC ) A
Union
Select A.Value, A.TimeStamp
From (
 SELECT TOP 1
   value, timestamp
 FROM myTable
 WHERE timestamp < @endDT
 ORDER BY value, timestamp ASC ) A

【讨论】:

    【解决方案4】:

    您帖子中的第二个和第三个查询没有多大意义,因为

    WHERE timestamp > @startDT
    

    WHERE timestamp < @endDT
    

    导致时间戳范围内,但您的描述

     . .  * | * * * * * | * . . .
          start        end
    

    上图只是将*s的记录显示为我需要的记录,而|...|是边界。

    意思不同。

    所以遵循描述并使用以下映射

    myTable = Posts
    value = score
    timestamp  = creationdate 
    

    我在 data.stackexchange.com 上写了this query(根据 exodream 的答案修改,但比较运算符在 correct 反向)

    DECLARE @START_DATE datetime
    DECLARE @END_DATE datetime
    SET @START_DATE = '2010-10-20'
    SET @END_DATE = '2010-11-01'
    
    SELECT score, 
           creationdate 
    FROM   posts 
    WHERE  creationdate BETWEEN (SELECT TOP 1 creationdate 
                                 FROM   posts 
                                 WHERE  creationdate < @START_DATE 
                                 ORDER  BY creationdate DESC) 
                                 AND 
                                       (SELECT TOP 1 creationdate 
                                        FROM   posts 
                                        WHERE  creationdate > @END_DATE 
                                        ORDER  BY creationdate ASC) 
    ORDER by creationDate
    

    哪些输出

    score creationdate        
    ----- ------------------- 
    4     2010-10-19 23:55:48 
    3     2010-10-20 2:24:50   
    6     2010-10-20 2:55:54  
    ...
    ...
    7     2010-10-31 23:14:48 
    4     2010-10-31 23:18:17 
    4     2010-10-31 23:18:48 
    0     2010-11-01 3:59:38  
    
    (382 row(s) affected)
    

    注意第一行和最后一行如何超出范围的限制

    【讨论】:

      【解决方案5】:

      您可以将这些有序查询放入子查询中,以避免无法直接 UNION 它们。有点烦人,但它会让你得到你想要的。

      SELECT value, timestamp 
       FROM myTable
       WHERE timestamp BETWEEN @startDT AND @endDT
       UNION
       SELECT value, timestamp 
       FROM (
       SELECT TOP 1
         value, timestamp
       FROM myTable
       WHERE timestamp > @startDT
       ORDER BY value, timestamp DESC 
       ) x
       UNION
       SELECT value, timestamp
       FROM (
       SELECT TOP 1
         value, timestamp
       FROM myTable
       WHERE timestamp < @endDT
       ORDER BY value, timestamp ASC
       ) x
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-03-22
        • 2019-02-11
        • 1970-01-01
        • 2020-10-03
        • 2016-08-31
        • 1970-01-01
        相关资源
        最近更新 更多