-2

我对使用 c# 的 (My)Sql 中的日期差异有疑问。
在我的数据库中,我有一个包含如下日期范围的表:

----------------------
从日期到日期
-------------------------
2012-07-01 2012-07-03
2012-07-05 2012-07-07
2012-07-10 2012-07-12
2012-07-13 2012-07-16
--------------------------

从这些范围内,我想计算所有包含的日期作为结果,即:

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

SELECT DATEDIFF('2012-01-01', '2012-02-02')不像我预期的那样工作,它只给出总天数......我想要这之间的所有日期,包括范围的日期(FromDate和ToDate)。

你能告诉我如何实现这个(在 SQL 或 C# 中)吗?

4

3 回答 3

1

我提供了两种可能的解决方案,第一种是在 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将范围表作为可查询对象列表返回的属性在哪里(例如,使用LinqToEntityLinqToSQL)。

有趣的部分是扩展方法 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计算和之间的天数fromDatetoDate如果 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

最后一点:

LinqToEntityLinqToSQL是您的数据库的“桥梁”。

它们是对象关系映射器,通过使用适当的数据库驱动程序(通过您指定的数据库连接)将您的 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 年的日期,则需要它。

于 2012-07-05T08:56:21.597 回答
0

如果我理解正确,您的表格包含以下内容:

------------------------
From date      to date
-------------------------
2012-07-01    2012-07-03
2012-07-05    2012-07-07
2012-07-10    2012-07-12
2012-07-13    2012-07-16
--------------------------

并且您想针对它运行 SQL 并取回列出的每个“从日期”和“到日期”之间的所有日期。

简单地说(即不考虑太多),MySql 不会那样做——它从表中返回现有记录。由于您已经在 ac# 应用程序中,因此从 MySql 读取记录并创建您自己的所有相关日期的列表似乎更容易(尽管这取决于您随后想要对它们做什么)。

于 2012-07-05T07:40:09.017 回答
0

谢谢所有我在 C# 中修复了它。在 mysql 中找不到任何东西。

dtDetails 是一个数据表,其中包含来自 DB 的值(从日期和到日期)

然后我又创建了一个数据表并存储了(fromdate -- todate)中的所有值

然后我将使用该数据表。

如果(dtDetails.Rows.Count > 0)

{ foreach (DataRow dr in dtDetails.Rows) { DateTime startingDate = Convert.ToDateTime( dr["fromdate"]); DateTime endingDate = Convert.ToDateTime(dr["todate"]); DateTime newdate; for (newdate = startingDate; newdate <= endingDate; newdate = newdate.AddDays(1)) { DataRow dr2 = dt2.NewRow(); dt2.Rows.Add(newdate); } } }
于 2012-07-05T10:05:30.300 回答