Отримання мінімум двох значень у SQL


180

У мене є дві змінні: одна називається PaidThisMonth, а інша називається OwedPast. Вони обидва результати деяких підзапитів у SQL. Як я можу вибрати менший з двох і повернути його як значення з назвою PaidForPast?

MINФункція працює за стовпцями, а не перемінних. На


1
Якщо ви перебуваєте на Postgres або MySQL, перейдіть до відповіді @ Gil_Margolin.
Номенон

Відповіді:


127

Корпус:

   Select Case When @PaidThisMonth < @OwedPast 
               Then @PaidThisMonth Else @OwedPast End PaidForPast

Як таблицю Inline оцінювали АДС

CREATE FUNCTION Minimum
(@Param1 Integer, @Param2 Integer)
Returns Table As
Return(Select Case When @Param1 < @Param2 
                   Then @Param1 Else @Param2 End MinValue)

Використання:

Select MinValue as PaidforPast 
From dbo.Minimum(@PaidThisMonth, @OwedPast)

ДОБАВЛЕННЯ: Це, мабуть, найкраще, коли звертаєтесь лише до двох можливих значень, якщо їх більше двох, врахуйте відповідь Крейга за допомогою пункту Значення.


краще зрозумілий синтаксис: return (виберіть minValue = випадок, коли кінець @@ param1 <@@ param2, то @@ param1 else @@ param2). Ок, це може не нормалізуватися, я не знаю. Але це набагато зрозуміліше і його слід нормалізувати.
Softlion

1
Ще одна причина віддати перевагу @ Craig відповіді нижче - через нульове поводження. Якщо величини, що порівнюються, є нульовими , а одне зі зіставлених значень є нульовим, показаний регістр комутатора може повернути нуль або значення, залежно від порядку тесту WHEN (якщо ви не додасте використання ISNULL). Підхід Крейга завжди віддасть перевагу вибору ненульового значення, яке здається мені більш правильним, принаймні в моєму поточному випадку використання при порівнянні нульових дат.
Ния

148

SQL Server 2012 та 2014 підтримує функцію IIF (продовження, правда, помилка). Таким чином, для мінімального вибору ви можете використовувати його як

SELECT IIF(first>second, second, first) the_minimal FROM table

Хоча IIF - це лише стенограма для написання CASE...WHEN...ELSE, писати простіше.


8
IIF- це лише синтаксичний цукор для CASE...WHEN...ELSE.
Салман

55
Можливо, так. Але простіше писати.
Мерт Гюлсой

1
@ MertGülsoy І простіше читати, що має бути вгорі у списку пріоритетів для всіх, відразу після коректності.
Даніель

119

Рішення, що використовують CASE, IIF та UDF, є адекватними, але недоцільними, коли поширювати проблему на загальний випадок, використовуючи понад 2 значення порівняння. Узагальнене рішення в SQL Server 2008+ використовує дивне застосування пункту VALUES:

SELECT
PaidForPast=(SELECT MIN(x) FROM (VALUES (PaidThisMonth),(OwedPast)) AS value(x))

Кредит за цей веб-сайт: http://sqlblog.com/blogs/jamie_thomson/archive/2012/01/20/use-values-clause-to-get-the-maximum-value-from-some-columns-sql- сервер-t-sql.aspx


12
Це найкраща відповідь
FindOutIslamNow

якщо ви хочете, щоб мінімум не дорівнював нулю:MIN(x*(case x when 0 then null else 1 end))
mpag

За винятком того, що MartinC дав таку ж відповідь чотирма роками раніше і насправді показав її більш ніж двома значеннями ...
Auspex

4
Auspex, відповідь MartinC не пов'язана. У цій відповіді профспілки не використовують.
Крейг

30

У мене просто виникла ситуація, коли мені довелося знайти максимум 4 складних селектора в межах оновлення. При такому підході ви можете мати скільки завгодно!

Ви також можете замінити номери додатковими виборами

select max(x)
 from (
 select 1 as 'x' union
 select 4 as 'x' union
 select 3 as 'x' union
 select 2 as 'x' 
 ) a

Більш складне використання

 @answer = select Max(x)
           from (
                select @NumberA as 'x' union
                select @NumberB as 'x' union
                select @NumberC as 'x' union
                select (
                       Select Max(score) from TopScores
                       ) as 'x' 
     ) a

Я впевнений, що UDF має кращі показники.


Мені це найбільше подобається, оскільки він є базовим SQL. Крім того, ФДН не є необхідним швидше. У більшості магазинів стовпців кожен атрибут (я припускаю, що ви також збираєтесь фільтрувати за атрибутами) можна обчислювати паралельно, і лише кваліфікаційний набір є об'єднаним. Тож спілки самі по собі не повільні.
Bouncner

простий і дивовижний
ashleedawg

22

Для MySQL або PostgreSQL 9.3+ кращим способом є використання функцій LEASTта GREATEST.

SELECT GREATEST(A.date0, B.date0) AS date0, 
       LEAST(A.date1, B.date1, B.date2) AS date1
FROM A, B
WHERE B.x = A.x

З:

  • GREATEST(value [, ...]): Повертає найбільший (максимально оцінений) аргумент із наданих значень
  • LEAST(value [, ...])Повертає найменший (мінімально оцінений) аргумент із наданих значень

Посилання на документацію:


Це також працює в PostgreSQL (і саме це я шукав :) Дивіться: postgresql.org/docs/9.5/static/functions-conditional.html
Albert Vaca Cintora

1
Це найкраща відповідь на сьогоднішній день.
Роберто Родрігес

2
@RobertoRodriguez було б найкраще, якби питання було позначено MySQL або PostgreSQL як частину питання. Питання стосувалося конкретно tsql, тому ця відповідь зовсім не допомагає.
Jmaurier

це не відповідь для MSSQL
Mujah Maskey

13

Ось хитрість, якщо ви хочете обчислити максимум (поле, 0):

SELECT (ABS(field) + field)/2 FROM Table

повертає 0, якщо fieldнегативно, інше, повернути field.


3
Отже, для обчислення мінімуму (@a, @b) ви можете використовувати:SELECT @a - ( ABS(@a-@b) + (@a-@b) ) / 2
scottyc

1
і не забувайте про тип overflow;)
pkuderov

Це врятує від точки зору точності з плаваючою точкою? Чи впевнено, що результат ніколи не буде чимось близьким до нуля, але негативним?
zuraff

6

Використовуйте оператор CASE.

Приклад B на цій сторінці має бути близьким до того, що ви намагаєтеся зробити:
http://msdn.microsoft.com/en-us/library/ms181765.aspx

Ось код зі сторінки:

USE AdventureWorks;
GO
SELECT   ProductNumber, Name, 'Price Range' = 
      CASE 
         WHEN ListPrice =  0 THEN 'Mfg item - not for resale'
         WHEN ListPrice < 50 THEN 'Under $50'
         WHEN ListPrice >= 50 and ListPrice < 250 THEN 'Under $250'
         WHEN ListPrice >= 250 and ListPrice < 1000 THEN 'Under $1000'
         ELSE 'Over $1000'
      END
FROM Production.Product
ORDER BY ProductNumber ;
GO

2

Використовуйте таблицю темп для вставки діапазону значень, а потім виберіть мінімум / макс таблиці темп із збереженої процедури або UDF. Це основна конструкція, тому сміливо переглядайте за потребою.

Наприклад:

CREATE PROCEDURE GetMinSpeed() AS
BEGIN

    CREATE TABLE #speed (Driver NVARCHAR(10), SPEED INT);
    '
    ' Insert any number of data you need to sort and pull from
    '
    INSERT INTO #speed (N'Petty', 165)
    INSERT INTO #speed (N'Earnhardt', 172)
    INSERT INTO #speed (N'Patrick', 174)

    SELECT MIN(SPEED) FROM #speed

    DROP TABLE #speed

END

2

Це працює до 5 дат і обробляє нулі. Просто не вдалося змусити його працювати як функція Inline.

CREATE FUNCTION dbo.MinDate(@Date1 datetime = Null,
                            @Date2 datetime = Null,
                            @Date3 datetime = Null,
                            @Date4 datetime = Null,
                            @Date5 datetime = Null)
RETURNS Datetime AS
BEGIN
--USAGE select dbo.MinDate('20120405',null,null,'20110305',null)
DECLARE @Output datetime;

WITH Datelist_CTE(DT)
AS (
        SELECT @Date1 AS DT WHERE @Date1 is not NULL UNION
        SELECT @Date2 AS DT WHERE @Date2 is not NULL UNION
        SELECT @Date3 AS DT WHERE @Date3 is not NULL UNION
        SELECT @Date4 AS DT WHERE @Date4 is not NULL UNION
        SELECT @Date5 AS DT WHERE @Date5 is not NULL
   )
Select @Output=Min(DT) FROM Datelist_CTE

RETURN @Output
END

Щойно зрозумів, що вам не потрібні пункти WHERE, оскільки MIN все одно видалить Нулі.
Лоуренс

2

Спираючись на блискучу логіку / код від mathematix та scottyc, я подаю:

DECLARE @a INT, @b INT, @c INT = 0

WHILE @c < 100
    BEGIN
        SET @c += 1
        SET @a = ROUND(RAND()*100,0)-50
        SET @b = ROUND(RAND()*100,0)-50
        SELECT @a AS a, @b AS b,
            @a - ( ABS(@a-@b) + (@a-@b) ) / 2 AS MINab,
            @a + ( ABS(@b-@a) + (@b-@a) ) / 2 AS MAXab,
            CASE WHEN (@a <= @b AND @a = @a - ( ABS(@a-@b) + (@a-@b) ) / 2)
            OR (@a >= @b AND @a = @a + ( ABS(@b-@a) + (@b-@a) ) / 2)
            THEN 'Success' ELSE 'Failure' END AS Status
    END

Хоча стрибок з функції MIN Scottyc на функцію MAX повинен був бути очевидним для мене, але цього не було, тому я вирішив це і включив сюди: SELECT @a + (ABS (@ b- @ a) + ( @ b- @ a)) / 2. Довільно генеровані числа, хоча і не є доказами, повинні хоча б переконати скептиків у тому, що обидві формули є правильними.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.