web-dev-qa-db-fra.com

Comment puis-je inclure des valeurs nulles dans un MIN ou MAX?

J'ai une table où je stocke des données de durée. la table a un schéma similaire à:

ID INT NOT NULL IDENTITY(1,1)   
RecordID INT NOT NULL  
StartDate DATE NOT NULL  
EndDate DATE NULL  

Et j'essaie de déterminer les dates de début et de fin de chaque identifiant d'enregistrement, donc les dates minimales StartDate et EndDate maximales. StartDate n'est pas nullable, donc je n'ai pas à m'inquiéter à ce sujet, mais j'ai besoin de MAX (EndDate) pour indiquer qu'il s'agit actuellement d'une durée d'exécution.

Il est important que je maintienne la valeur NULL de EndDate et que je la traite comme la valeur maximale.

La tentative la plus simple (ci-dessous) ne fonctionne pas, soulignant le problème selon lequel MIN et MAX ignoreront NULLS (source: http://technet.Microsoft.com/en-us/library/ms179916.aspx ).

SELECT recordid, MIN(startdate), MAX(enddate) FROM tmp GROUP BY recordid

J'ai créé un SQL Fiddle avec la configuration de base terminée.

http://sqlfiddle.com/#!3/b0a75

Comment puis-je plier SQL Server 2008 à ma volonté pour produire le résultat suivant à partir des données fournies dans SQLFiddle?

RecordId  Start       End  
1         2009-06-19  NULL
2         2012-05-06  NULL
3         2013-01-25  NULL
4         2004-05-06  2009-12-01
55
Ant Swift

C'est un peu moche mais parce que les NULLs ont une signification particulière pour vous, c'est la façon la plus propre de le faire:

SELECT recordid, MIN(startdate),
   CASE WHEN MAX(CASE WHEN enddate IS NULL THEN 1 ELSE 0 END) = 0
        THEN MAX(enddate)
   END
FROM tmp GROUP BY recordid

C'est-à-dire que si une ligne a un NULL, nous voulons le forcer à être la réponse. Nous ne devons renvoyer le NULL (ou MIN) que si aucune ligne ne contient un MAX.

63

L’effet souhaité est de traiter la valeur NULL comme la date la plus longue possible, puis de la remplacer par NULL à la fin:

SELECT RecordId, MIN(StartDate), NULLIF(MAX(COALESCE(EndDate,'9999-12-31')),'9999-12-31') 
  FROM tmp GROUP BY RecordId

Selon votre violon, cela retournera les résultats exacts que vous spécifiez dans toutes les conditions.

39
Matthew Erwin

in my expression - count (enddate) compte le nombre de lignes dont la date de fin n'est pas nulle. count (*) compte le nombre de lignes total. En comparant, vous pouvez facilement déterminer si une date de fin contient null. Si elles sont identiques, alors max (enddate) est le résultat. Sinon, le cas par défaut retournera null qui est aussi la réponse. C'est un moyen très populaire de faire cette vérification exacte.

SELECT recordid, 
MIN(startdate), 
case when count(enddate) = count(*) then max(enddate) end
FROM tmp 
GROUP BY recordid
17
t-clausen.dk

Utilisez IsNull

SELECT recordid, MIN(startdate), MAX(IsNull(enddate, Getdate()))
FROM tmp 
GROUP BY recordid

J'ai modifié MIN dans la deuxième instruction en MAX

3
Marko Juvančič

En supposant que vous n'ayez qu'un seul enregistrement avec null dans la colonne EndDate pour un RecordID donné, quelque chose comme ceci devrait vous donner le résultat souhaité:

WITH cte1 AS
(
SELECT recordid, MIN(startdate) as min_start , MAX(enddate) as max_end
FROM tmp 
GROUP BY recordid
)

SELECT a.recordid, a.min_start , 
CASE 
   WHEN b.recordid IS  NULL THEN a.max_end
END as max_end
FROM cte1 a
LEFT JOIN tmp b ON (b.recordid = a.recordid AND b.enddate IS NULL)
1
a1ex07

Utilisez la fonction analytique:

select case when 
    max(field) keep (dense_rank first order by datfin desc nulls first) is null then 1 
    else 0 end as flag 
from MYTABLE;
0
TheBakker