You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在Ruby中结合Arel.sql与Arel::Nodes::Case实现动态子查询

如何基于PostgreSQL表中时区列动态查询对应时区的时间戳?

需求说明:需要从locations表中查询每行记录对应本地时区的时间戳,时区信息存储在tz_name列中;部分记录的tz_name可能为null,此时需要使用默认时区America/New_York兜底。

现有可正常运行的查询代码

以下是当前能正常执行的查询,但仅固定使用America/New_York时区:

locations = Location.select(
  Location.arel_table[:id],
  Arel::Nodes::Case.new
    .when(Location.arel_table[:tz_name].eq(nil))
    .then('America/New_York')
    .else(Location.arel_table[:tz_name]).as("zone"),
  Arel::Nodes::NamedFunction.new(
    'date_part',
    [Arel::Nodes.build_quoted('hour'),
     Arel.sql(
       "(select CURRENT_TIMESTAMP AT TIME ZONE
                'America/New_York')"
     )
    ]
  ).as("now_hour")
).where(
  Arel::Nodes::NamedFunction.new(
    'date_part',
    [Arel::Nodes.build_quoted('hour'),
     Arel.sql(
       "(select CURRENT_TIMESTAMP AT TIME ZONE
                'America/New_York')"
     )
    ]
  ).eq(9)
)

尝试过但失败的写法

写法1:在Arel.sql中拼接Case语句

直接拼接Arel的Case节点到SQL字符串中会报错:

Arel.sql(
  "(select CURRENT_TIMESTAMP AT TIME ZONE " +
    Arel::Nodes::Case.new
      .when(Location.arel_table[:tz_name].eq(nil))
      .then('America/New_York')
      .else(Location.arel_table[:tz_name])
    + ")"
) # 执行失败

写法2:将Arel.sql放入Case语句的分支中

then分支可正常运行,但else分支引用表列时失败:

Arel::Nodes::Case.new           
  .when(Location.arel_table[:tz_name].eq(nil))                                  
  .then(Arel.sql("(select CURRENT_TIMESTAMP AT TIME ZONE 'America/New_York')"))     # 正常运行
  .else(Arel.sql("(select CURRENT_TIMESTAMP AT TIME ZONE '#{Location.arel_table[:tz_name]}')"))  # 执行失败

解决方案

利用PostgreSQL原生的TIMEZONE函数,结合COALESCE函数处理tz_name为null的情况,同时用Arel构建查询:

Arel::Nodes::NamedFunction.new(
  'date_part',
  [Arel::Nodes.build_quoted('hour'),
   Arel.sql("(TIMEZONE(COALESCE(locations.tz_name, 'America/New_York'), CURRENT_TIMESTAMP))")
  ]
).as("now_hour")

说明:

  • COALESCE(locations.tz_name, 'America/New_York')会优先使用tz_name字段的值,若该值为null则返回默认时区America/New_York
  • PostgreSQL的TIMEZONE(timezone, timestamp)函数可直接将时间戳转换为指定时区的时间,相比CURRENT_TIMESTAMP AT TIME ZONE的写法,更适配动态传入表列值的场景

内容的提问来源于stack exchange,提问作者dreadstar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 15:32:06