【问题标题】:Expand query beyond that specifed in the WHERE clause将查询扩展到 WHERE 子句中指定的范围之外
【发布时间】:2009-03-13 18:01:25
【问题描述】:

一定有更好的方法来编写这个查询。

我想选择一对日期之间的所有数据。理想情况下,结果集的第一行和最后一行将是 WHERE 子句中指定的行。如果这些行不存在,我希望这些行在请求的范围之前和之后。

一个例子:

如果我的数据是:

...
135321, 20090311 10:15:00
135321, 20090311 10:45:00
135321, 20090311 11:00:00
135321, 20090311 11:15:00
135321, 20090311 11:30:00
135321, 20090311 12:30:00
...

查询是:

    SELECT * 
    FROM data_bahf 
    WHERE param_id = 135321 
    AND datetime >= '20090311 10:30:00' 
    AND datetime <= '20090311 12:00:00'

我希望返回的数据包括 10:15 的行和 12:30 的行。不只是那些严格符合 WHERE 子句的人。

这是我想出的最好的。

SELECT * FROM (
    SELECT * 
    FROM data_bahf 
    WHERE param_id = 135321 
    AND datetime > '20090311 10:30:00' 
    AND datetime < '20090311 12:00:00'

    UNION

    (
        SELECT * FROM data_bahf 
        WHERE param_id = 135321 
        AND datetime <= '20090311 10:30:00' 
        ORDER BY datetime desc
        LIMIT 1
    )

    UNION

    (
        SELECT * FROM data_bahf 
        WHERE param_id = 135321 
        AND datetime >= '20090311 12:00:00'
        ORDER BY datetime asc
        LIMIT 1
    )
) 
AS A
ORDER BY datetime

(暂时忽略 SELECT * 的使用)

编辑: 我在 param_id、datetime 和 (param_id, datetime) 上有索引

【问题讨论】:

  • 您需要将 param_id 添加到 ORDER BY 子句中,以便使用 (param_id, datetime) 上的索引,如下所示: ORDER BY param_id, datetime; ORDER BY param_id DESC,日期时间 DESC

标签: sql postgresql union


【解决方案1】:

我会这样说:

SELECT 
  o.* 
FROM 
  data_bahf o
WHERE 
  o.param_id = 135321 
  AND o.datetime BETWEEN
  ISNULL(
    (
      SELECT   MAX(datetime) 
      FROM     data_bahf i
      WHERE    i.param_id = 135321 AND i.datetime <= '20090311 10:30:00'
    ),
    '0001-01-01 00:00:00'
  )
  AND
  ISNULL(
    (
      SELECT   MIN(datetime) 
      FROM     data_bahf i
      WHERE    i.param_id = 135321 AND i.datetime >= '20090311 12:00:00'
    ),
    '9999-12-31 23:59:59'
  )

编辑:添加了后备。
当子查询没有匹配的行时,会产生一个NULL 值,必须被ISNULL() 捕获,否则BETWEEN 操作符将失败,主查询将不返回任何行。

【讨论】:

  • 它看起来确实更好 - 但它慢了 10 倍!
  • 您的日期时间字段有索引吗? (我认为您的 param_id 字段中有一个是理所当然的。)
  • 我愿意,并且查询正在使用它。原始查询的时间为 10 毫秒,新查询的时间为 200 毫秒。从绝对意义上讲,这并不多,但我确实有很多这样的查询要运行。只有大约 200k 行。
  • 恐怕我不是 PostgreSQL 专家。对于 200k 行,性能应该非常快(刚刚在 SQL Server 上对类似数量的实时数据进行了测试,速度非常快)你能发布两个变体的查询执行计划 (EXPLAIN) 吗?
  • Postgres 无法缓存相关的内部查询,它会在每次连接时重新评估。这就是为什么您的查询很慢。为了加快速度,您需要将常量传递给每个内部查询,或者将相关子查询包装为不相关子查询的结果。
【解决方案2】:

首先,确保您在 (param_id, datetime)

上有一个复合索引

其次,这样查询:

SELECT  *
FROM    data_bahf
WHERE   param_id = 135321
        AND datetime BETWEEN
        COALESCE(
        (
        SELECT  MAX(datetime)
        FROM    data_bahf
        WHERE   param_id = 135321
              AND datetime <= '2009-01-01 00:00:00'
        ), '0001-01-01')
        AND 
        COALESCE(
        (
        SELECT  MIN(datetime)
        FROM    data_bahf
        WHERE   param_id = 135321
              AND datetime >= '2009-01-02 00:00:00'
        ), '9999-01-01')

刚刚检查过,它在1.215 ms 中运行200,000 行的示例表

【讨论】:

  • 你能说为什么有一个复合索引是有益的吗? (没有批评,我只是想知道)
  • 因为如果没有,则需要在搜索 MAX 和 MIN 时过滤掉所有不匹配的 param_id。
  • 当你已经有一个关于“param_id”的索引(我把它当作这个问题的读物)我希望优化器在它对剩余的执行 MAX() 或 MIN() 之前应用它行。所以我认为有两个独立的索引可以产生相同的性能。 (或者不是?)
  • 不,他们不会。优化器可以使用 param_id 上的任一索引,在这种情况下,它需要使用此 param_id 扫描所有行以找到 MIN 日期时间;或日期时间的索引,在这种情况下,它需要按降序扫描所有行,直到找到第一个匹配的 param_id
  • 当你有一个复合索引时,你需要在给定的 (param_id, datetime) 对的左边找到一个叶子。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-03-16
  • 2021-09-11
  • 1970-01-01
  • 2013-02-12
  • 1970-01-01
  • 2013-01-25
  • 1970-01-01
相关资源
最近更新 更多