2010-04-27 7 views
0

У меня есть расписание (Voyages) таблицу, как это:Исключить конкретное значение из агрегатной функции Min/Max с использованием ICriteria

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, беря 1-й, а затем порядок DESC и беря 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")) 
    ); 

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

этот код отправляет этот запрос в двигатель DB (я использую 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 не likie позиционных параметров в CASE отчетности. Braindead, если это правда. Это может быть не проблема в 2008 году, хотя я не тестировал - я упоминаю SQL Server специально, потому что я предполагаю, что это то, что вы используете с 1/1/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 
+0

ничего себе, что это довольно хорошее усилие на вопрос с пару месяцев старых (почти мертв) =), мое мнение идет по первому варианту и исправляет этот грязный дизайн, если вы спросите меня, что у меня есть OrderIndex в дополнение к Ordered by Arrival, а затем отправлением кажется немного запутанным, вы должны вернуться и переосмыслить, если вы действительно хотите этого «1/1/1753 00:00:00 «возиться, если порт с прибытием, но не выезд (и наоборот) действительно подходит вашей модели ?, с моей точки зрения, это не имеет смысла, вы, возможно, могли бы принять дату, если пользователь не предусматривает этого. – JOBG

+0

@ Омар: Да. Если бы я возился с дизайном, я бы нормализовался в таблицу отправлений и таблицу прибытий, а затем, вероятно, создавал индексированное представление для рейсов. Тогда не было бы необходимости делать сумасшедший запрос на уровне приложения! –