日期相同数据集的范围

时间:2015-05-26 09:55:12

标签: sql sql-server ssms

我正在尝试构建一个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

4 个答案:

答案 0 :(得分:3)

非关系解决方案

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

  • GROUP BY无法工作

  • 使用ROW_NUMBER()强制数据进入物理记录文件系统结构,然后将其作为物理记录处理。以巨大的性能成本。当然,为了编写这样的代码,它会迫使你根据RFS 思考,而不是用关系术语思考。

  • 使用CTE是一样的。迭代数据,尤其是不变的数据。价格略有不同。

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

关系解决方案

  1. 您的数据是关系型的,合乎逻辑的,只有两个数据列才是必需的。

  2. 当然,我们必须形成一个View(派生的Relation),以获得所需的报告,但它包含纯SELECT,这与处理完全不同(将其转换为文件,这是物理的,然后处理文件;或临时表;或工作表;或CTE;或ROW_Number();等)。

  3. 与#34;理论家"的哀叹相反,谁有一个议程,SQL完全处理关系数据。你的数据是关系数据。

  4. 因此,维护关系思维模式,数据的关系视图和集合处理心态。可以使用单个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
    

    方法,通用

    当然这是一种方法,因此它是通用的,可用于确定任何数据范围(此处为From_范围)的To_Date任何数据更改(此处为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
    

    享受。

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

答案 1 :(得分:0)

您可以通过添加分组列来完成此操作。一个巧妙的技巧是两个数字序列的差异 - 当差异是恒定的,那么价格是相同的。

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;

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

答案 2 :(得分:-1)

这就是你要找的东西

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

答案 3 :(得分:-2)

使用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;

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