我提供了两种可能的解决方案,第一种是在 C# 中使用 LINQ,第二种是在 SQL 中使用递归 CTE 技术生成日期。随意选择你更喜欢的一个。
一、使用LINQ的解决方案:
您提到您正在使用 C# - 在这种情况下,您可以利用 LINQ,解决方案是以下程序(您可以在 LinqPad 或 VisualStudio 中运行的控制台应用程序示例)。
private static void Main(string[] args)
{
var query1 = from r in RangeTable.AsQueryable() select r;
query1.Dump("List of ranges:");
Console.WriteLine();
query1.CreateDates().Dump("Result list of dates:");
Console.ReadLine();
}
RangeTable
将范围表作为可查询对象列表返回的属性在哪里(例如,使用LinqToEntity或LinqToSQL)。
有趣的部分是扩展方法 CreateDates
:
public static IQueryable<DateTime> CreateDates(this IQueryable<DateRange> rtbl)
{
var result = new List<DateTime>();
foreach (var dr in rtbl)
{
var q = from i in Enumerable.Range(0, dr.CalcDays())
select (DateTime)dr.fromDate.AddDays(i);
foreach (var d in q) result.Add(d);
}
return result.AsQueryable<DateTime>();
}
这个函数使用一个简单的扩展方法CalcDays
计算和之间的天数fromDate
(toDate
如果 fromDate 和 toDate 相等,则有意返回 1):
public static int CalcDays(this DateRange dr)
{
var days = (int)(dr.toDate - dr.fromDate).TotalDays + 1;
return days;
}
现在有关该RangeTable
功能的一些详细信息。它需要DateRange
实现日期范围的两个属性的类:
public class DateRange
{
public DateTime fromDate;
public DateTime toDate;
}
在此示例RangeTable
中,为简单起见,在没有数据库连接的情况下定义,但也可以使用 LinqToSQL 或 LinqToEntity:
private static List<DateRange> RangeTable
{
get
{
var result = new List<DateRange>() {
new DateRange() { fromDate = new DateTime(2012, 07, 01), toDate = new DateTime(2012, 07, 03) },
new DateRange() { fromDate = new DateTime(2012, 07, 05), toDate = new DateTime(2012, 07, 07) },
new DateRange() { fromDate = new DateTime(2012, 07, 10), toDate = new DateTime(2012, 07, 12) },
new DateRange() { fromDate = new DateTime(2012, 07, 13), toDate = new DateTime(2012, 07, 16) }
};
return result;
}
}
最后,如果你没有LinqPad并想使用 VisualStudio 中的示例,你可以轻松定义两个 Dump 扩展方法,如下所示:
const string dateMask = "yyyy-MM-dd";
public static void Dump(this IQueryable<DateRange> item, string msg)
{
Console.WriteLine(msg);
foreach (var i in item)
{
Console.WriteLine(string.Format("{0} to {1}", i.fromDate.ToString(dateMask), i.toDate.ToString(dateMask)));
}
}
public static void Dump(this IQueryable<DateTime> item, string msg)
{
Console.WriteLine(msg);
foreach (var i in item)
{
Console.WriteLine(string.Format("{0}", i.ToString(dateMask)));
}
}
public static class Extensions
只需将它们与其他扩展方法一起
放在顶层即可。注意:该类DateRange
是顶级类,方法RangeTable
在内部Program
类中定义。
该示例产生以下输出:
List of ranges:
2012-07-01 to 2012-07-03
2012-07-05 to 2012-07-07
2012-07-10 to 2012-07-12
2012-07-13 to 2012-07-16
Result list of dates:
2012-07-01
2012-07-02
2012-07-03
2012-07-05
2012-07-06
2012-07-07
2012-07-10
2012-07-11
2012-07-12
2012-07-13
2012-07-14
2012-07-15
2012-07-16
最后一点:
LinqToEntity和LinqToSQL是您的数据库的“桥梁”。
它们是对象关系映射器,通过使用适当的数据库驱动程序(通过您指定的数据库连接)将您的 LINQ 语句转换为 SQL 并“及时”提交 SQL 查询,它们隐藏了有关数据库的实现特定细节,所以您可以专注于编程标准化的 LINQ 查询。
在此示例中,您需要修改属性 RangeTable,以便它查询您的 mySQL 数据库并返回一个 IQueryable 对象。您只需要以下内容:
- 添加到 LinqPad 的新连接,然后向下钻取到您需要的表,最后拖放到查询中(或者,如果名称已经匹配,只需添加连接,然后单击连接)。
使用 LinqToEntities:将新项目添加到您的项目中,在“数据”中选择“ADO.NET 实体数据模型”,然后选择“从数据库生成”,添加所需的数据库连接,添加表。它将生成一个Model1.edmx文件。例如,如果您使用Nortwind 对此进行了测试,那么示例查询将如下所示:
var entities = new NorthwindEntities1();
entities.Connection.Open();
var query1 = from c in entities.Customers select c;
二、使用 SQL 的解决方案:
您还可以使用 T-SQL 2008 中提供的公用表表达式(又名 CTE)来解决此问题:
with
sampleTable as (
select CAST('2012-07-05' as DATETIME) fromDt
,CAST('2012-07-08' as DATETIME) toDt
union
select CAST('2012-08-01' as DATETIME) fromDt
,CAST('2012-08-11' as DATETIME) toDt
),
calcdiff as (
select DATEDIFF(day, fromDt, toDt) d
,*
from sampleTable
),
recursion as (
select fromDt as dt, toDt from calcdiff
union all
select dateadd(day, 1, dt) dt2, toDt from recursion
where dateadd(day, 1, dt)<=toDt
)
select dt as Result from recursion
order by dt
SampleTable
可以用您的“真实”SQL 表替换,它仅用于本示例的完整性,calcdiff
计算日期差异,并recursion
递归计算所需的所有日期:联合的第一部分是递归的锚点,它是强制性的。注意union all
在 CTE 递归中是必需的,否则 T-SQL 将不会执行查询。
如果由于递归级别而出现错误,只需附加
option (maxrecursion 365);
这将允许“更深”的递归,例如,如果您要计算 1 年的日期,则需要它。