合并日期范围 Linq 到 SQL
Merge Date Ranges Linq to SQL
注意:这是一个 linq to sql 问题,而不是一般的 linq 问题。 Linq to SQL 对您可以执行的操作有限制。
我需要合并日期范围以消除重叠。在 Whosebug 上已经有这个问题的答案,但答案不能在 linq to sql 中实现(没有递归 CTE、滞后等)。
SQL: Merge Date Ranges
使用 SQL 92 语法的第二个答案有一个错误(如果有行与相同的结束日期相交,则这些行不被视为重叠)。
有什么建议吗?
提前致谢
示例数据(无耻地从另一个stackover flow Q复制过来的)
PK |开始日期 |结束日期
1 | 2012/07/21 02:00 | 2012/07/21 04:00
2 | 2012/07/21 03:00 | 2012/07/21 10:00
3 | 2012/07/21 06:00 | 2012/07/21 17:00
4 | 2012/07/21 18:00 | 2012/07/21 19:00
合并将导致:
PK |开始日期 |结束日期
1 | 2012/07/21 02:00 | 2012/07/21 17:00
2 | 2012/07/21 18:00 | 2012/07/21 19:00
让我们先创建一个运动场:
CREATE TABLE [dbo].[dateTable](
[dt_from] [datetime2](7) NOT NULL,
[dt_to] [datetime2](7) NOT NULL
) ON [PRIMARY]
GO
INSERT INTO [dbo].[dateTable]
([dt_from]
,[dt_to])
VALUES
('2012-07-21 02:00:00', '2012-07-21 04:00:00'),
('2012-07-21 03:00:00', '2012-07-21 10:00:00'),
('2012-07-21 06:00:00', '2012-07-21 17:00:00'),
('2012-07-21 18:00:00', '2012-07-21 19:00:00')
GO
我们有一堆间隔,我们想合并它们。我们需要找到这些合并区间的起点和终点。合并开始必须满足这些约束:
- 这是某个未合并区间的开始。
- 没有这样的间隔,在这个时间或更晚结束,比这个时间更早开始。
- 如果有多个间隔同时开始,我们只取一次。
合并端类似。每个合并的开始都有一个合并的结束。唯一的问题可能是未合并的间隔,其中开始大于结束。我决定跳过它们,因为它们无效。代码在这里
var ed = new DataClasses1DataContext();
var res = ed.dateTables
.Where(d => d.dt_from <= d.dt_to) //change to < for nonzero intervals only
.Select(f => f.dt_from)
.Distinct()
.Where(w => !ed.dateTables.Any(a => a.dt_from < w && a.dt_to >= w))
.Select(date => new
{
FROM = date,
TO = ed.dateTables.Select(t => t.dt_to)
.Where(w => !ed.dateTables.Any(a => a.dt_from <= w && a.dt_to > w) && w >= date)
.Min()
}).ToArray();
foreach (var r in res)
{
Console.WriteLine("FROM: " + r.FROM + " TO: " + r.TO);
}
结果在这里:
FROM: 21.07.2012 2:00:00 TO: 21.07.2012 17:00:00
FROM: 21.07.2012 18:00:00 TO: 21.07.2012 19:00:00
生成的SQL不出意外,就是有点乱
SELECT [t1].[dt_from] AS [FROM], (
SELECT MIN([t3].[dt_to])
FROM [dbo].[dateTable] AS [t3]
WHERE (NOT (EXISTS(
SELECT NULL AS [EMPTY]
FROM [dbo].[dateTable] AS [t4]
WHERE ([t4].[dt_from] <= [t3].[dt_to]) AND ([t4].[dt_to] > [t3].[dt_to])
))) AND ([t3].[dt_to] >= [t1].[dt_from])
) AS [TO]
FROM (
SELECT DISTINCT [t0].[dt_from]
FROM [dbo].[dateTable] AS [t0]
WHERE [t0].[dt_from] <= [t0].[dt_to]
) AS [t1]
WHERE NOT (EXISTS(
SELECT NULL AS [EMPTY]
FROM [dbo].[dateTable] AS [t2]
WHERE ([t2].[dt_from] < [t1].[dt_from]) AND ([t2].[dt_to] >= [t1].[dt_from])
))
注意:这是一个 linq to sql 问题,而不是一般的 linq 问题。 Linq to SQL 对您可以执行的操作有限制。
我需要合并日期范围以消除重叠。在 Whosebug 上已经有这个问题的答案,但答案不能在 linq to sql 中实现(没有递归 CTE、滞后等)。
SQL: Merge Date Ranges
使用 SQL 92 语法的第二个答案有一个错误(如果有行与相同的结束日期相交,则这些行不被视为重叠)。
有什么建议吗?
提前致谢
示例数据(无耻地从另一个stackover flow Q复制过来的)
PK |开始日期 |结束日期
1 | 2012/07/21 02:00 | 2012/07/21 04:00
2 | 2012/07/21 03:00 | 2012/07/21 10:00
3 | 2012/07/21 06:00 | 2012/07/21 17:00
4 | 2012/07/21 18:00 | 2012/07/21 19:00
合并将导致:
PK |开始日期 |结束日期
1 | 2012/07/21 02:00 | 2012/07/21 17:00
2 | 2012/07/21 18:00 | 2012/07/21 19:00
让我们先创建一个运动场:
CREATE TABLE [dbo].[dateTable](
[dt_from] [datetime2](7) NOT NULL,
[dt_to] [datetime2](7) NOT NULL
) ON [PRIMARY]
GO
INSERT INTO [dbo].[dateTable]
([dt_from]
,[dt_to])
VALUES
('2012-07-21 02:00:00', '2012-07-21 04:00:00'),
('2012-07-21 03:00:00', '2012-07-21 10:00:00'),
('2012-07-21 06:00:00', '2012-07-21 17:00:00'),
('2012-07-21 18:00:00', '2012-07-21 19:00:00')
GO
我们有一堆间隔,我们想合并它们。我们需要找到这些合并区间的起点和终点。合并开始必须满足这些约束:
- 这是某个未合并区间的开始。
- 没有这样的间隔,在这个时间或更晚结束,比这个时间更早开始。
- 如果有多个间隔同时开始,我们只取一次。
合并端类似。每个合并的开始都有一个合并的结束。唯一的问题可能是未合并的间隔,其中开始大于结束。我决定跳过它们,因为它们无效。代码在这里
var ed = new DataClasses1DataContext();
var res = ed.dateTables
.Where(d => d.dt_from <= d.dt_to) //change to < for nonzero intervals only
.Select(f => f.dt_from)
.Distinct()
.Where(w => !ed.dateTables.Any(a => a.dt_from < w && a.dt_to >= w))
.Select(date => new
{
FROM = date,
TO = ed.dateTables.Select(t => t.dt_to)
.Where(w => !ed.dateTables.Any(a => a.dt_from <= w && a.dt_to > w) && w >= date)
.Min()
}).ToArray();
foreach (var r in res)
{
Console.WriteLine("FROM: " + r.FROM + " TO: " + r.TO);
}
结果在这里:
FROM: 21.07.2012 2:00:00 TO: 21.07.2012 17:00:00
FROM: 21.07.2012 18:00:00 TO: 21.07.2012 19:00:00
生成的SQL不出意外,就是有点乱
SELECT [t1].[dt_from] AS [FROM], (
SELECT MIN([t3].[dt_to])
FROM [dbo].[dateTable] AS [t3]
WHERE (NOT (EXISTS(
SELECT NULL AS [EMPTY]
FROM [dbo].[dateTable] AS [t4]
WHERE ([t4].[dt_from] <= [t3].[dt_to]) AND ([t4].[dt_to] > [t3].[dt_to])
))) AND ([t3].[dt_to] >= [t1].[dt_from])
) AS [TO]
FROM (
SELECT DISTINCT [t0].[dt_from]
FROM [dbo].[dateTable] AS [t0]
WHERE [t0].[dt_from] <= [t0].[dt_to]
) AS [t1]
WHERE NOT (EXISTS(
SELECT NULL AS [EMPTY]
FROM [dbo].[dateTable] AS [t2]
WHERE ([t2].[dt_from] < [t1].[dt_from]) AND ([t2].[dt_to] >= [t1].[dt_from])
))