Исключить конкретное значение из агрегатной функции Min / Max с использованием ICriteria - PullRequest
0 голосов
/ 28 апреля 2010

У меня есть таблица расписания (рейсов), подобная этой:

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 'портит Аронуд.

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

Так есть ли способ пропустить это значение при сравнении функции Min (без потери всей строки данных), или есть лучший способ сделать это, возможно, используя OrderIndex, который всегда указывает на правильный порядок элементов , возможно, заказываю ASC, беря 1-й, а затем Order DESC и снова беру 1-й, но я не совсем уверен, как это сделать с помощью синтаксиса критериев.

1 Ответ

2 голосов
/ 05 ноября 2010

Лучшим (и самым разумным) способом, конечно, было бы использование 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"))
    );

(Единственная причина, по которой я отправляю DateTime.MaxValue, заключается в том, что NHibernate принудительно приводит условные значения true / false к одному и тому же типу, и я не мог понять, как получить NULL в часть true .)

Этот код отправляет этот запрос механизму БД (я использую диалект SQL Server 2005 при поддержке SQL Express 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 операторах. Braindead, если это правда. Это может не быть проблемой в 2008+, хотя я не тестировал - я упоминаю SQL Server специально, потому что я предполагаю, что это то, что вы используете, поскольку 01.01.1753 - это минимальная дата, которую он позволяет 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
...