web-dev-qa-db-fra.com

SQL MAX de plusieurs colonnes?

Comment renvoyer 1 valeur par ligne sur le maximum de plusieurs colonnes:

Nom de la table

[Number, Date1, Date2, Date3, Cost]

J'ai besoin de retourner quelque chose comme ça:

[Number, Most_Recent_Date, Cost]

Question?

317
BenB

Eh bien, vous pouvez utiliser l'instruction CASE:

SELECT
    CASE
        WHEN Date1 >= Date2 AND Date1 >= Date3 THEN Date1
        WHEN Date2 >= Date1 AND Date2 >= Date3 THEN Date2
        WHEN Date3 >= Date1 AND Date3 >= Date2 THEN Date3
        ELSE                                        Date1
    END AS MostRecentDate

[Pour Microsoft SQL Server 2008 et les versions ultérieures, vous pouvez considérer la réponse plus simple de Sven ci-dessous.]

140

Voici une autre solution intéressante pour la fonctionnalité Max utilisant T-SQL et SQL Server

SELECT [Other Fields],
  (SELECT Max(v) 
   FROM (VALUES (date1), (date2), (date3),...) AS value(v)) as [MaxDate]
FROM [YourTableName]
748
Sven

Si vous utilisez MySQL, vous pouvez utiliser

SELECT GREATEST(col1, col2 ...) FROM table
111
bajafresh4life

Il existe 3 méthodes supplémentaires pour lesquelles UNPIVOT (1) est de loin le plus rapide, suivi de Simulated Unpivot (3), beaucoup plus lent que (1) mais toujours plus rapide que (2)

CREATE TABLE dates
    (
      number INT PRIMARY KEY ,
      date1 DATETIME ,
      date2 DATETIME ,
      date3 DATETIME ,
      cost INT
    )

INSERT  INTO dates
VALUES  ( 1, '1/1/2008', '2/4/2008', '3/1/2008', 10 )
INSERT  INTO dates
VALUES  ( 2, '1/2/2008', '2/3/2008', '3/3/2008', 20 )
INSERT  INTO dates
VALUES  ( 3, '1/3/2008', '2/2/2008', '3/2/2008', 30 )
INSERT  INTO dates
VALUES  ( 4, '1/4/2008', '2/1/2008', '3/4/2008', 40 )
GO

Solution 1 (UNPIVOT)

SELECT  number ,
        MAX(dDate) maxDate ,
        cost
FROM    dates UNPIVOT ( dDate FOR nDate IN ( Date1, Date2,
                                            Date3 ) ) as u
GROUP BY number ,
        cost 
GO

Solution 2 (sous-requête par ligne)

SELECT  number ,
        ( SELECT    MAX(dDate) maxDate
          FROM      ( SELECT    d.date1 AS dDate
                      UNION
                      SELECT    d.date2
                      UNION
                      SELECT    d.date3
                    ) a
        ) MaxDate ,
        Cost
FROM    dates d
GO

Solution 3 (simulée UNPIVOT)

;WITH    maxD
          AS ( SELECT   number ,
                        MAX(CASE rn
                              WHEN 1 THEN Date1
                              WHEN 2 THEN date2
                              ELSE date3
                            END) AS maxDate
               FROM     dates a
                        CROSS JOIN ( SELECT 1 AS rn
                                     UNION
                                     SELECT 2
                                     UNION
                                     SELECT 3
                                   ) b
               GROUP BY Number
             )
    SELECT  dates.number ,
            maxD.maxDate ,
            dates.cost
    FROM    dates
            INNER JOIN MaxD ON dates.number = maxD.number
GO

DROP TABLE dates
GO
57
Niikola

L'un ou l'autre des deux échantillons ci-dessous fonctionnera:

SELECT  MAX(date_columns) AS max_date
FROM    ( (SELECT   date1 AS date_columns
           FROM     data_table         )
          UNION
          ( SELECT  date2 AS date_columns
            FROM    data_table
          )
          UNION
          ( SELECT  date3 AS date_columns
            FROM    data_table
          )
        ) AS date_query

La seconde est un ajout à lassevk's answer.

SELECT  MAX(MostRecentDate)
FROM    ( SELECT    CASE WHEN date1 >= date2
                              AND date1 >= date3 THEN date1
                         WHEN date2 >= date1
                              AND date2 >= date3 THEN date2
                         WHEN date3 >= date1
                              AND date3 >= date2 THEN date3
                         ELSE date1
                    END AS MostRecentDate
          FROM      data_table
        ) AS date_query 
16
databyss

Les fonctions scalaires entraînent toutes sortes de problèmes de performances. Il est donc préférable de regrouper la logique dans une fonction à valeurs de table en ligne, si possible. C'est la fonction que j'ai utilisée pour remplacer certaines fonctions définies par l'utilisateur qui ont sélectionné les dates Min/Max dans une liste de dix dates au maximum. Une fois testée sur un ensemble de données de 1 million de lignes, la fonction Scalar a pris plus de 15 minutes avant de supprimer la requête. La TVF en ligne a pris 1 minute, soit le même temps que la sélection du jeu de résultats dans une table temporaire. Pour utiliser cet appel, appelez la fonction à partir d’une sous-requête du SELECT ou d’un CROSS APPLY.

CREATE FUNCTION dbo.Get_Min_Max_Date
(
    @Date1  datetime,
    @Date2  datetime,
    @Date3  datetime,
    @Date4  datetime,
    @Date5  datetime,
    @Date6  datetime,
    @Date7  datetime,
    @Date8  datetime,
    @Date9  datetime,
    @Date10 datetime
)
RETURNS TABLE
AS
RETURN
(
    SELECT      Max(DateValue)  Max_Date,
                Min(DateValue)  Min_Date
    FROM        (
                    VALUES  (@Date1),
                            (@Date2),
                            (@Date3),
                            (@Date4),
                            (@Date5),
                            (@Date6),
                            (@Date7),
                            (@Date8),
                            (@Date9),
                            (@Date10)
                )   AS Dates(DateValue)
)
9
MartinC
DECLARE @TableName TABLE (Number INT, Date1 DATETIME, Date2 DATETIME, Date3 DATETIME, Cost MONEY)

INSERT INTO @TableName 
SELECT 1, '20000101', '20010101','20020101',100 UNION ALL
SELECT 2, '20000101', '19900101','19980101',99 

SELECT Number,
       Cost  ,
       (SELECT MAX([Date])
       FROM    (SELECT Date1 AS [Date]
               UNION ALL
               SELECT Date2
               UNION ALL
               SELECT Date3
               )
               D
       )
       [Most Recent Date]
FROM   @TableName
8
Martin Smith

Pour T-SQL (MSSQL 2008+)

SELECT
  (SELECT
     MAX(MyMaxName) 
   FROM ( VALUES 
            (MAX(iSortCode)), 
            (MAX(Field2)) 
        ) MyAlias(MyMaxName)
  ) 
FROM MyTable1
6
doker
SELECT 
    CASE 
        WHEN Date1 >= Date2 AND Date1 >= Date3 THEN Date1 
        WHEN Date2 >= Date3 THEN Date2 
        ELSE Date3
    END AS MostRecentDate 

Il est légèrement plus facile à écrire et ignore les étapes d’évaluation car l’instruction de cas est évaluée dans l’ordre.

5
Nat

Malheureusement réponse de Lasse , bien qu'apparemment évident, il comporte un défaut crucial. Il ne peut pas gérer les valeurs NULL. Toute valeur NULL unique entraîne le renvoi de Date1. Malheureusement, toute tentative de résolution de ce problème a tendance à devenir extrêmement compliquée et ne permet pas d'atteindre 4 valeurs ou plus.

La première réponse de databyss était (et est) bonne. Cependant, il n'était pas clair si la réponse pourrait facilement être extrapolée à 3 valeurs à partir d'une jointure multi-tables au lieu des 3 valeurs plus simples d'une seule table. Je voulais éviter de transformer une telle requête en une sous-requête uniquement pour obtenir le maximum de 3 colonnes. J'étais également persuadé que l'excellente idée de databyss pourrait être un peu épurée.

Alors sans plus tarder, voici ma solution (dérivée de l’idée de databyss).
Il utilise des constantes pour sélectionner les constantes afin de simuler l’effet d’une jointure multi-tables. Il est important de noter que tous les alias nécessaires sont correctement gérés (ce qui n'est pas toujours le cas), ce qui permet de garder le modèle assez simple et assez évolutif au moyen de colonnes supplémentaires.

DECLARE @v1 INT ,
        @v2 INT ,
        @v3 INT
--SET @v1 = 1 --Comment out SET statements to experiment with 
              --various combinations of NULL values
SET @v2 = 2
SET @v3 = 3

SELECT  ( SELECT    MAX(Vals)
          FROM      ( SELECT    v1 AS Vals
                      UNION
                      SELECT    v2
                      UNION
                      SELECT    v3
                    ) tmp
          WHERE     Vals IS NOT NULL -- This eliminates NULL warning

        ) AS MaxVal
FROM    ( SELECT    @v1 AS v1
        ) t1
        CROSS JOIN ( SELECT @v2 AS v2
                   ) t2
        CROSS JOIN ( SELECT @v3 AS v3
                   ) t3
4
Disillusioned

Problème: choisissez la valeur de taux minimale attribuée à une entité.

[MinRateValue] = 
CASE 
   WHEN ISNULL(FitchRating.RatingValue, 100) < = ISNULL(MoodyRating.RatingValue, 99) 
   AND  ISNULL(FitchRating.RatingValue, 100) < = ISNULL(StandardPoorsRating.RatingValue, 99) 
   THEN FitchgAgency.RatingAgencyName

   WHEN ISNULL(MoodyRating.RatingValue, 100) < = ISNULL(StandardPoorsRating.RatingValue , 99)
   THEN MoodyAgency.RatingAgencyName

   ELSE ISNULL(StandardPoorsRating.RatingValue, 'N/A') 
END 

Inspiré par cette réponse de Nat

4
Luis Miguel Rosa

En utilisant CROSS APPLY (pour 2005+) ....

SELECT MostRecentDate 
FROM SourceTable
    CROSS APPLY (SELECT MAX(d) MostRecentDate FROM (VALUES (Date1), (Date2), (Date3)) AS a(d)) md
3
EarlOfEnnui

Depuis SQL Server 2012, nous pouvons utiliser IIF .

 DECLARE @Date1 DATE='2014-07-03';
 DECLARE @Date2 DATE='2014-07-04';
 DECLARE @Date3 DATE='2014-07-05';

 SELECT IIF(@Date1>@Date2,
        IIF(@Date1>@Date3,@Date1,@Date3),
        IIF(@Date2>@Date3,@Date2,@Date3)) AS MostRecentDate
3
abdulbasit

Si vous utilisez SQL Server 2005, vous pouvez utiliser la fonctionnalité UNPIVOT. Voici un exemple complet:

create table dates 
(
  number int,
  date1 datetime,
  date2 datetime,
  date3 datetime 
)

insert into dates values (1, '1/1/2008', '2/4/2008', '3/1/2008')
insert into dates values (1, '1/2/2008', '2/3/2008', '3/3/2008')
insert into dates values (1, '1/3/2008', '2/2/2008', '3/2/2008')
insert into dates values (1, '1/4/2008', '2/1/2008', '3/4/2008')

select max(dateMaxes)
from (
  select 
    (select max(date1) from dates) date1max, 
    (select max(date2) from dates) date2max,
    (select max(date3) from dates) date3max
) myTable
unpivot (dateMaxes For fieldName In (date1max, date2max, date3max)) as tblPivot

drop table dates
3
Lance Fisher

Veuillez essayer d'utiliser UNPIVOT:

SELECT MAX(MaxDt) MaxDt
   FROM tbl 
UNPIVOT
   (MaxDt FOR E IN 
      (Date1, Date2, Date3)
)AS unpvt;
1
TechDo

J'ai une table appelée TblItem si je veux obtenir le plus gros ID. J'utilise le code suivant.

select MAX(Id) from TblItem
0
Diako Hasani

Je préfère les solutions basées sur des cas où, mon hypothèse est que cela devrait avoir le moins d'impact sur la baisse de performance possible par rapport à d'autres solutions possibles comme celles avec application croisée, valeurs (), fonctions personnalisées etc.

Voici la version cas-quand qui gère les valeurs NULL avec la plupart des cas de tests possibles:

SELECT
    CASE 
        WHEN Date1 > coalesce(Date2,'0001-01-01') AND Date1 > coalesce(Date3,'0001-01-01') THEN Date1 
        WHEN Date2 > coalesce(Date3,'0001-01-01') THEN Date2 
        ELSE Date3
    END AS MostRecentDate
    , *
from 
(values
     (  1, cast('2001-01-01' as Date), cast('2002-01-01' as Date), cast('2003-01-01' as Date))
    ,(  2, cast('2001-01-01' as Date), cast('2003-01-01' as Date), cast('2002-01-01' as Date))
    ,(  3, cast('2002-01-01' as Date), cast('2001-01-01' as Date), cast('2003-01-01' as Date))
    ,(  4, cast('2002-01-01' as Date), cast('2003-01-01' as Date), cast('2001-01-01' as Date))
    ,(  5, cast('2003-01-01' as Date), cast('2001-01-01' as Date), cast('2002-01-01' as Date))
    ,(  6, cast('2003-01-01' as Date), cast('2002-01-01' as Date), cast('2001-01-01' as Date))
    ,( 11, cast(NULL         as Date), cast('2002-01-01' as Date), cast('2003-01-01' as Date))
    ,( 12, cast(NULL         as Date), cast('2003-01-01' as Date), cast('2002-01-01' as Date))
    ,( 13, cast('2003-01-01' as Date), cast(NULL         as Date), cast('2002-01-01' as Date))
    ,( 14, cast('2002-01-01' as Date), cast(NULL         as Date), cast('2003-01-01' as Date))
    ,( 15, cast('2003-01-01' as Date), cast('2002-01-01' as Date), cast(NULL         as Date))
    ,( 16, cast('2002-01-01' as Date), cast('2003-01-01' as Date), cast(NULL         as Date))
    ,( 21, cast('2003-01-01' as Date), cast(NULL         as Date), cast(NULL         as Date))
    ,( 22, cast(NULL         as Date), cast('2003-01-01' as Date), cast(NULL         as Date))
    ,( 23, cast(NULL         as Date), cast(NULL         as Date), cast('2003-01-01' as Date))
    ,( 31, cast(NULL         as Date), cast(NULL         as Date), cast(NULL         as Date))

) as demoValues(id, Date1,Date2,Date3)
order by id
;

et le résultat est:

MostRecent    id   Date1      Date2      Date3
2003-01-01    1    2001-01-01 2002-01-01 2003-01-01
2003-01-01    2    2001-01-01 2003-01-01 2002-01-01
2003-01-01    3    2002-01-01 2001-01-01 2002-01-01
2003-01-01    4    2002-01-01 2003-01-01 2001-01-01
2003-01-01    5    2003-01-01 2001-01-01 2002-01-01
2003-01-01    6    2003-01-01 2002-01-01 2001-01-01
2003-01-01    11   NULL       2002-01-01 2003-01-01
2003-01-01    12   NULL       2003-01-01 2002-01-01
2003-01-01    13   2003-01-01 NULL       2002-01-01
2003-01-01    14   2002-01-01 NULL       2003-01-01
2003-01-01    15   2003-01-01 2002-01-01 NULL
2003-01-01    16   2002-01-01 2003-01-01 NULL
2003-01-01    21   2003-01-01 NULL       NULL
2003-01-01    22   NULL       2003-01-01 NULL
2003-01-01    23   NULL       NULL       2003-01-01
NULL          31   NULL       NULL       NULL
0
Robert Lujo

Une autre façon d'utiliser CASE WHEN  

SELECT CASE true 
       WHEN max(row1) >= max(row2) THEN CASE true WHEN max(row1) >= max(row3) THEN max(row1) ELSE max(row3) end ELSE
       CASE true WHEN max(row2) >= max(row3) THEN max(row2) ELSE max(row3) END END
FROM yourTable
0
M.A.Bell

Basé sur la solution de ScottPletcher de http://www.experts-exchange.com/Microsoft/Development/MS-SQL-Server/Q_24204894.html .__, j'ai créé un ensemble de fonctions (par exemple, GetMaxOfDates3, GetMaxOfDates13) pour trouver un maximum de 13 valeurs de date à l'aide de UNION ALL . Voir Fonction T-SQL pour obtenir le maximum de valeurs de la même ligne solution envisagée par UNPIVOT au moment de la rédaction de ces fonctions 

0
Michael Freidgeim

Vous pouvez créer une fonction dans laquelle vous transmettez les dates, puis vous l'ajoutez à l'instruction select, comme ci-dessous . Numéro, dbo.fxMost_Recent_Date (Date1, Date2, Date3), Coût

create FUNCTION  fxMost_Recent_Date 

( @ Date1 smalldatetime, @ Date2 smalldatetime, @ Date3 smalldatetime ) RETURNS smalldatetime AS BEGIN DECLARE @Result smalldatetime

declare @MostRecent smalldatetime

set @MostRecent='1/1/1900'

if @Date1>@MostRecent begin set @MostRecent=@Date1 end
if @Date2>@MostRecent begin set @MostRecent=@Date2 end
if @Date3>@MostRecent begin set @MostRecent=@Date3 end
RETURN @MostRecent

FIN

0
DrYodo