SQL MAX из нескольких столбцов? - PullRequest
       59

SQL MAX из нескольких столбцов?

329 голосов
/ 16 сентября 2008

Как вы возвращаете 1 значение в строке максимум из нескольких столбцов:

TableName

[Number, Date1, Date2, Date3, Cost]

Мне нужно вернуть что-то вроде этого:

[Number, Most_Recent_Date, Cost]

Запрос

Ответы [ 21 ]

771 голосов
/ 29 июля 2011

Вот еще одно хорошее решение для функциональности Max с использованием T-SQL и SQL Server

SELECT [Other Fields],
  (SELECT Max(v) 
   FROM (VALUES (date1), (date2), (date3),...) AS value(v)) as [MaxDate]
FROM [YourTableName]
149 голосов
/ 16 сентября 2008

Ну, вы можете использовать оператор 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

[Для Microsoft SQL Server 2008 и выше вы можете рассмотреть более простой ответ Свена ниже.]

118 голосов
/ 01 декабря 2008

Если вы используете MySQL, вы можете использовать

SELECT GREATEST(col1, col2 ...) FROM table
59 голосов
/ 09 сентября 2009

Есть еще 3 метода, где UNPIVOT (1) - самый быстрый на сегодняшний день, за которым следует Simulated Unpivot (3), который намного медленнее, чем (1), но все же быстрее, чем (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

Решение 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

Решение 2 (подзапрос на строку)

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

Решение 3 (Имитация 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
16 голосов
/ 16 сентября 2008

Любой из двух примеров ниже будет работать:

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

Второй является дополнением к ответу lassevk .

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 
9 голосов
/ 07 февраля 2011

Скалярная функция вызывает всевозможные проблемы с производительностью, поэтому лучше по возможности заключить логику во встроенную табличную функцию. Это функция, которую я использовал для замены некоторых пользовательских функций, которые выбирали минимальные / максимальные даты из списка до десяти дат. При тестировании на моем наборе данных из 1 миллиона строк скалярной функции потребовалось более 15 минут, прежде чем я завершил запрос, и для встроенного TVF потребовалась 1 минута, то есть столько же времени, сколько и для выбора набора результатов во временной таблице. Чтобы использовать этот вызов функции из подзапроса в SELECT или 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)
)
8 голосов
/ 01 апреля 2015

Для T-SQL (MSSQL 2008 +)

SELECT
  (SELECT
     MAX(MyMaxName) 
   FROM ( VALUES 
            (MAX(Field1)), 
            (MAX(Field2)) 
        ) MyAlias(MyMaxName)
  ) 
FROM MyTable1
8 голосов
/ 30 ноября 2010
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
5 голосов
/ 30 ноября 2010
SELECT 
    CASE 
        WHEN Date1 >= Date2 AND Date1 >= Date3 THEN Date1 
        WHEN Date2 >= Date3 THEN Date2 
        ELSE Date3
    END AS MostRecentDate 

Это немного легче записать и пропускает этапы оценки, так как оператор case оценивается по порядку.

4 голосов
/ 23 января 2012

К сожалению Ответ Лассе , хотя и кажется очевидным, имеет решающий недостаток. Он не может обрабатывать значения NULL. Любое единственное значение NULL приводит к возвращению Date1. К сожалению, любая попытка решить эту проблему имеет тенденцию быть чрезвычайно запутанной и не очень хорошо масштабируется до 4 или более значений.

Первый ответ databyss выглядел (и есть) хорошо. Однако было неясно, будет ли ответ легко экстраполироваться на 3 значения из объединения нескольких таблиц вместо более простых 3 значений из одной таблицы. Я хотел избежать превращения такого запроса в подзапрос, чтобы получить максимум 3 столбца, а также был уверен, что отличную идею databyss можно немного исправить.

Итак, без лишних слов, вот мое решение (полученное из идеи databyss).
Он использует перекрестные объединения, выбирая константы для имитации эффекта объединения нескольких таблиц. Важно отметить, что все необходимые псевдонимы проходят правильно (что не всегда так), и это делает шаблон довольно простым и достаточно масштабируемым с помощью дополнительных столбцов.

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...