2010-04-27 19 views
0

J'ai une table horaire (Voyages) comme ceci:Excluez valeur spécifique d'une fonction d'agrégation Min/Max en utilisant 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 

par sa conception i sauver '01/01/1753' comme valeur par défaut si l'utilisateur ne remplit pas le champ sur l'écran de capture et pour la toute première Arrivée et le tout dernier Départ qui ne sont jamais fournis. Im utilisant Nhibernate et Critères, et je me demande quelle est la meilleure façon d'interroger ces données si je veux connaître le premier départ et la dernière arrivée pour chaque voyage dans le tableau. Ma première pensée a été un groupby (ID) et puis faire un peu de Min et Max avec l'arrivée et le départ, mais la valeur de '01/01/1753 'est aronud déconnecter.

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

est donc là un moyen de sauter cette valeur dans la comparaison de la fonction Min (sans perdre toute la ligne de données), ou il y a une meilleure façon de le faire, en utilisant peut-être le OrderIndex qui indiquent toujours le bon ordre des éléments, peut-être commander ASC prenant le 1er et ensuite DESC de commande et de prendre le 1 er à nouveau, mais je ne suis pas tout à fait sûr de savoir comment faire cela avec la syntaxe des critères.

Répondre

2

Le meilleur (et le plus sain) moyen, bien sûr, serait d'utiliser les valeurs NULL au lieu des valeurs minimum datetime. Si vous aviez fait cela (ou changé votre application pour le faire), alors le code que vous avez écrit fonctionnera comme écrit. De la façon dont c'est maintenant, je garantis que ces fausses valeurs reviendront hanter quelqu'un (si c'est une vraie application). Peut-être pas vous, mais le prochain gars. Bien sûr, vous devriez également normaliser ce tableau ...

Mais de toute façon. Plus sur ce truc plus tard. Vous avez posé une question spécifique.

Voici le code devrait de travail (pas faute de tests - continuer à lire).

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")) 
    ); 

(La seule raison pour laquelle je suis l'envoi d'une DateTime.MaxValue est parce que NHibernate force les résultats vrais/faux conditionnels au même type, et je ne pouvais pas comprendre comment obtenir un NULL dans la partie true.)

ce code envoie cette requête au moteur DB (j'utilise SQL Server 2005 dialecte soutenu par 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 

ce qui a l'air bien. Maintenant, je dis cela devrait de travail, parce que lorsque vous branchez les paramètres et exécutez la requête directement sur le moteur, il donne les résultats souhaités. Cependant, sur ma machine, en utilisant NHibernate, tout explose:

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

Ce qui me dit que SQL Server aucun paramètre de position de likie dans CASE déclarations. Braindead si c'est vrai. Cela peut ne pas être un problème en 2008+, bien que je n'ai pas testé - je mentionne SQL Server spécifiquement parce que je suppose que c'est ce que vous utilisez depuis le 1/1/1753 est la date minimale qu'il permet dans un datetime.


Alors, où cela laisse-t ce problème? Il y a des options.Fixer le schéma de la base de données comme mentionné dans mon tout premier paragraphe (idéal)

  1. Notez que vous ne devrez pas autoriser des valeursNULL dans vos données si le schéma est normalisé.
  2. Voyez si vous pouvez écrire la requête en utilisant HQL à la place (je ne suis pas un expert, donc je ne pourrais même pas dire si c'est possible).
  3. Découvrez que ce n'est pas un problème dans SQL 2008+ et/ou vos moteurs de base de données cible et c'est tout ce que vous allez cibler.
  4. Réécrire la requête pour contourner entièrement les valeurs folles et exploiter la colonne OrderIndex à la place. J'ai écrit cela en SQL - Je ne suis pas sûr de savoir comment écrire cela en utilisant ICriteria (et si vous voulez la punition de cela, faites-vous une faveur et passez le temps sur l'option n ° 1 à la place). Notez que cela est moins de la moitié aussi vite que la requête initiale, qui est AFAIK presque optimale:
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

wow qui est un très bon effort pour une question avec deux ou trois mois (presque mort) =), mon avis aller pour la première option et réparer ce design désordonné, si vous me demandez d'avoir le OrderIndex en plus d'une commande par arrivée puis par départ semblent un peu confus, vous devriez revenir en arrière et repenser si vous voulez vraiment ce "1/1/1753 00:00:00 "déconner, si un port avec une arrivée mais pas de départ (et vice versa) correspond vraiment à votre modèle ?, de mon point de vue, il n'a pas de sens, vous pourriez peut-être supposer une date si l'utilisateur ne fournit pas cela. – JOBG

+0

@Omar: Oui. Si je me trompais dans la conception, je normaliserais dans une table de départs et une table des arrivées, alors probablement créer une vue indexée pour les voyages. Ensuite, il n'y aurait pas besoin de faire une requête folle au niveau de l'application! –