Лучшим (и самым разумным) способом, конечно, было бы использовать значения 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
. Безмозглый, если это правда. Это может не быть проблемой в 2008+, хотя я не проверял — я упоминаю SQL Server специально, потому что я предполагаю, что вы используете его, поскольку 01.01.1753 — это минимальная дата, которую он допускает в datetime
.
Итак, где же остается эта проблема? Есть варианты.
- Исправьте схему базы данных, как указано в моем самом первом абзаце (идеально). Обратите внимание, что вам не нужно будет разрешать какие-либо
NULL
значения в ваших данных, если схема нормализована.
- Посмотрите, можете ли вы вместо этого написать запрос, используя HQL (я не эксперт, поэтому я даже не могу сказать, возможно ли это).
- Выясните, что это не проблема в SQL 2008+ и/или вашем целевом механизме(ах) базы данных, и это все, на что вы когда-либо собирались нацеливаться.
- Перепишите запрос, чтобы полностью обойти сумасшедшие значения и вместо этого использовать столбец
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
person
Jon Seigel
schedule
05.11.2010