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を使用していますが、テーブル内の各航海の最初の出発と最後の到着を知りたい場合は、このデータをクエリするための最良の方法は何でしょうか。

私の最初の考えはグループビー(ID)で、次に到着と出発で最小と最大を実行しましたが、 `'01 /01/1753'VALUEは混乱しています。

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

したがって、最小関数の比較でこの値をスキップする方法はありますか(データの行全体を失うことなく)、または要素の正しい順序を常に示すOrderIndexを利用して、これを行うためのより良い方法があります。 ASCが1番目を取得し、次にDESCを注文し、1番目を再度取得しますが、基準構文を使用してそれを行う方法がよくわかりません。

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 が条件付きの true/false の結果を同じ型に強制するためであり、NULLそのtrue部分に a を取得する方法がわかりませんでした。)

そのコードは、このクエリを DB エンジンに送信します (私は、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。本当ならブレインデッド。これは 2008 年以降の問題ではないかもしれませんが、私はテストしていません。特に SQL Server について言及しているのは、1753 年 1 月 1 日が許容される最小の日付であるため、SQL Server を使用していると想定しているためです。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 に答える