0

我有一个这样的时间表(航行)表:

ID      Arrival        Departure    OrderIndex
1     01/01/1753      02/10/2009        0
1     02/11/2009      02/15/2009        1
1     02/16/2009      02/19/2009        2
1     02/21/2009      01/01/1753        3

2     01/01/1753      03/01/2009        0
2     03/04/2009      03/07/2009        1
2     03/09/2009      01/01/1753        2

按照设计'01/01/1753',如果用户没有填写捕获屏幕上的字段以及从未提供的第一次到达和最后一次离开,我将保存为默认值。我使用 Nhibernate 和 Criteria,我想知道如果我想知道表中每个航次的第一次出发和最后一次到达,查询这些数据的最佳方法是什么。

我的第一个想法是 groupby (ID),然后在到达和离开时做一些 Min 和 Max,但 ''01/01/1753' VALUE 很混乱。

...
.SetProjection(Projections.ProjectionList()
               .Add(Projections.GroupProperty("ID"), "ID")
               .Add(Projections.Min("DepartureDate"), "DepartureDate")
               .Add(Projections.Max("ArrivalDate"), "ArrivalDate")
               )
...

那么有没有办法在 Min 函数比较中跳过这个值(不会丢失整行数据),或者有更好的方法来做到这一点,也许利用总是指示元素正确顺序的 OrderIndex,也许是排序ASC 获得第一个然后 Order DESC 并再次获得第一个,但我不太确定如何使用标准语法来做到这一点。

4

1 回答 1

2

当然,最好的(也是最明智的)方法是使用NULL值而不是最小值datetime。如果您已经这样做了(或更改您的应用程序以执行此操作),那么您编写的代码将按照编写的方式工作。现在的情况,我保证那些虚假的价值观最终会回来困扰某人(如果这是一个真正的应用程序)。也许不是你,而是下一个人。当然,你也应该规范化这个表......

但无论如何。稍后再详细介绍。你问了一个具体的问题。

这是应该工作的代码(不是因为缺乏测试——继续阅读)。

DateTime bogusDate = new DateTime(1753, 1, 1);

ICriteria criteria =
    session.CreateCriteria(typeof(Voyage))
    .SetProjection
    (
        Projections.ProjectionList()
        .Add
        (
            Projections.Min
            (
                Projections.Conditional
                (
                    Restrictions.Eq("Departure", bogusDate),
                    Projections.Constant(DateTime.MaxValue, NHibernateUtil.DateTime),
                    Projections.Property("Departure")
                )
            )
        )
        .Add(Projections.Max("Arrival"))
        .Add(Projections.GroupProperty("Id"))
    );

(我发送 a 的唯一原因DateTime.MaxValue是 NHibernate 将条件真/假结果强制为同一类型,而我不知道如何将 aNULL放入该true部分。)

该代码将此查询发送到数据库引擎(我使用的是由 SQL Express 2005 支持的 SQL Server 2005 方言):

SELECT min((case when this_.Departure = ? then ? else this_.Departure end)) as y0_,
    max(this_.Arrival) as y1_,
    this_.Id as y2_
    FROM Voyages this_
    GROUP BY this_.Id;

@p0 = 1/1/1753 12:00:00 AM,
@p1 = 12/31/9999 11:59:59 PM

看起来不错。现在,我说这应该可行,因为当您插入参数并直接在引擎上运行查询时,它会给出所需的结果。然而,在我的机器上,使用 NHibernate,一切都崩溃了:

System.Data.SqlClient.SqlException: Incorrect syntax near '?'.

这告诉我 SQL Server 在CASE语句中没有 likie 位置参数。如果是真的,脑残。这在 2008+ 年可能不是问题,尽管我没有测试过——我特别提到 SQL Server,因为我假设这就是你正在使用的,因为 1753 年 1 月 1 日是它允许的最小日期datetime.


那么,这会在哪里留下这个问题呢?有选项。

  1. 修复我在第一段中提到的数据库模式(理想)。请注意,如果模式被规范化,您将不必允许任何值进入您的数据。 NULL
  2. 看看您是否可以改用 HQL 编写查询(我不是专家,所以我什至不能说这是否可能)。
  3. 发现这在 SQL 2008+ 和/或您的目标数据库引擎中不是问题,而这就是您要针对的所有目标。
  4. 重写查询以完全绕过疯狂值并利用该OrderIndex列代替。我已经用 SQL 编写了这个——我不知道如何使用ICriteria(如果你想受到惩罚,请帮自己一个忙,把时间花在选项 #1 上)。请注意,这比原始查询快一半,这几乎是最佳的 AFAIK:
SELECT
    s.Id,
    v1.Departure,
    v2.Arrival
    FROM
    (
        SELECT DISTINCT
            Id,
            MAX(OrderIndex) AS MaxIndex,
            MIN(OrderIndex) AS MinIndex
            FROM Voyages
            GROUP BY Id
    ) s
    INNER JOIN Voyages v1 ON v1.Id = s.Id AND v1.OrderIndex = MinIndex
    INNER JOIN Voyages v2 ON v2.Id = s.Id AND v2.OrderIndex = MaxIndex
于 2010-11-05T04:10:24.290 回答