每次在同一日期栏中更改价格后,如何选择首个日期和最后一个日期(加上一天)?

时间:2018-07-18 04:13:42

标签: sql-server sql-server-2008 tsql

假设我有一个表格,其中价格和日期为列,如:

Price       Date
3.20000000  2018-01-14
3.20000000  2018-01-18
3.20000000  2018-01-19
4.00000000  2018-02-10
4.00000000  2018-02-11
4.00000000  2018-02-12
5.10000000  2018-02-16
3.20000000  2018-03-11
4.00000000  2018-03-15

我希望每次出现新价格时都获得第一个日期,并希望在同一日期列中获得该价格的最后日期(加上一天)。 请注意,价格可以上下波动,同一价格在整个表格中可能出现多次。因此上表的结果应为:

Price       Date
3.20000000  2018-01-14
3.20000000  2018-01-20 -- 19 + 1
4.00000000  2018-02-10
4.00000000  2018-02-13 -- 12 + 1
5.10000000  2018-02-16
5.10000000  2018-02-17 -- 16 + 1
3.20000000  2018-03-11
3.20000000  2018-03-12 -- 11 + 1
4.00000000  2018-03-15
4.00000000  2018-03-16 -- 15 + 1

我希望使用自联接,是否可以在没有row_number函数的情况下进行自联接?

3 个答案:

答案 0 :(得分:1)

这是一个变体。您可以对其进行修改以适合您的需求。

DECLARE @DataSource TABLE
(
    [Price] SMALLMONEY
   ,[Date] DATE
);

INSERT INTO @DataSource ([Price], [Date])
VALUES ('3.20000000', '2018-01-14')
      ,('3.20000000', '2018-01-18')
      ,('3.20000000', '2018-01-19')
      ,('4.00000000', '2018-02-10')
      ,('4.00000000', '2018-02-11')
      ,('4.00000000', '2018-02-12')
      ,('5.10000000', '2018-02-16')
      ,('3.20000000', '2018-03-11')
      ,('4.00000000', '2018-03-15');

-- we need to order the data, if you do not want to use ROW_NUMBER(), use another way to order it (for exmaple record ID of each row using IDENTITY column)
WITH DataSourceOrdered AS
(
    SELECT ROW_NUMBER() OVER(ORDER BY [Date]) AS [RowID]
         ,[Price]
         ,[Date]
    FROM @DataSource
), RecordsWhenPriceChanged AS
(
    -- records when price has changed
    SELECT DS1.[Price]
          ,DS1.[Date]
          ,DS1.[RowID]
    FROM DataSourceOrdered DS1
    INNER JOIN DataSourceOrdered DS2
        ON DS1.[RowID] -1 = DS2.[RowID]
        AND DS1.[Price] <> DS2.[Price]
    UNION ALL
    -- the initial record
    SELECT [Price]
          ,[Date]
          ,[RowID]
    FROM DataSourceOrdered
    WHERE [RowID] = 1
)
-- getting current records
SELECT [Price]
      ,[Date]
      ,[RowID]
FROM RecordsWhenPriceChanged
UNION ALL
-- getting previous records + 1 day for each
SELECT DS1.[Price]
      ,DATEADD(DAY, 1, DS1.[Date])
      ,DS1.[RowID]
FROM DataSourceOrdered DS1
INNER JOIN RecordsWhenPriceChanged DS2
    ON DS1.[RowID] + 1 = DS2.[RowID]
-- getting the last record
UNION ALL
SELECT *
FROM
(
    SELECT TOP 1 [Price]
                ,DATEADD(DAY, 1,[Date]) AS [Date]
                ,[RowID]
    FROM RecordsWhenPriceChanged
    ORDER BY [Date] DESC
) DS
ORDER BY [Date];

enter image description here

答案 1 :(得分:1)

正如我在评论中所写,这是一个典型的空白和孤岛问题,解决此问题的简单方法是两次使用row_number

首先,创建并填充示例表(在您将来的问题中为我们保存此步骤):

DECLARE @T AS TABLE
(
    Price decimal(15,5),
    [Date] Date
)

INSERT INTO @T (Price, [Date]) VALUES
(3.20000000, '2018-01-14'),
(3.20000000, '2018-01-18'),
(3.20000000, '2018-01-19'),
(4.00000000, '2018-02-10'),
(4.00000000, '2018-02-11'),
(4.00000000, '2018-02-12'),
(5.10000000, '2018-02-16'),
(3.20000000, '2018-03-11'),
(4.00000000, '2018-03-15')

现在,使用公共表格表达式标记岛:

;WITH CTE AS
(
SELECT  Price, 
        [Date], 
        ROW_NUMBER() OVER(ORDER BY [Date]) - 
        ROW_NUMBER() OVER(PARTITION BY Price ORDER BY [Date]) As Island
FROM @T
)

然后使用联合查询进行查询-一个使用min(date) over(partition by island),第二个使用max(dateadd(day, 1, date)) over(partition by island),按日期对结果进行排序:

SELECT Price, MIN(Date) OVER (PARTITION BY Island) As [Date]
FROM CTE
UNION
SELECT Price, DATEADD(DAY, 1, MAX(Date)  OVER (PARTITION BY Island))
FROM CTE
ORDER BY Date

结果:

Price       Date
3,20000     14.01.2018
3,20000     20.01.2018
4,00000     10.02.2018
4,00000     13.02.2018
5,10000     16.02.2018
5,10000     17.02.2018
3,20000     11.03.2018
3,20000     12.03.2018
4,00000     15.03.2018
4,00000     16.03.2018

You can see a live demo on rextester.

答案 2 :(得分:0)

您可以尝试不使用row_number的此解决方案。

DECLARE @MyTable TABLE ([Price] DECIMAL(18,8), [Date] DATE);

INSERT INTO @MyTable ([Price], [Date])VALUES 
('3.20000000', '2018-01-14'),
('3.20000000', '2018-01-18'),
('3.20000000', '2018-01-19'),
('4.00000000', '2018-02-10'),
('4.00000000', '2018-02-11'),
('4.00000000', '2018-02-12'),
('5.10000000', '2018-02-16'),
('3.20000000', '2018-03-11'),
('4.00000000', '2018-03-15')

;WITH CTE AS 
(
    SELECT * FROM @MyTable 
    UNION ALL
    SELECT Price, DATEADD(DAY,1,[Date]) FROM @MyTable 
)
, CTE2 AS (
    SELECT T1.Price, MIN(T1.[Date]) MinDate, MAX(T1.[Date]) MaxDate
    FROM CTE T1
        OUTER APPLY( SELECT COUNT(DISTINCT Price) DCNT, MIN(Date) [Date] FROM CTE T2 WHERE T2.Date >= T1.Date ) TA1 
    GROUP BY T1.Price, TA1.DCNT
)
SELECT Price, [Date] FROM CTE2 UNPIVOT ( [Date] FOR Col IN (MinDate, MaxDate)) UNPVT
ORDER BY [Date]

结果:

Price                                   Date
--------------------------------------- ----------
3.20000000                              2018-01-14
3.20000000                              2018-01-20
4.00000000                              2018-02-10
4.00000000                              2018-02-13
5.10000000                              2018-02-16
5.10000000                              2018-02-17
3.20000000                              2018-03-11
3.20000000                              2018-03-12
4.00000000                              2018-03-15
4.00000000                              2018-03-16