【问题标题】:How To Select Records in a Status Between Timestamps? T-SQL如何在时间戳之间选择状态中的记录? T-SQL
【发布时间】:2017-07-14 00:03:18
【问题描述】:

我有一个 T-SQL 报价表,需要能够计算过去几个月有多少报价处于打开状态。

我必须使用的日期是“Add_Date”时间戳和“Update_Date”时间戳。一旦将报价放入“1”的“Closed_Status”,就无法再更新。因此,“Update_Date”实际上变成了Closed_Status 时间戳。

我被困住了,因为我不知道如何选择在特定月份打开的所有打开的报价。

以下是一些示例记录:

Quote_No   Add_Date   Update_Date  Open_Status  Closed_Status
001        01-01-2016  NULL         1            0
002        01-01-2016  3-1-2016     0            1
003        01-01-2016  4-1-2016     0            1

期望的结果是:

Year  Month  Open_Quote_Count
2016  01     3
2016  02     3
2016  03     2
2016  04     1

我在这个问题上碰到了一堵心理墙,我试图做一些case when 过滤,但我似乎无法弄清楚这个谜题。理想情况下,我不会对日期进行硬编码,因为它跨越数年,而且我不想在编写后维护它。

提前感谢您的帮助。

【问题讨论】:

  • 即使计数器为 0,您是否要显示月份?
  • 请注意,您可能指的是DateTimeDateTime2Date,但不是Timestamp,因为TimeStamp 实际上是一个自动递增的二进制值,没有任何内容与实际日期/时间有关。

标签: sql sql-server tsql datetime


【解决方案1】:

您按月执行此操作。因此,我想到了三个选项:

  • 使用left join 的所有月份列表。
  • 递归 CTE。
  • 一个数字表。

让我展示最后一个:

with n as (
      select row_number() over (order by (select null)) - 1 as n
      from master..spt_values
     )
select format(dateadd(month, n.n, q.add_date), 'yyyy-MM') as yyyymm,
       count(*) as Open_Quote_Count
from quotes q join
     n
     on (closed_status = 1 and dateadd(month, n.n, q.add_date) <= q.update_date) or
        (closed_status = 0 and dateadd(month, n.n, q.add_date) <= getdate()) 
group by format(dateadd(month, n.n, q.add_date), 'yyyy-MM')
order by yyyymm;

这确实假设每个月至少有一个打开的记录。为此目的,这似乎是合理的。

【讨论】:

  • @Gordon_Linoff,它有效并解决了我的问题。我很感激,但确实想了解 row_number() 按部分排序的方式/原因。你有什么推荐的参考吗?我对你的技能印象深刻。
  • @SDS 。 . . master..spt_values 是一个大表(数千行),通常用于在 SQL 中生成数字。事实上,其中一列是顺序的,但我不记得是哪一列,所以我只使用row_number() 来生成顺序值。如果你只运行 CTE,你可以看到它只是产生一堆数字,从 0 开始。
  • 我已经用了几个月了,结果发现有问题。基本上这是返回一个累积数字,所以一月很好,但是二月实际上是一月+二月,三月是一月+二月+三月,等等。现在我们在七月,我们已经发现了这一点。有关如何纠正此问题的任何建议?谢谢!
  • @SDS 。 . .您能否就样本数据和期望的结果提出另一个问题?
  • 我在这里发布了一个包含完整数据集和所需结果的新问题:stackoverflow.com/questions/45439853/… 感谢您的帮助!
【解决方案2】:

您可以使用datepart 提取日期的部分内容,例如:

select datepart(year, add_date) as 'year',
       datepart(month, date_date) as 'month',
       count(1)
from theTable
where open_status = 1
group by datepart(year, add_date), datepart(month, date_date)

注意:这会计算开始月份,主要是为了显示datepart 的使用情况。

【讨论】:

  • 正如 OP 提到的select all open quotes that *were* open,这不会解决问题。
【解决方案3】:

更新为误解了初始请求。

考虑以下测试数据:

DECLARE @test TABLE 
(
    Quote_No VARCHAR(3),
    Add_Date DATE,
    Update_Date DATE,
    Open_Status INT,
    Closed_Status INT
)

INSERT INTO @test (Quote_No, Add_Date, Update_Date, Open_Status, Closed_Status)
VALUES ('001', '20160101', NULL, 1, 0)
     , ('002', '20160101', '20160301', 0, 1)
     , ('003', '20160101', '20160401', 0, 1)

这是一个递归解决方案,它不依赖于系统表,但性能也较差。当我们谈论月份和年份的组合时,递归的数量不会过多。

;WITH YearMonths AS
(
    SELECT YEAR(MIN(Add_Date)) AS [Year]
        , MONTH(MIN(Add_Date)) AS [Month]
        , MIN(Add_Date) AS YMDate
    FROM @test
    UNION ALL
    SELECT YEAR(DATEADD(MONTH,1,YMDate))
        , MONTH(DATEADD(MONTH,1,YMDate))
        , DATEADD(MONTH,1,YMDate)
    FROM YearMonths
    WHERE YMDate <= SYSDATETIME()
)
SELECT [Year]
    , [Month]
    , COUNT(*) AS Open_Quote_Count
FROM YearMonths ym
INNER JOIN @test t
ON (
    [Year] * 100 + [Month] <= CAST(FORMAT(t.Update_Date, 'yyyyMM') AS INT)
    AND t.Closed_Status = 1
    )
OR (
    [Year] * 100 + [Month] <= CAST(FORMAT(SYSDATETIME(), 'yyyyMM') AS INT)
    AND t.Closed_Status = 0
    )
GROUP BY [Year], [Month]
ORDER BY [Year], [Month]

声明更长,也更易读,并列出了迄今为止的所有年/月组合。

查看Date and Time Data Types and Functions 的 SQL-Server 2008+

Recursive Queries Using Common Table Expressions

【讨论】:

  • 正如 OP 提到的select all open quotes that *were* open,这不会解决问题。
  • 一开始没有明确说明。在阅读了戈登的回答后,我才意识到这是什么意思。更新了我的脚本。
猜你喜欢
  • 2012-02-02
  • 2015-01-15
  • 2020-12-19
  • 2013-10-18
  • 1970-01-01
  • 2015-07-19
  • 1970-01-01
  • 2017-07-10
  • 2012-06-03
相关资源
最近更新 更多