Trier par la valeur minimale de deux colonnes

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:

  • Cela ne fonctionnera pas si les colonnes sont du type Date (si vous utilisez les dates antérieures au 01/01/1753 )
  • Cela ne fonctionnera pas dans le cas où l'une des colonnes est 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.
  • L'expression 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:

  • Il ne faut pas beaucoup de encoding pour l'implémenter – il suffit d'append une ligne supplémentaire.
  • Vous n'avez pas besoin de vous soucier de savoir si les colonnes sont nullables ou non. Vous utilisez simplement le code et cela fonctionne.
  • Vous pouvez étendre le nombre de colonnes dans votre requête simplement en ajoutant celui après une virgule.
  • Cela fonctionne avec les colonnes 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:

Le résultat de sortie pour le mode de commande de * Zohar

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:

Bizarre ORDER BY de la solution <code> COALESCE </ code>

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.

  • Si vous devez le faire souvent, le minimum des deux dates doit avoir une signification physique indépendante pour votre application.
    • Ce qui justifie une colonne séparée (ou peut-être une colonne remplaçant l'un des deux) – maintenue par un triggersur ou même manuellement si la signification est suffisamment indépendante pour que la colonne ne soit éventuellement ni dans certains cas.

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:

  1. quand les deux sont nuls
  2. lorsque date1 est null
  3. lorsque la date 2 est nulle
  4. min (date1, date2)

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