【问题标题】:SQL to calculate difference from 1st day of Month to Date specifiedSQL 计算从当月的第一天到指定日期的差异
【发布时间】:2019-04-23 07:51:06
【问题描述】:

我正在处理一个 sql 查询以获取差异。我有一个包含读数的表格,读数的时间戳,id。我的最终目标是获得三个差异。 1. 次日前一天读数之间的差异, 2. 时间戳值前 7 天读数之间的差异, 3. 从每月 1 日开始到每个指定日期的读数之间的差异。

我破解了第一个两项。现在我正在尝试破解第三个。我知道它很容易使用功能,任何人都可以帮助我解决第三个请求。

预期结果:11 月 1 日读数为 1000,11 月 2 日读数为 1020,11 月 3 日读数为 1050,11 月 2 日应为 20,11 月 3 日应为 50。

如果一个月的第一天没有数据,则取可用日期的最少数据。例如,september 只有从 24 开始,所以从 sep 24 开始读取。

下面是示例表。

+----+-----------+---------+----------------+----------------+-----------------+
| id | timestamp | Reading | 1DayDifference | 7DayDifference | monthDifference |
+----+-----------+---------+----------------+----------------+-----------------+
| A1 | 11/20/18  |   44182 |              0 |            300 |             541 |
| A1 | 11/19/18  |   44182 |              0 |            338 |             541 |
| A1 | 11/18/18  |   44182 |              0 |            338 |             541 |
| A1 | 11/17/18  |   44182 |             38 |            338 |             541 |
| A1 | 11/16/18  |   44144 |            197 |            300 |             503 |
| A1 | 11/15/18  |   43947 |             26 |            103 |                 |
| A1 | 11/14/18  |   43921 |             39 |            158 |                 |
| A1 | 11/13/18  |   43882 |             38 |            158 |                 |
| A1 | 11/12/18  |   43844 |              0 |            120 |                 |
| A1 | 11/11/18  |   43844 |              0 |            120 |                 |
| A1 | 11/10/18  |   43844 |              0 |            160 |                 |
| A1 | 11/09/18  |   43844 |              0 |            203 |                 |
| A1 | 11/08/18  |   43844 |             81 |            241 |                 |
| A1 | 11/06/18  |   43763 |             39 |            198 |                 |
| A1 | 11/05/18  |   43724 |              0 |            198 |                 |
| A1 | 11/04/18  |   43724 |              0 |            198 |                 |
| A1 | 11/03/18  |   43724 |             40 |            198 |                 |
| A1 | 11/02/18  |   43684 |             43 |            199 |                 |
| A1 | 11/01/18  |   43641 |             38 |            194 |                 |
| A1 | 10/31/18  |   43603 |             38 |            275 |             237 |
| A1 | 10/30/18  |   43565 |             39 |            317 |                 |
| A1 | 10/29/18  |   43526 |              0 |            317 |                 |
| A1 | 10/28/18  |   43526 |              0 |            317 |                 |
| A1 | 10/27/18  |   43526 |             41 |            317 |                 |
| A1 | 10/26/18  |   43485 |             38 |            276 |                 |
| A1 | 10/25/18  |   43447 |            119 |            238 |                 |
| A1 | 10/24/18  |   43328 |             80 |            119 |                 |
+----+-----------+---------+----------------+----------------+-----------------+

我使用的第一种 SQL 类型。

SELECT  id,
        timestamp,
        Reading,
        Reading - lead(Reading,1,0) OVER( partition BY [id] ORDER BY timestamp desc) [OneDayDifference],
        Reading - lead(Reading,7,0) OVER( partition BY [id] ORDER BY timestamp desc) [SevDayDifference]
FROM    [dbo].[test_example]     s
ORDER BY id, timestamp desc

下面是生成上述数据的脚本。

CREATE TABLE [dbo].[test_Example](
    [id] [nvarchar](50) NOT NULL,
    [timestamp] [datetime2](7) NOT NULL,
    [reading] [int] NOT NULL,
    [OneDayDifference] [int] NOT NULL,
    [SevDayDifference] [int] NOT NULL
) ON [PRIMARY]
GO
INSERT [dbo].[test_Example] ([id], [timestamp], [reading], [OneDayDifference], [SevDayDifference]) VALUES (N'A1', CAST(N'2018-11-19T00:01:38.0000000' AS DateTime2), 44182, 0, 338)
GO
INSERT [dbo].[test_Example] ([id], [timestamp], [reading], [OneDayDifference], [SevDayDifference]) VALUES (N'A1', CAST(N'2018-11-18T00:01:44.0000000' AS DateTime2), 44182, 0, 338)
GO
INSERT [dbo].[test_Example] ([id], [timestamp], [reading], [OneDayDifference], [SevDayDifference]) VALUES (N'A1', CAST(N'2018-11-17T00:01:35.0000000' AS DateTime2), 44182, 38, 338)
GO
INSERT [dbo].[test_Example] ([id], [timestamp], [reading], [OneDayDifference], [SevDayDifference]) VALUES (N'A1', CAST(N'2018-11-16T00:01:39.0000000' AS DateTime2), 44144, 197, 300)
GO
INSERT [dbo].[test_Example] ([id], [timestamp], [reading], [OneDayDifference], [SevDayDifference]) VALUES (N'A1', CAST(N'2018-11-15T00:01:47.0000000' AS DateTime2), 43947, 26, 103)
GO
INSERT [dbo].[test_Example] ([id], [timestamp], [reading], [OneDayDifference], [SevDayDifference]) VALUES (N'A1', CAST(N'2018-11-14T00:01:40.0000000' AS DateTime2), 43921, 39, 158)
GO
INSERT [dbo].[test_Example] ([id], [timestamp], [reading], [OneDayDifference], [SevDayDifference]) VALUES (N'A1', CAST(N'2018-11-13T00:01:38.0000000' AS DateTime2), 43882, 38, 158)
GO
INSERT [dbo].[test_Example] ([id], [timestamp], [reading], [OneDayDifference], [SevDayDifference]) VALUES (N'A1', CAST(N'2018-11-12T00:02:39.0000000' AS DateTime2), 43844, 0, 120)
GO
INSERT [dbo].[test_Example] ([id], [timestamp], [reading], [OneDayDifference], [SevDayDifference]) VALUES (N'A1', CAST(N'2018-11-11T00:01:37.0000000' AS DateTime2), 43844, 0, 120)
GO
INSERT [dbo].[test_Example] ([id], [timestamp], [reading], [OneDayDifference], [SevDayDifference]) VALUES (N'A1', CAST(N'2018-11-10T00:01:37.0000000' AS DateTime2), 43844, 0, 160)
GO
INSERT [dbo].[test_Example] ([id], [timestamp], [reading], [OneDayDifference], [SevDayDifference]) VALUES (N'A1', CAST(N'2018-11-09T00:01:37.0000000' AS DateTime2), 43844, 0, 203)
GO
INSERT [dbo].[test_Example] ([id], [timestamp], [reading], [OneDayDifference], [SevDayDifference]) VALUES (N'A1', CAST(N'2018-11-08T00:01:46.0000000' AS DateTime2), 43844, 81, 241)
GO
INSERT [dbo].[test_Example] ([id], [timestamp], [reading], [OneDayDifference], [SevDayDifference]) VALUES (N'A1', CAST(N'2018-11-06T00:01:36.0000000' AS DateTime2), 43763, 39, 198)
GO
INSERT [dbo].[test_Example] ([id], [timestamp], [reading], [OneDayDifference], [SevDayDifference]) VALUES (N'A1', CAST(N'2018-11-05T00:02:27.0000000' AS DateTime2), 43724, 0, 198)
GO
INSERT [dbo].[test_Example] ([id], [timestamp], [reading], [OneDayDifference], [SevDayDifference]) VALUES (N'A1', CAST(N'2018-11-04T00:01:37.0000000' AS DateTime2), 43724, 0, 198)
GO
INSERT [dbo].[test_Example] ([id], [timestamp], [reading], [OneDayDifference], [SevDayDifference]) VALUES (N'A1', CAST(N'2018-11-03T00:01:48.0000000' AS DateTime2), 43724, 40, 198)
GO
INSERT [dbo].[test_Example] ([id], [timestamp], [reading], [OneDayDifference], [SevDayDifference]) VALUES (N'A1', CAST(N'2018-11-02T00:01:33.0000000' AS DateTime2), 43684, 43, 199)
GO
INSERT [dbo].[test_Example] ([id], [timestamp], [reading], [OneDayDifference], [SevDayDifference]) VALUES (N'A1', CAST(N'2018-11-01T00:01:41.0000000' AS DateTime2), 43641, 38, 194)
GO
INSERT [dbo].[test_Example] ([id], [timestamp], [reading], [OneDayDifference], [SevDayDifference]) VALUES (N'A1', CAST(N'2018-10-31T00:01:32.0000000' AS DateTime2), 43603, 38, 275)
GO
INSERT [dbo].[test_Example] ([id], [timestamp], [reading], [OneDayDifference], [SevDayDifference]) VALUES (N'A1', CAST(N'2018-10-30T00:01:34.0000000' AS DateTime2), 43565, 39, 43565)
GO
INSERT [dbo].[test_Example] ([id], [timestamp], [reading], [OneDayDifference], [SevDayDifference]) VALUES (N'A1', CAST(N'2018-10-29T00:02:45.0000000' AS DateTime2), 43526, 0, 43526)
GO
INSERT [dbo].[test_Example] ([id], [timestamp], [reading], [OneDayDifference], [SevDayDifference]) VALUES (N'A1', CAST(N'2018-10-28T00:01:43.0000000' AS DateTime2), 43526, 0, 43526)
GO
INSERT [dbo].[test_Example] ([id], [timestamp], [reading], [OneDayDifference], [SevDayDifference]) VALUES (N'A1', CAST(N'2018-10-27T00:01:31.0000000' AS DateTime2), 43526, 41, 43526)
GO
INSERT [dbo].[test_Example] ([id], [timestamp], [reading], [OneDayDifference], [SevDayDifference]) VALUES (N'A1', CAST(N'2018-10-26T00:01:30.0000000' AS DateTime2), 43485, 38, 43485)
GO
INSERT [dbo].[test_Example] ([id], [timestamp], [reading], [OneDayDifference], [SevDayDifference]) VALUES (N'A1', CAST(N'2018-10-25T00:01:35.0000000' AS DateTime2), 43447, 119, 43447)
GO
INSERT [dbo].[test_Example] ([id], [timestamp], [reading], [OneDayDifference], [SevDayDifference]) VALUES (N'A1', CAST(N'2018-10-24T00:01:43.0000000' AS DateTime2), 43328, 43328, 43328)
GO

【问题讨论】:

    标签: sql sql-server tsql azure-sqldw


    【解决方案1】:

    查找月份的第一天需要向后查看可变数量的行,因此您可以在 apply 中使用相关子查询,而不是 LEAD()LAG()。请注意,因为您“向后看”,所以我更喜欢使用 LAG() 而不是颠倒时间戳和 LEAD() 的顺序,但是两者都会产生相同的结果。

    nb:此子查询将查找任何月份中最早的时间戳,如果不需要,则将 and t.timestamp < dateadd(dd,1,dateadd(mm,datediff(mm,0,s.timestamp),0)) 添加到 where 子句中

    SELECT
        id
      , timestamp
      , Reading
      , Reading - LAG( Reading, 1, 0 ) OVER (PARTITION BY [id] ORDER BY timestamp) [OneDayDifference]
      , Reading - LAG( Reading, 7, 0 ) OVER (PARTITION BY [id] ORDER BY timestamp) [SevDayDifference]
      , reading - oa.prev_reading [ThisMonthDiff]
    FROM [dbo].[test_example] s
    outer apply (
        select top(1) t.reading prev_reading
        from [dbo].[test_example] t
        where s.id = t.id
        and t.timestamp >= dateadd(mm,datediff(mm,0,s.timestamp),0)
           -- and t.timestamp < dateadd(dd,1,dateadd(mm,datediff(mm,0,s.timestamp),0))
        order by t.timestamp
        ) oa
    ORDER BY
        id
      , timestamp DESC
    ;
    

    结果:

    +----+----+------------+---------+------------------+------------------+---------------+
    |    | id | timestamp  | Reading | OneDayDifference | SevDayDifference | ThisMonthDiff |
    +----+----+------------+---------+------------------+------------------+---------------+
    |  1 | A1 | 2018-11-19 |   44182 |                0 |              338 |           541 |
    |  2 | A1 | 2018-11-18 |   44182 |                0 |              338 |           541 |
    |  3 | A1 | 2018-11-17 |   44182 |               38 |              338 |           541 |
    |  4 | A1 | 2018-11-16 |   44144 |              197 |              300 |           503 |
    |  5 | A1 | 2018-11-15 |   43947 |               26 |              103 |           306 |
    |  6 | A1 | 2018-11-14 |   43921 |               39 |              158 |           280 |
    |  7 | A1 | 2018-11-13 |   43882 |               38 |              158 |           241 |
    |  8 | A1 | 2018-11-12 |   43844 |                0 |              120 |           203 |
    |  9 | A1 | 2018-11-11 |   43844 |                0 |              120 |           203 |
    | 10 | A1 | 2018-11-10 |   43844 |                0 |              160 |           203 |
    | 11 | A1 | 2018-11-09 |   43844 |                0 |              203 |           203 |
    | 12 | A1 | 2018-11-08 |   43844 |               81 |              241 |           203 |
    | 13 | A1 | 2018-11-06 |   43763 |               39 |              198 |           122 |
    | 14 | A1 | 2018-11-05 |   43724 |                0 |              198 |            83 |
    | 15 | A1 | 2018-11-04 |   43724 |                0 |              198 |            83 |
    | 16 | A1 | 2018-11-03 |   43724 |               40 |              198 |            83 |
    | 17 | A1 | 2018-11-02 |   43684 |               43 |              199 |            43 |
    | 18 | A1 | 2018-11-01 |   43641 |               38 |              194 |             0 |
    | 19 | A1 | 2018-10-31 |   43603 |               38 |              275 |           275 |
    | 20 | A1 | 2018-10-30 |   43565 |               39 |            43565 |           237 |
    | 21 | A1 | 2018-10-29 |   43526 |                0 |            43526 |           198 |
    | 22 | A1 | 2018-10-28 |   43526 |                0 |            43526 |           198 |
    | 23 | A1 | 2018-10-27 |   43526 |               41 |            43526 |           198 |
    | 24 | A1 | 2018-10-26 |   43485 |               38 |            43485 |           157 |
    | 25 | A1 | 2018-10-25 |   43447 |              119 |            43447 |           119 |
    | 26 | A1 | 2018-10-24 |   43328 |            43328 |            43328 |             0 |
    +----+----+------------+---------+------------------+------------------+---------------+
    

    上面我使用了outer apply,它的作用就像一个外连接(如果没有找到匹配的结果,仍然返回源行)。如果没有必要,请改用cross apply


    编辑

    SELECT
        id
      , format(timestamp, 'yyyy-MM-dd') [timestamp]
      , Reading
      , COALESCE(Reading - LAG( Reading, 1) OVER (PARTITION BY [id] ORDER BY timestamp),0) [OneDayDifference]
      , COALESCE(Reading - LAG( Reading, 7) OVER (PARTITION BY [id] ORDER BY timestamp),0) [SevDayDifference]
      , reading - ca.tr [ThisMonthDiff]
    FROM [dbo].[test_example] s
    cross apply (
        select top(1) t.reading tr
        from [dbo].[test_example] t
        where s.id = t.id
        and t.timestamp >= dateadd(mm,datediff(mm,0,s.timestamp),0)
        order by t.timestamp
        ) ca
    ORDER BY
        id
      , timestamp DESC
    ;
    
    +----+----+------------+---------+------------------+------------------+---------------+
    |    | id | timestamp  | Reading | OneDayDifference | SevDayDifference | ThisMonthDiff |
    +----+----+------------+---------+------------------+------------------+---------------+
    |  1 | A1 | 2018-11-19 |   44182 |                0 |              338 |           541 |
    |  2 | A1 | 2018-11-18 |   44182 |                0 |              338 |           541 |
    |  3 | A1 | 2018-11-17 |   44182 |               38 |              338 |           541 |
    
    | 18 | A1 | 2018-11-01 |   43641 |               38 |              194 |             0 |
    | 19 | A1 | 2018-10-31 |   43603 |               38 |              275 |           275 |
    | 20 | A1 | 2018-10-30 |   43565 |               39 |                0 |           237 |
    | 21 | A1 | 2018-10-29 |   43526 |                0 |                0 |           198 |
    | 22 | A1 | 2018-10-28 |   43526 |                0 |                0 |           198 |
    | 23 | A1 | 2018-10-27 |   43526 |               41 |                0 |           198 |
    | 24 | A1 | 2018-10-26 |   43485 |               38 |                0 |           157 |
    | 25 | A1 | 2018-10-25 |   43447 |              119 |                0 |           119 |
    | 26 | A1 | 2018-10-24 |   43328 |                0 |                0 |             0 |
    +----+----+------------+---------+------------------+------------------+---------------+
    

    【讨论】:

    • 甜,这很好用。我看到我们用于 SevenDayDifference 的 Lead() 或 Lag() 函数的行为有点奇怪。最近 7 天的读数显示相同的读数,因为它没有可比较的值。可以做任何替代吗?
    • 删除滞后函数中的默认值零(无第三个参数),如果删除,您将获得空值
    • 添加到答案的查询变体;删除 LAG 默认为零
    【解决方案2】:

    不要使用Lead(),而是使用子查询获取相同Idyearmonth的前1行,按timestamp ASC排序,并计算与reading的差异子查询返回的行。

    【讨论】:

    • 有成千上万的 ID,过去一年中每个 ID 的每一天都有条目。我不确定这种方法如何适用于其他 ID 和那么多天的数据
    • 它应该可以正常工作。您可能可以使用 CROSS APPLY 执行类似的操作,但性能应该大致相同。
    猜你喜欢
    • 2018-04-24
    • 1970-01-01
    • 2017-10-27
    • 2020-05-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-22
    • 1970-01-01
    相关资源
    最近更新 更多