【问题标题】:Get last week's data from a table with a creation date从具有创建日期的表中获取上周的数据
【发布时间】:2017-01-19 22:26:52
【问题描述】:

使用以下查询检索上周数据,但出现错误

Postgres 错误:“CAST”位置或附近的语法错误:127

我不知道错误在哪里:

SELECT count(*), extract(day from createdon) AS period
FROM orders
WHERE servicename =:serviceName AND  createdon BETWEEN 
    CAST(NOW() AS CAST(DATE-EXTRACT(DOW FROM NOW()) AS INTEGER-7)) AND
    CAST(NOW() AS CAST(DATE-EXTRACT(DOW from NOW()) AS INTEGER))
GROUP BY  extract(day from createdon)
ORDER BY extract(day from createdon);

【问题讨论】:

  • 你的 - 7 应该在括号之后,因为现在它正在寻找一个 INTEGER-7 数据类型。
  • 我试过了,但还是同样的错误....宝贝
  • Marc 我改变了......但它在位置 127 处显示与“\n”相同的错误
  • CAST(expr1 AS CAST(expr2 AS <type>)) 肯定是无效的。我什至无法猜测,你试图用它来实现什么。
  • 另外:您确定要使用 ANSI SQL 的 DOW 概念,而不是 ISODOW? (DOW 周从星期日开始,ISODOW 周从星期一开始:就像 date_trunc('week', ...)a_horse_with_no_name's anwser 中一样)

标签: sql postgresql


【解决方案1】:

你把事情复杂化了。要获取上周的数据,只需获取“本周开始”减去 7 天之后的所有数据:

可以使用date_trunc('week', current_date) 评估“本周开始”。

如果减去 7 天,您将得到前一周的开始date_trunc('week', current_date) - interval '7' day。如果减去 1 天,您将得到前一周的结束

date_trunc 总是使用星期一作为一周的开始,所以如果你的一周从星期日开始,只需再减去一个,例如date_trunc('week', current_date)::date - 8 将是前一周的星期日

把所有这些放在一起你会得到:

SELECT count(*), extract(day from createdon) AS period
FROM orders
WHERE servicename =:serviceName 
  AND createdon 
      between date_trunc('week', current_date)::date - 7 
          and date_trunc('week', current_date)::date - 1
GROUP BY extract(day from createdon)
ORDER BY extract(day from createdon);

如果您的列是时间戳列,您可以简单地将 createdon 转换为日期以摆脱时间部分:

  AND createdon::date 
      between date_trunc('week', current_date)::date - 7 
          and date_trunc('week', current_date)::date

请注意,createdon 上的常规索引不会用于该条件,如果您需要性能,则需要在 createdon::date 上创建索引。

如果您不能(或不想)创建这样的索引,则需要使用与between不同的东西

  AND createdon >= date_trunc('week', current_date)::date - 7 
  AND createdon < date_trunc('week', current_date)::date

(注意使用&lt; 而不是&lt;=,这是`between 使用的)

另一种选择是将日期信息转换为周和年的组合:

AND to_char(createdon, 'iyyy-iw') = to_char(date_trunc('week', current_date)::date - 7, 'iyyy-iw')

注意,我在上面使用了ISO week definition。如果您使用不同的周编号系统,则需要为to_char() 函数使用不同的format mask

【讨论】:

  • 不,date_trunc('week', current_date) - interval '1' day 是上周结束/本周开始前 1 天的午夜。
  • @pozs:我认为那些是DATE 列。如果这些是timestamp 你显然是对的
  • @a_horse_with_no_name 对,这可以工作,但扩大date 列进行比较感觉很奇怪; between date_trunc('week', current_date)::date - 7 and date_trunc('week', current_date)::date - 1 也可以使用索引(如果它是 date 列 OFC)。
  • @posz:你是对的。如果那是一个日期列,那就更好了
  • @pozs 如果我使用上面的时间戳查询会发生什么......它不会给我确切的结果......
【解决方案2】:

如果您使用北美周系统(其周从星期日开始),您的原始方法已经足够好,只需使用正确的语法 CAST(&lt;epr&gt; AS &lt;type&gt;)

SELECT   COUNT(*),
         EXTRACT(DAY FROM createdon) period
FROM     orders
WHERE    servicename = 'Cell Tower Monitoring'
AND      createdon BETWEEN CURRENT_DATE - CAST(EXTRACT(DOW FROM CURRENT_DATE) AS INTEGER) - 7 
                       AND CURRENT_DATE - CAST(EXTRACT(DOW FROM CURRENT_DATE) AS INTEGER) - 1
GROUP BY EXTRACT(DAY FROM createdon)
ORDER BY EXTRACT(DAY FROM createdon);

注意:这假定createdonDATE 列。如果是TIMESTAMP(或TIMESTAMP WITH TIME ZONE),则需要稍微不同的版本:

SELECT   COUNT(*),
         EXTRACT(DAY FROM createdon) period
FROM     orders
WHERE    servicename = 'Cell Tower Monitoring'
AND      createdon >= CURRENT_TIMESTAMP - INTERVAL '1 day' * (EXTRACT(DOW FROM CURRENT_TIMESTAMP) + 7)
AND      createdon <  CURRENT_TIMESTAMP - INTERVAL '1 day' *  EXTRACT(DOW FROM CURRENT_TIMESTAMP)
GROUP BY EXTRACT(DAY FROM createdon)
ORDER BY EXTRACT(DAY FROM createdon);

如果您想使用 ISO 周系统(其周从星期一开始),则只需使用 ISODOW 而不是 DOW。或者,您可以使用date_trunc('week', ...) 函数,例如@a_horse_with_no_name's answer

如果您想使用另一周系统(例如从星期六开始),您需要在 CASE 表达式中添加一些额外的逻辑,因为从 DOW 中减去 1 不会得到预期的结果在那种星期的开始(例如星期六,它会在两周前的那一周)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-11
    • 2011-01-20
    相关资源
    最近更新 更多