Postgresql 优化/索引时区查询

Postgresql 优化/索引时区查询,postgresql,timezone,Postgresql,Timezone,我想知道是否有人对如何优化这个查询以便它可以使用索引有任何建议?我们有与数据库中的属性相关联的保留。每个属性都有一个时区集。我们要查询的是“在给定日期(例如今天)开始的所有预订” 相关的查询片段是 reservations.start_on::Date = COALESCE((current_timestamp at time zone properties.time_zone), current_timestamp)::Date 正如您所期望的那样,属性表在查询的前面被联接 正如你可能猜到的

我想知道是否有人对如何优化这个查询以便它可以使用索引有任何建议?我们有与数据库中的属性相关联的保留。每个属性都有一个时区集。我们要查询的是“在给定日期(例如今天)开始的所有预订”

相关的查询片段是

reservations.start_on::Date = COALESCE((current_timestamp at time zone properties.time_zone), current_timestamp)::Date
正如您所期望的那样,属性表在查询的前面被联接

正如你可能猜到的,问题在于时区。我们不能简单地要求
start_on={date}
,因为当我们说
{date}
时,我们实际上指的是该属性的
时区中的{date
},根据当前时间,它可能不是
{date}
<代码>{date}将根据我们的应用程序服务器的时区作为,例如,
4/7/2014
,但如果预订的财产位于澳大利亚悉尼,我们实际上希望包括从
4/8/2014
开始的预订

----编辑----

一些补充资料

reservations.start_on存储为type
date

properties.time_zone存储为
字符变化(255),默认为“America/Los_Angeles”::字符变化。

预订的开始日期存储为您在酒店时区的日期。在大多数情况下,需要解释的部分是我们查询的日期,以便将其移动到各个属性时区中的适当日期

我当然对其他方法持开放态度,但到目前为止,这是我所能想到的全部。。。基本上,我想问DB几个问题:

  • 什么预订从“今天”开始
  • 哪些预订在将来有开始日期
  • 哪些预订在过去有开始日期
  • 开始日期,作为一个日期,显然实际上是一个时间范围,所以你不能仅仅说
    Start\u on>now()
    ,例如,因为时区

    表定义:

    ----编辑2----

    我尝试切换到使用tsrange启动。该范围从酒店的时区时移到UTC。这意味着2014年11月4日在悉尼的开始日期存储为
    ['4/10/2014 14:00:00','4/11/2014 13:59:59']
    。我对start_on列的tsrange版本有一个要点索引

    这似乎工作完美,并返回正确的结果。它使用gist索引进行特定时间的@>查询,速度极快。不幸的是,它没有对所有范围操作使用gist索引

    下面是一个要点(哈哈),并举例说明了我们的一些问题:

    示例中显示的列,
    arrival\u day
    相当于
    start\u on
    ,但它是一个tsrange。我还没有删除列上的旧
    start\u


    一些现在比较慢的查询是“即将到来的”或“过去的”预订,我必须构建一个没有上限或下限的tsrange。我似乎不知道如何使用>进行查询,该查询接受一个元素,而不是像使用@>

    那样接受一个范围。如果您实际有一个类型为
    timestamp
    的列,并根据当前时区对其进行解释(部分),并且该时区可能会有所不同,那么通常不可能使用索引。您只能在
    不可变的
    数据上建立索引

    更新后: 要回答这些问题:

  • 什么预订从“今天”开始
  • 哪些预订在将来有开始日期
  • 哪些预订在过去有开始日期
  • 。。。你最好去商店。仅仅一个日期是不够精确的

    只要我们只对本地“今天”(由当前时区定义)感兴趣,我们就不需要明确保存时区。我们不在乎它发生在世界的什么地方,我们只需要一个绝对的时间来比较

    然后,要从“今天”开始预订,只需:

    但这是因为
    start\u on::date
    是一个派生表达式,我们也无法为此构建函数索引(没有肮脏的技巧),因为表达式取决于当前时区,并且不是不可变的

    相反,与UTC时间中“我们的”一天的开始和结束相比:

    SELECT *
    FROM   reservations
    WHERE  start_on >= current_date::timestamptz
    AND    start_on < (current_date + 1)::timestamptz; -- exclude upper border
    
    演示 SQL Fiddle已关闭ATM。下面是一个小演示,有助于理解:

    CREATE TEMP TABLE reservations (
       reservation_id serial
     , start_on timestamptz NOT NULL
     , time_zone text);    -- we don't need this
    
    INSERT INTO reservations (start_on, time_zone) VALUES
      ('2014-04-09 01:00+02', 'Europe/Vienna')
    , ('2014-04-09 23:00+02', 'Europe/Vienna')
    , ('2014-04-09 01:00+00', 'UTC')    -- the value is independent of the time zone
    , ('2014-04-09 23:00+00', 'UTC')    -- only display depends on current time zone
    , ('2014-04-09 01:00-07', 'America/Los_Angeles')
    , ('2014-04-09 23:00-07', 'America/Los_Angeles');
    
    SELECT start_on, time_zone 
         , start_on::timestamp             AS local_ts
         , start_on AT TIME ZONE time_zone AS ts_at_tz
         , current_date::timestamptz       AS lower_bound
         , (current_date + 1)::timestamptz AS upper_bound
    FROM   reservations
    WHERE  start_on >= current_date::timestamptz
    AND    start_on < (current_date + 1)::timestamptz;
    
    创建临时表保留(
    预定号序列号
    ,在时间戳上启动\u不为空
    ,时区文字);--我们不需要这个
    插入预订(开始、时区)值
    (‘2014-04-09 01:00+02’、‘欧洲/维也纳’)
    ,('2014-04-09 23:00+02','欧洲/维也纳')
    ,('2014-04-09 01:00+00','UTC')--该值与时区无关
    ,('2014-04-09 23:00+00','UTC')--仅显示取决于当前时区
    ,('2014-04-09 01:00-07','America/Los_Angeles')
    ,(“2014-04-09 23:00-07”,“美国/洛杉矶”);
    选择开始,时区
    ,start_on::时间戳为本地\u ts
    ,在时区启动时区为Tsu AT tz
    ,当前日期::timestamptz为下限
    ,(当前日期+1)::时间戳作为上限
    保留地
    其中开始日期>=当前日期::时间戳
    并在<(当前日期+1)开始:时间戳;
    
    此处有更多说明和链接:

    看看我的答案,这里有一个解决方法也可以为您提供:


    表定义应该在问题中,显示有关所涉及列的详细信息(
    \d tbl
    在psql中)。还有你的Postgres版本。谢谢,我用更多信息更新了这个问题,并链接到了属性和预订的表定义。到目前为止,我唯一能想到的是存储一个“utc开始时间”列,该列预先计算相对于utc的日期,这样“utc开始时间”可能并不总是等于“utc开始时间”。然后使用utc上的start_进行这些查询。这里的缺点是,如果编辑属性以调整其时区
    CREATE INDEX ON reservations (start_on);
    
    CREATE TEMP TABLE reservations (
       reservation_id serial
     , start_on timestamptz NOT NULL
     , time_zone text);    -- we don't need this
    
    INSERT INTO reservations (start_on, time_zone) VALUES
      ('2014-04-09 01:00+02', 'Europe/Vienna')
    , ('2014-04-09 23:00+02', 'Europe/Vienna')
    , ('2014-04-09 01:00+00', 'UTC')    -- the value is independent of the time zone
    , ('2014-04-09 23:00+00', 'UTC')    -- only display depends on current time zone
    , ('2014-04-09 01:00-07', 'America/Los_Angeles')
    , ('2014-04-09 23:00-07', 'America/Los_Angeles');
    
    SELECT start_on, time_zone 
         , start_on::timestamp             AS local_ts
         , start_on AT TIME ZONE time_zone AS ts_at_tz
         , current_date::timestamptz       AS lower_bound
         , (current_date + 1)::timestamptz AS upper_bound
    FROM   reservations
    WHERE  start_on >= current_date::timestamptz
    AND    start_on < (current_date + 1)::timestamptz;