一组相同数据的日期范围
Date Range for set of same data
我正在尝试构建一个 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
这就是您要找的东西
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
您可以通过添加分组列来完成此操作。一个巧妙的技巧是两个数字序列的差异——当差异是常数时,那么价格是相同的。
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;
注意:请注意价格存储为固定小数而不是浮动小数。否则,看起来相同的值实际上可能并不相同。
使用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。
在最终组中,您将获得所需的结果。
非关系解决方案
我认为其他答案都不正确。
GROUP BY
不行
使用ROW_NUMBER()
强制数据进入记录归档系统结构,这是物理的,然后将其作为物理记录处理。以巨大的性能成本。当然,为了编写这样的代码,它迫使您思考而不是关系术语。
使用 CTE 是一样的。遍历数据,尤其是不会改变的数据。成本略有不同。
由于一系列不同的原因,游标肯定是错误的。 (a) 游标需要代码,并且您请求了一个视图 (b) 游标放弃了集合处理引擎,并恢复为逐行处理。同样,不需要。如果我的任何团队中的开发人员在关系数据库(即不是记录归档系统)上使用游标或临时表,我就会枪毙他们。
关系解决方案
您的 data 是关系型的,符合逻辑的,给定的两个 data 列都是必需的。
当然,我们必须形成一个View(derived Relation),才能得到想要的报表,但是它由纯SELECT组成,这与处理(转换它)有很大不同到物理的 file,然后处理 file;或临时表;或工作表;或 CTE;或 ROW_Number (); 等).
与有议程的 "theoreticians" 的哀叹相反,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
方法,通用
当然这是一个方法,所以是通用的,可以用来判断任意数据范围的From_
和To_
(这里是Date
范围) ,基于任何数据变化(这里是 Price
的变化)。
在这里,你的Dates
是连续的,所以Date_Next
的确定很简单:将Date
增加1天。如果 PK 正在增加但 不是 连续(例如 DateTime
或 TimeStamp
或其他一些密钥),请更改 Derived Table 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 查询,它将为我提供具有相同价格的日期的日期范围。如果价格出现突破,我希望在新的行中看到它。即使在一个月中的某个时间有相同的价格,如果价格在两者之间的某个时间发生变化,我希望将其视为具有特定日期范围的两个单独的行。
示例数据:
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
这就是您要找的东西
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
您可以通过添加分组列来完成此操作。一个巧妙的技巧是两个数字序列的差异——当差异是常数时,那么价格是相同的。
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;
注意:请注意价格存储为固定小数而不是浮动小数。否则,看起来相同的值实际上可能并不相同。
使用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。 在最终组中,您将获得所需的结果。
非关系解决方案
我认为其他答案都不正确。
GROUP BY
不行使用
ROW_NUMBER()
强制数据进入记录归档系统结构,这是物理的,然后将其作为物理记录处理。以巨大的性能成本。当然,为了编写这样的代码,它迫使您思考而不是关系术语。使用 CTE 是一样的。遍历数据,尤其是不会改变的数据。成本略有不同。
由于一系列不同的原因,游标肯定是错误的。 (a) 游标需要代码,并且您请求了一个视图 (b) 游标放弃了集合处理引擎,并恢复为逐行处理。同样,不需要。如果我的任何团队中的开发人员在关系数据库(即不是记录归档系统)上使用游标或临时表,我就会枪毙他们。
关系解决方案
您的 data 是关系型的,符合逻辑的,给定的两个 data 列都是必需的。
当然,我们必须形成一个View(derived Relation),才能得到想要的报表,但是它由纯SELECT组成,这与处理(转换它)有很大不同到物理的 file,然后处理 file;或临时表;或工作表;或 CTE;或 ROW_Number (); 等).
与有议程的 "theoreticians" 的哀叹相反,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
方法,通用
当然这是一个方法,所以是通用的,可以用来判断任意数据范围的From_
和To_
(这里是Date
范围) ,基于任何数据变化(这里是 Price
的变化)。
在这里,你的Dates
是连续的,所以Date_Next
的确定很简单:将Date
增加1天。如果 PK 正在增加但 不是 连续(例如 DateTime
或 TimeStamp
或其他一些密钥),请更改 Derived Table 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
尽情享受吧。
欢迎大家留言、提问等