Как вернуть 1 значение на строку из нескольких столбцов:
TableName
[Number, Date1, Date2, Date3, Cost]
Мне нужно вернуть что-то вроде этого:
[Number, Most_Recent_Date, Cost]
Запрос?


Что ж, вы можете использовать оператор 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 г. и выше вы можете рассмотреть более простой ответ Свена ниже.]
Очевидный ответ, но он не работает со значениями NULL, и попытки исправить это очень беспорядочно.
Necro'ing этот старый пост, но вы можете обернуть каждую дату в COALESCE для обработки NULL. Тогда один из этих операторов WHEN будет выглядеть так: WHEN Date1> = COALESCE (Date2, '') AND Date1> = COALESCE (Date3, '') THEN Date3 (сделайте то же самое для другого when)
для тех, кто пришел сюда в поисках пути к MySQL, взгляните на ответ @ bajafresh4life: stackoverflow.com/a/331873/1412157
Кстати, он возвращает Date1, когда Date2 имеет значение null, даже если Date3> Date1.
Подойдет любой из двух примеров ниже:
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
Второй - дополнение к ответу лассевка.
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
Первый ответ хорош, но его можно значительно упростить. Второй ответ не работает со значениями NULL. Попытки решить эту проблему становятся очень беспорядочными.
Вы должны использовать UNION ALL, а не UNION, чтобы избежать ненужной подразумеваемой операции DISTINCT.
Если вы используете MySQL, вы можете использовать
SELECT GREATEST(col1, col2 ...) FROM table
Верно, но все же очень полезный ответ, поскольку люди находят этот вопрос в отношении MySQL.
Также доступно в PostgreSQL из 8.1.
Не справляется с NULL, но если вы объедините (col1, 0) вокруг значений столбца, вы будете готовить на газе, см. Этот ответ stackoverflow.com/questions/9831851/…
А как насчет этого решения: stackoverflow.com/a/2166693/4824854
Я просто использовал это в TOAD для Oracle SQL, и это сработало.
Если вы используете SQL Server 2005, вы можете использовать функцию UNPIVOT. Вот полный пример:
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
Думаю, мне больше нравится пример UNION.
"Как вернуть ОДНО ЗНАЧЕНИЕ НА СТРОК из максимального количества нескольких столбцов"
Есть еще 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
UNPIVOT)SELECT number ,
MAX(dDate) maxDate ,
cost
FROM dates UNPIVOT ( dDate FOR nDate IN ( Date1, Date2,
Date3 ) ) as u
GROUP BY number ,
cost
GO
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
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
Отлично. Я не знал об операторах PIVOT и UNPIVOT.
Есть идеи, какие версии SQL Server поддерживают поворот / отключение?
@CraigYoung SQL Server 2005 с COMPATIBILITY_LEVEL равным 90.
SELECT
CASE
WHEN Date1 >= Date2 AND Date1 >= Date3 THEN Date1
WHEN Date2 >= Date3 THEN Date2
ELSE Date3
END AS MostRecentDate
Это немного легче записать, и шаги оценки пропускаются, поскольку оператор case оценивается по порядку.
Осторожный. Если Date2 имеет значение NULL, ответ будет Date3; даже если Date1 больше.
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
Работал в любой версии SQL для меня, хорошее решение
Вы можете создать функцию, в которой вы передаете даты, а затем добавляете функцию в оператор выбора, как показано ниже. выберите Number, dbo.fxMost_Recent_Date (Date1, Date2, Date3), Cost
create FUNCTION fxMost_Recent_Date
( @ Date1 smalldatetime, @ Date2 smalldatetime, @ Date3 smalldatetime ) ВОЗВРАЩАЕТСЯ smalldatetime В КАЧЕСТВЕ НАЧИНАТЬ ОБЪЯВИТЬ @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
КОНЕЦ
Скалярная функция вызывает всевозможные проблемы с производительностью, поэтому по возможности лучше обернуть логику во встроенную табличную функцию. Это функция, которую я использовал для замены некоторых пользовательских функций, которые выбирали минимальные / максимальные даты из списка до десяти дат. При тестировании на моем наборе данных из 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)
)
Не могу поверить, что спустя 10 лет вы получили всего 9 баллов (плюс мой) за этот ответ. Люди, кажется, не осознают эффективность, ясность, удобочитаемость и т. д. Молодцы!
На основе решения СкоттПлетчер от http://www.experts-exchange.com/Microsoft/Development/MS-SQL-Server/Q_24204894.html Я создал набор функций (например, GetMaxOfDates3, GetMaxOfDates13), чтобы найти максимум до 13 значений даты с помощью UNION ALL. См. Функция T-SQL для получения максимума значений из одной строки Однако я не рассматривал решение UNPIVOT на момент написания этих функций.
Вот еще одно хорошее решение для функциональности Max с использованием T-SQL и SQL Server.
SELECT [Other Fields],
(SELECT Max(v)
FROM (VALUES (date1), (date2), (date3),...) AS value(v)) as [MaxDate]
FROM [YourTableName]
Версия SQL должна быть> = 2008.
Это очень хорошо работает с 2008 и обрабатывает NULL. Очень красивое решение.
@SteveC, @Sven - Большое спасибо. Но я думал, что VALUES используется только с заявлением Insert. А что такое value(v) - почему я просто не могу написать v?
@Cheburek: Из значения (v) «значение» - это псевдоним виртуальной таблицы, а «v» - это имя виртуального столбца значений даты.
Это великолепно. Где я могу найти документацию для этой виртуальной таблицы Value ()?
Я тоже изначально не понимал VALUE (v). Если вы хотите понять VALUE, попробуйте этот запрос, который создает виртуальную таблицу с одним столбцом: SELECT * FROM (VALUES (1), (5), (1)) as listOfValues (columnName) И этот запрос, который создает виртуальную таблицу с двумя столбцами: SELECT * FROM (VALUES (1,2), (5,3), (1,4)) as tableOfValues (columnName1, ColumnName2) Теперь вы можете понять, почему этот пример запроса содержит значение AS (v). Мой последний запрос выглядел так: SELECT Max (currentValues) as Max FROM (VALUES (12), (25), (35)) AS allCurrents (currentValues) Он выберет максимальное значение, которое в данном случае равно 35.
Отличный материал, хорошо работает и в части WHEN MATCHED оператора MERGE! :)
Если date1 и другие на самом деле являются subselects, вам нужно будет заключить их в круглые скобки два набора: ...FROM (VALUES ((SELECT TOP(1) alias.Date FROM ... )), ...
если бы я только мог проголосовать более одного раза. Ты спас меня. ^^
@VaibhavGarg Прекрасно работает в представлениях, какая у вас версия SQL?
Я так долго это искал! Спасибо!
это действительный SQL92, но он работает на меньшем количестве платформ, чем greatest(a,b,c)
Это великолепно. Я должен добавить, что сегодня я столкнулся с ситуацией, когда мне нужно было суммировать по горизонтали, но только тогда, когда второй набор категориальных столбцов был эквивалентен. Я обнаружил, что можно вернуть несколько столбцов в каждом операторе VALUES, а затем объединить их по одному набору в зависимости от того, эквивалентен ли другой набор (или соответствует другому условию CASE). Я мог бы опубликовать код, если мое объяснение сбивает с толку. В общем, вы можете делать это многомерно.
Сначала я пробовал вложенный оператор case, пока не нашел это элегантное решение.
Удивительный. Спасибо, что осветили нам путь: D
К сожалению, Ответ Лассе, хотя и кажется очевидным, имеет серьезный недостаток. Он не может обрабатывать значения 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
Проблема: выберите минимальное значение ставки, присваиваемое юридическому лицу Требования: ставки агентства могут быть нулевыми.
[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
На основе этот ответ из Нат
Пожалуйста, попробуйте использовать UNPIVOT:
SELECT MAX(MaxDt) MaxDt
FROM tbl
UNPIVOT
(MaxDt FOR E IN
(Date1, Date2, Date3)
)AS unpvt;
Использование CROSS APPLY (для 2005+) ....
SELECT MostRecentDate
FROM SourceTable
CROSS APPLY (SELECT MAX(d) MostRecentDate FROM (VALUES (Date1), (Date2), (Date3)) AS a(d)) md
Использование VALUES не позволяет этому работать в 2005 году. Вам придется заменить VALUES на эквивалентную серию SELECT value UNION ALL SELECT value UNION ALL ….
Начиная с SQL Server 2012 мы можем использовать 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
Довольно красиво, но не обрабатывает нули. Например: DECLARE @Date1 DATE='2014-08-01'; DECLARE @Date2 DATE=null; DECLARE @Date3 DATE='2014-07-05'; /*this gets returned*/
Мы могли бы обрабатывать нули следующим образом: select IIF(@Date1 > @Date2 or @Date2 is null, IIF(@Date1 > @Date3 or @Date3 is null, @Date1, @Date3), IIF(@Date2 > @Date3 or @Date3 is null, @Date2, @Date3)) as MostRecentDate
Для T-SQL (MSSQL 2008+)
SELECT
(SELECT
MAX(MyMaxName)
FROM ( VALUES
(MAX(Field1)),
(MAX(Field2))
) MyAlias(MyMaxName)
)
FROM MyTable1
Это точное решение было предложил еще в 2011 году
вот хорошее решение:
CREATE function [dbo].[inLineMax] (@v1 float,@v2 float,@v3 float,@v4 float)
returns float
as
begin
declare @val float
set @val = 0
declare @TableVal table
(value float )
insert into @TableVal select @v1
insert into @TableVal select @v2
insert into @TableVal select @v3
insert into @TableVal select @v4
select @val= max(value) from @TableVal
return @val
end
Я не знаю, есть ли это на SQL и т.д ... в справке M $ ACCESS есть функция MAXA(Value1;Value2;...), которая должна это делать.
Надежда может кому-то помочь.
П.Д .: Значения могут быть столбцами, расчетными и т. д.
Microsoft Access - это совершенно другой продукт. Кроме того, можете ли вы заявить о такой функции? Я никогда не видел и не слышал об этом в Access.
MAXA - это Функция Excel, а не Access.
Другой способ использовать СЛУЧАЙ, КОГДА
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
Я предпочитаю решения, основанные на случае, когда, я предполагаю, что это должно иметь наименьшее влияние на возможное падение производительности по сравнению с другими возможными решениями, такими как перекрестное применение, значения (), пользовательские функции и т. д.
Вот версия case-when, которая обрабатывает нулевые значения с большинством возможных тестовых случаев:
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
;
и результат:
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
о боже, спасибо, сэр! Я потратил так много времени на эту чертову формулу монстра, которая все еще давала мне нули, и теперь я вижу свет в конце туннеля.
A Выше таблица представляет собой таблицу заработной платы сотрудников со столбцами salary1, salary2, salary3, salary4. Запрос ниже вернет максимальное значение из четырех столбцов.
select
(select Max(salval) from( values (max(salary1)),(max(salary2)),(max(salary3)),(max(Salary4)))alias(salval)) as largest_val
from EmployeeSalary
Выполнение вышеуказанного запроса даст результат как large_val (10001)
Логика вышеуказанного запроса следующая:
select Max(salvalue) from(values (10001),(5098),(6070),(7500))alias(salvalue)
вывод будет 10001
Это почти копия решения, опубликованного 29 июля 2011 г. пользователем @sven.
Разве не достаточно использовать
WHEN Date1 > Date2 AND Date1 > Date3 THEN Date1; WHEN Date2 > Date3 THEN Date3; ELSE Date3?