455 votes

SQL MAX de plusieurs colonnes ?

Comment retourner 1 valeur par ligne du maximum de plusieurs colonnes :

Nom de la table

[Number, Date1, Date2, Date3, Cost]

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

[Number, Most_Recent_Date, Cost]

Une requête ?

970voto

Sven Points 2176

Voici une autre solution intéressante pour le Max à l'aide de T-SQL et de SQL Server

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

Les valeurs sont les suivantes Constructeur de valeur de tableau .

"Spécifie un ensemble d'expressions de valeur de ligne à construire dans une table. Le constructeur de valeurs de table Transact-SQL permet de spécifier plusieurs lignes de données dans une seule instruction DML. Le constructeur de valeurs de table peut être spécifié soit comme clause VALUES d'une instruction INSERT ... VALUES, soit comme table dérivée dans la clause USING de l'instruction MERGE ou la clause FROM."

51 votes

La version de SQL doit être >= 2008.

11 votes

Cela fonctionne très bien avec 2008 et gère les NULLs. Très bonne solution.

1 votes

@SteveC, @Sven - Merci beaucoup. Mais je pensais que VALUES est utilisé uniquement avec Insert déclaration. Et qu'est-ce que value(v) - pourquoi je ne peux pas simplement écrire v ?

189voto

Lasse V. Karlsen Points 148037

Il s'agit d'une ancienne réponse, qui ne fonctionne pas à tous les égards.

Ver https://stackoverflow.com/a/6871572/194653 qui a beaucoup plus de votes positifs et qui fonctionne avec sql server 2008+ et gère les valeurs nulles, etc.

Réponse originale mais problématique :

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

12 votes

Ne suffirait-il pas d'utiliser WHEN Date1 > Date2 AND Date1 > Date3 THEN Date1; WHEN Date2 > Date3 THEN Date3; ELSE Date3 ?

23 votes

C'est la réponse la plus évidente, mais elle ne fonctionne pas avec les valeurs NULL, et tenter de résoudre ce problème devient très compliqué.

8 votes

Je fais un retour en arrière sur cet ancien message, mais vous pourriez intégrer chaque date dans un COALESCE pour gérer les NULL. L'une de ces instructions WHEN ressemblerait alors à ceci : WHEN Date1 >= COALESCE(Date2,'') AND Date1 >= COALESCE(Date3,'') THEN Date3 (faites de même pour les autres when)

189voto

bajafresh4life Points 6392

Si vous utilisez MySQL, vous pouvez utiliser

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

117 votes

C'est vrai, mais c'est quand même une réponse très utile car les gens trouvent cette question en référence à MySQL.

13 votes

Également disponible en PostgreSQL à partir de 8.1 .

6 votes

Il ne gère pas bien les NULL, mais si vous coalescez(col1, 0) autour des valeurs de vos colonnes, vous aurez du gaz voir cette réponse stackoverflow.com/questions/9831851/

68voto

Niikola Points 702

Il existe 3 autres méthodes où UNPIVOT (1) est de loin le plus rapide, suivi par le dépassement simulé (3) qui est 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

1 votes

Sympa. Je ne connaissais pas les opérateurs PIVOT et UNPIVOT.

0 votes

Savez-vous quelles sont les versions de SQL Server qui prennent en charge le pivotage/dépivrage ?

1 votes

@CraigYoung SQL Server 2005 avec COMPATIBILITY_LEVEL fixé à 90.

18voto

databyss Points 1673

L'un ou l'autre des deux exemples 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

Le second est un complément à de lassevk réponse.

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

0 votes

La première réponse est bonne, mais peut être considérablement simplifiée. La deuxième réponse ne fonctionne pas avec les valeurs NULL. Tenter de résoudre ce problème devient très compliqué.

1 votes

Vous devriez utiliser UNION ALL et non UNION pour éviter une opération implicite DISTINCT inutile.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X