相同数据集的日期范围

Posted

技术标签:

【中文标题】相同数据集的日期范围【英文标题】:Date Range for set of same data 【发布时间】:2015-05-26 09:55:12 【问题描述】:

我正在尝试构建一个 SQL 查询,该查询将为我提供价格相同的日期的日期范围。如果价格出现突破,我希望看到它出现在新的行列。即使在本月的某个时候价格相同,如果两者之间的某个时间价格发生变化,我希望将其视为具有特定日期范围的两个单独的行。

样本数据:

Date    Price
1-Jan   3.2
2-Jan   3.2
3-Jan   3.2
4-Jan   3.2
5-Jan   3.2
6-Jan   3.2
7-Jan   3.2
8-Jan   3.2
9-Jan   3.5
10-Jan  3.5
11-Jan  3.5
12-Jan  3.5
13-Jan  3.5
14-Jan  4.2
15-Jan  4.2
16-Jan  4.2
17-Jan  3.2
18-Jan  3.2
19-Jan  3.2
20-Jan  3.2
21-Jan  3.2
22-Jan  3
23-Jan  3
24-Jan  3
25-Jan  3
26-Jan  3
27-Jan  3
28-Jan  3
29-Jan  3.5
30-Jan  3.5
31-Jan  3.5

期望的结果:

Price   Date Range
3.2     1-8
3.5     9-13
4.2     14-16
3.2     17-22
3       22-28
3.5     29-31

【问题讨论】:

使用光标,检测变化。 到目前为止您尝试过什么?您的日期是否总是连续的,或者您的表格中是否缺少日期? 日期列&价格数据类型? 我需要视图中的结果,它将作为报告的数据集。这是我在构建时遇到问题的代码的一部分,我正计划有一个函数,它将价格与先前数据记录的价格进行比较。对于所有相同的数据集,我将让它返回一个整数值.即具有相同定价的所有连续日期将具有函数返回 1 然后 2 等等。我打算在视图的 select 语句中调用此函数,但由于 SQL 不支持用户定义的全局变量,我无法引用维护的整数值 哪个版本的sqlserver 【参考方案1】:

非关系解决方案

我认为其他任何答案都不正确。

GROUP BY 不起作用

使用ROW_NUMBER() 强制数据进入物理记录归档系统结构,然后将其作为物理记录处理。以巨大的性能成本为代价。当然,为了编写这样的代码,它迫使您考虑 RFS,而不是考虑关系。

使用 CTE 也是如此。遍历数据,尤其是不变的数据。巨额成本略有不同。

由于一系列不同的原因,游标绝对是错误的。 (a) 游标需要代码,并且您已请求视图 (b) 游标放弃集合处理引擎,并恢复为逐行处理。同样,不是必需的。如果我的任何团队中的开发人员在关系数据库(即,不是记录归档系统)上使用游标或临时表,我会向他们开枪。

关系解决方案

    您的 data 是关系型的、逻辑的,两个给定的 data 列就足够了。

    当然,我们必须形成一个视图(派生关系)才能获得所需的报告,但它由纯 SELECT 组成,这与处理完全不同(将其转换为 文件,这是物理的,然后处理 文件;或临时表;或工作表;或 CTE;或 ROW_Number();等)。

    与有议程的“理论家”的哀叹相反,SQL 可以完美地处理关系数据。而且你的数据是关系型的。

因此,请保持关系型思维模式、数据的关系型视图和集合处理思维模式。可以使用单个 SELECT 来满足关系数据库上的每个报告要求。无需退回到 1970 年以前的 ISAM 文件处理方法。

我将假设主键(提供关系行唯一性的列集)为Date,,根据给出的示例数据,数据类型为DATE.

试试这个:

    CREATE VIEW MyTable_Base_V          -- Foundation View
    AS
        SELECT  Date,
                Date_Next,
                Price
            FROM (
            -- Derived Table: project rows with what we need
            SELECT  Date,
                    [Date_Next] = DATEADD( DD, 1, O.Date ),
                    Price,
                    [Price_Next] = (

                SELECT Price            -- NULL if not exists
                    FROM MyTable
                    WHERE Date = DATEADD( DD, 1, O.Date )
                    )

                FROM MyTable MT

                ) AS X
            WHERE Price != Price_Next   -- exclude unchanging rows
    GO

    CREATE VIEW MyTable_V               -- Requested View
    AS
        SELECT  [Date_From] = (
            --  Date of the previous row
            SELECT MAX( Date_Next )     -- previous row
                FROM MyTable_V
                WHERE Date_Next < MT.Date
                ),

                [Date_To] = Date,       -- this row
                Price
            FROM MyTable_Base_V MT
    GO

    SELECT  *
        FROM MyTable_V
    GO

方法,通用

当然这是一个方法,所以它是通用的,它可以用来根据任何数据变化(这里,Price 的更改)。

在这里,您的Dates 是连续的,因此Date_Next 的确定很简单:将Date 增加1 天。如果 PK 增加但连续(例如DateTimeTimeStamp 或其他密钥),请将派生表X 更改为:

    -- Derived Table: project rows with what we need
    SELECT  DateTime,
            [DateTime_Next] = (
            -- first row > this row
        SELECT  TOP 1
                DateTime                -- NULL if not exists
            FROM MyTable
            WHERE DateTime > MT.DateTime
            ),

            Price,
            [Price_Next] = (
            -- first row > this row
        SELECT  TOP 1
                Price                   -- NULL if not exists
            FROM MyTable
            WHERE DateTime > MT.DateTime
            )

        FROM MyTable MT

享受吧。

请随时发表评论、提出问题等。

【讨论】:

这似乎是 SQL Server 的最佳方法。我已经尝试了您的两种视图解决方案和基于 CTE 的我,如下所示。您的两个视图解决方案直到 4 分钟才返回结果,然后我取消了查询。但是 CTE 在 18 秒内返回了 2989355 行中的 972762 行。 我比较了这两个计划,在基于视图的解决方案中,hash mach(内部连接)的成本为 79%,两次聚集索引扫描的成本为 18%。在 CTE 哈希匹配中只有 9% 和排序 24% 。请提出我在建议针对此问题的基于 CTE 的解决方案方面缺乏的地方。据我所知,当有非常非常大的数据时,CTE 可能会变慢,并且由于内存不足,它将被写入文件。但是这里一天一排似乎没有这么大的数据。 我注意到两个视图解决方案的另一个问题是 first from date 为空! @AnilKumar。 (a) 如果第一个 FromDate 为空,您显然没有使用我给出的代码。 (b) 日期在这里是唯一的。 (c) 这对 Dfangs 来说“完美运行”,我在生产中拥有 120 多个这样的代码段,每天可以完美运行数百万次。 (d) 众所周知,这种方法比 CTE 快得多,因为 CTE 具有巨大的资源成本,您似乎同意这一点。 (e) 如果您有想要检查的代码,我建议您打开一个新问题并发布您的所有代码(DDL 和 DML)。【参考方案2】:

您可以通过添加分组列来做到这一点。一个巧妙的技巧是两个数字序列的差异 - 当差异不变时,价格相同。

select price, min(date), max(date)
from (select s.*,
             (row_number() over (order by date) - 
              row_number() over (partition by price order by date)
             ) as grp
      from sample s
     ) grp
group by grp, price;

注意:请注意价格存储为固定小数而不是浮动小数。否则,看起来相同的值可能实际上并不相同。

【讨论】:

您能否确认一下,这是否处理了价格可能出现在多个组中的事实,例如 3.5 ?我无法尝试,因为我的平台上没有ROW_NUMBER()。此外,无论如何,我现在无法访问我的平台。 @PerformanceDBA 。 . .尝试运行它。它绝对适用于出现在多个组中的相同价格。您是否在未测试的情况下对答案投了反对票? 测试 我只是解释了我无法测试,以及原因。这就是我问这个问题的原因。了解您的代码/平台,它具有记录归档系统的功能。特别是“巧妙的把戏”。鉴于您的其他答案,我很确定它有效。 投反对票不!【参考方案3】:

这就是你要找的东西

declare @temptbl table (price decimal(18,2), mindate date, maxdate date)

declare @price as decimal(18,2), @date as date

declare tempcur cursor for 
select price, date
from YourTable

open tempcur

fetch next from tempcur
into @price, @date

while (@@fetch_status = 0)
begin
    if (isnull((select price from @temptbl where maxdate = (select max(maxdate)from @temptbl)),0) <> @price)
        insert into @temptbl (price,mindate,maxdate) values (@price,@date,@date)
    else
        update @temptbl
        set maxdate = @date
        where maxdate = (select max(maxdate)from @temptbl)


    fetch next from tempcur
    into @price, @date
end

deallocate tempcur

select price, convert(nvarchar(50), mindate) + ' to ' + convert(nvarchar(50), maxdate) as [date range] from @temptbl

【讨论】:

他需要一个视图,我需要一个视图中的结果! 据我所知,如果不使用临时表或 CTE 就无法做到这一点,因此需要在过程中执行此操作 是的,CTE 是解决方案,分区视图,我在下面添加了我的答案。【参考方案4】:

使用 CTE,下面是工作代码。

WITH grouped AS (
SELECT 
Pricedate, price,
grp1= ROW_NUMBER() OVER (ORDER BY Pricedate) -
ROW_NUMBER() OVER (Partition by price ORDER BY Pricedate)
FROM yourTablewithDateAndPrice 
)
SELECT
  DtFrom = MIN(Pricedate),
  DtTo = MAX(Pricedate),
  Price = price

FROM grouped
GROUP BY Price,grp1 
order by DtFrom;

内部查询将创建相同的组,直到找到相同的价格,否则组将加一。 在最终组中,您将获得所需的结果。

【讨论】:

(a)之前我没有投票给你它。 Comments Help。很少有人这样做。这是一个示例 (b) 我正在 现在 投票给您,因为虽然您的代码产生了所需的结果,但 [阅读我的答案了解详细信息] 它 (i) 无法理解数据。 (ii) 将关系数据简化为记录归档系统,然后 (iii) 使用 (iv) 效率极低 (v) 的 RFS 方法,而不是按原样使用关系数据。 这种关系数据解决方案似乎不是 SQL Server 的最佳方法。我已经尝试了您的两种视图解决方案,并基于上面给出的 CTE。您的两个视图解决方案直到 4 分钟才返回结果,然后我取消了查询。但是 CTE 在 18 秒内返回了 2989355 行中的 972762 行 我比较了这两个计划,在基于视图的解决方案中,hash mach(内部连接)的成本为 79%,两次聚集索引扫描的成本为 18%。在 CTE 哈希匹配中只有 9% 和排序 24% 。请提出我在建议针对此问题的基于 CTE 的解决方案方面缺乏的地方。据我所知,当有非常非常大的数据时,CTE 可能会变慢,并且由于内存不足,它将被写入文件。但是这里一天一排似乎没有这么大的数据。

以上是关于相同数据集的日期范围的主要内容,如果未能解决你的问题,请参考以下文章

对于大型数据集的查询,日期类型是不是比日期时间更高效?

使用datetime列的r dplyr :: left_join无法正确连接

列出一些数据集的索引超出范围?

减小数据集的范围

Lesson 1 数据集的建立

matplotlib:两个数据集的相同图例