J'utilise SQL Server 2008 R2
.
J'ai besoin de sortinger une table par la valeur minimale de deux colonnes.
La table ressemble à ceci:
ID: integer; Date1: datetime; Date2: datetime.
Je veux que mes données soient sortingées par minime de deux dates.
Quelle est la manière la plus simple de sortinger cette table de cette façon?
Les colonnes NOT NULL Vous devez append l'instruction CASE dans la clause ORDER BY de la manière suivante:
SELECT Id, Date1, Date2 FROM YourTable ORDER BY CASE WHEN Date1 < Date2 THEN Date1 ELSE Date2 END
Colonnes NULLABLE . Comme Zohar Peled a écrit dans les commentaires si les colonnes sont nullables, vous pouvez utiliser ISNULL
(mais mieux utiliser COALESCE
au lieu de ISNULL
, car c'est le ANSI SQL standard
) comme suit:
SELECT Id, Date1, Date2 FROM YourTable ORDER BY CASE WHEN COALESCE(Date1, '1753-01-01') < COALESCE(Date2, '1753-01-01') THEN Date1 ELSE Date2 END
Vous pouvez lire à propos de la norme ANSI dateformat 1753-01-01
ici .
Utilisez un CASE
dans ORDER BY
:
ORDER BY case when date1 < date2 then date1 else date2 end
La manière la plus simple consiste à utiliser le mot-key VALUES
, comme suit:
SELECT ID, Date1, Date2 FROM YourTable ORDER BY (SELECT MIN(v) FROM (VALUES (Date1), (Date2)) AS value(v))
Ce code fonctionnera pour tous les cas, même avec des colonnes nullables .
Modifier :
La solution avec le mot-key COALESCE
n'est pas universelle. Il a les ressortingctions importantes:
Date
(si vous utilisez les dates antérieures au 01/01/1753
) NULL
. Il interprète la valeur NULL
comme valeur de datetime
minimale. Mais est-ce vrai? Ce n'est même pas datetime
, ce n'est rien. IF
sera beaucoup plus compliquée si nous utilisons plus de deux colonnes. Selon la question:
Quelle est la manière la plus simple de sortinger cette table de cette façon?
La solution la plus courte et la plus simple est celle décrite ci-dessus, car:
Date
et vous n'avez pas besoin de modifier le code. Édition 2:
Zohar Peled a suggéré le mode de command suivant:
Je voudrais ordonner les lignes par ces règles: d'abord, lorsque les deux null, seconde, lorsque date1 est nulle, troisième, lorsque date 2 est nulle, quasortingème, min (date1, date2)
Donc, pour ce cas, la solution peut être atteinte en utilisant la même approche, comme suit:
SELECT ID, Date1, Date2 FROM YourTable ORDER BY CASE WHEN Date1 IS NULL AND Date2 IS NULL THEN 0 WHEN Date1 IS NULL THEN 1 WHEN Date2 IS NULL THEN 2 ELSE 3 END, (SELECT MIN(v) FROM (VALUES ([Date1]), ([Date2])) AS value(v))
La sortie pour ce code est ci-dessous:
La solution COALESCE
ne COALESCE
pas la table de cette façon. Il salit les lignes où au less une cellule de la valeur NULL
. La sortie de celui-ci est la suivante:
J'espère que cela aide et attend les critiques.
Cela peut être une solution alternative qui ne nécessite pas de twigment comme CASE WHEN
. Ceci est basé sur la formule max(a,b)=1/2(a+b+|a−b|)
comme décrit ici . Nous obtenons les valeurs absolues de a et b en utilisant DATEDIFF
avec une date de reference ( '1773-01-01'
).
ORDER BY (DATEDIFF(d,'17730101' ,isnull(Startdate,enddate)) + DATEDIFF(d,'17730101' ,isnull(EndDate,Startdate)) - ABS(DATEDIFF(d,isnull(Startdate,enddate),isnull(EndDate,Startdate))))
Données de test
Create Table #DateData(ID int Identity, Name varchar(15),Startdate datetime,EndDate DateTime) Insert Into #DateData(Name,Startdate,EndDate) values ('myName','2015-04-17 18:48:27','2015-04-18 18:48:27') Insert Into #DateData(Name,Startdate,EndDate) values ('myName','2015-04-19 18:48:27','2015-04-18 18:48:27') Insert Into #DateData(Name,Startdate,EndDate) values ('myName','2015-04-20 18:48:27','2015-04-18 18:48:27') Insert Into #DateData(Name,Startdate,EndDate) values ('myName','2015-04-11 18:48:27','2015-04-22 18:48:27') Insert Into #DateData(Name,Startdate,EndDate) values ('myName','2015-05-09 18:48:27','2015-04-18 18:48:27') Insert Into #DateData(Name,Startdate,EndDate) values ('myName','2015-04-17 19:07:38','2015-04-17 18:55:38') Insert Into #DateData(Name,Startdate,EndDate) values ('myName','2015-04-17 19:07:38','2015-05-12 18:56:29')
Terminer la requête
select * from #DateData order by (DATEDIFF(d,'17730101' ,isnull(Startdate,enddate)) + DATEDIFF(d,'17730101' ,isnull(EndDate,Startdate)) - ABS(DATEDIFF(d,isnull(Startdate,enddate),isnull(EndDate,Startdate))))
Si vous ne souhaitez pas utiliser l' Case statement
dans la zone Order By
, il s'agit d'une autre approche, en déplaçant simplement l' Case statement
sur Select
SELECT Id, Date1, Date2 FROM (SELECT Id, Date1, Date2 ,CASE WHEN Date1 < Date2 THEN Date1 ELSE Date2 END as MinDate FROM YourTable) as T ORDER BY MinDate
Je préfère cette façon de gérer les colonnes nullables:
SELECT Id, Date1, Date2 FROM YourTable ORDER BY CASE WHEN Date1 < Date2 OR Date1 IS NULL THEN Date1 ELSE Date2 END
Code pour max
J'utilise CROSS APPLY
, je ne suis pas sûr de la performance, mais CROSS APPLY
souvent une meilleure performance dans mon expérience.
CREATE TABLE #Test (ID INT, Date1 DATETIME, Date2 DATETIME) INSERT INTO #Test SELECT 1, NULL, '1/1/1';INSERT INTO #Test SELECT 2, NULL, NULL;INSERT INTO #Test SELECT 3, '2/2/2', '3/3/1';INSERT INTO #Test SELECT 4, '3/3/3', '11/1/1' SELECT t.ID, Date1, Date2, MinDate FROM #TEST t CROSS APPLY (SELECT MIN(d) MinDate FROM (VALUES (Date1), (Date2)) AS a(d)) md ORDER BY MinDate DROP TABLE #Test
Je déplacerais le focus de la façon de faire ceci à pourquoi vous avez besoin de ceci – et proposerais de changer le schéma à la place. La règle générale est la suivante: si vous avez besoin d'effectuer des cascades pour accéder à vos données, la décision de design est mauvaise.
Comme vous l'avez vu, cette tâche est très inhabituelle pour SQL, même si c'est possible, toutes les methods proposées sont douloureusement lentes par rapport à un ORDER BY
ordinaire.
Je pense que quand vous voulez sortinger sur les deux champs de date1
et date2
, vous devriez avoir les deux dans la partie ORDER BY
, comme ceci:
SELECT * FROM aTable ORDER BY CASE WHEN date1 < date2 THEN date1 ELSE date2 END, CASE WHEN date1 < date2 THEN date2 ELSE date1 END
Le résultat peut être comme ceci:
date1 | date2 -----------+------------ 2015-04-25 | 2015-04-21 2015-04-26 | 2015-04-21 2015-04-25 | 2015-04-22 2015-04-22 | 2015-04-26
Pour avoir un résultat de préfet avec des valeurs Null
, utilisez:
SELECT * FROM aTable ORDER BY CASE WHEN date1 IS NULL THEN NULL WHEN date1 < date2 THEN date1 ELSE date2 END ,CASE WHEN date2 IS NULL THEN date1 WHEN date1 IS NULL THEN date2 WHEN date1 < date2 THEN date2 ELSE date1 END
Les résultats seront comme ceci:
date1 | date2 -----------+------------ NULL | NULL NULL | 2015-04-22 2015-04-26 | NULL 2015-04-25 | 2015-04-21 2015-04-26 | 2015-04-21 2015-04-25 | 2015-04-22
Il y a une autre option. Vous pouvez calculer la colonne résultat par la logique nécessaire et couvrir la sélection par une externe avec la command par votre colonne. Dans ce cas, le code sera le suivant:
select ID, x.Date1, x.Date2 from ( select ID, Date1, Date2, SortColumn = case when Date1 < Date2 then Date1 else Date2 end from YourTable ) x order by x.SortColumn
L'avantage de cette solution est que vous pouvez append des requêtes de filtrage nécessaires (dans la sélection interne) et que les index seront toujours utiles.
Je orderais les lignes par ces règles:
Pour ce faire un cas nested sera simple et efficace (sauf si la table est très grande) selon ce post .
SELECT ID, Date1, Date2 FROM YourTable ORDER BY CASE WHEN Date1 IS NULL AND Date2 IS NULL THEN 0 WHEN Date1 IS NULL THEN 1 WHEN Date2 IS NULL THEN 2 ELSE 3 END, CASE WHEN Date1 < Date2 THEN Date1 ELSE Date2 END
Vous pouvez utiliser la fonction min
dans order by
clause:
select * from [table] d order by ( select min(qt) from ( select d.date1 t union select d.date2) q )
Vous pouvez aussi utiliser case
statement dans order by
clause mais comme vous savez le résultat de la comparaison ( >
et <
) n'importe quelle valeur (null ou none null) avec null n'est pas true
même si vous avez mis ansi_nulls
à off
. donc pour garantir le type que vous vouliez, vous devez gérer null
, comme vous le savez dans la clause case
si le résultat d'un when
est true
puis plus loin when
instructions ne sont pas évaluées, vous pouvez dire:
select * from [table] order by case when date1 is null then date2 when date2 is null then date1 when date1<date2 then date1 -- surely date1 and date2 are not null here else date2 end
Voici également quelques autres solutions si votre scénario est différent, peut-être que vous évaluez le résultat de la comparaison de plusieurs colonnes (ou d'un calcul) dans un champ séparé et enfin l'ordre par ce champ calculé sans utiliser de clause dans la clause order by.
SELECT ID, Date1, Date2 FROM YourTable ORDER BY (SELECT TOP(1) v FROM (VALUES (Date1), (Date2)) AS value(v) ORDER BY v)
Très similaire à la réponse @dyatchenko mais sans problème NULL