هل هناك دالة Max في SQL Server تأخذ قيمتين مثل Math.Max ​​في .NET؟

StackOverflow https://stackoverflow.com/questions/124417

  •  02-07-2019
  •  | 
  •  

سؤال

أريد أن أكتب استعلام مثل هذا:

SELECT o.OrderId, MAX(o.NegotiatedPrice, o.SuggestedPrice)
FROM Order o

ولكن هذه ليست الطريقة MAX وظيفة تعمل، أليس كذلك؟إنها دالة مجمعة لذا فهي تتوقع معلمة واحدة ثم تقوم بإرجاع الحد الأقصى لجميع الصفوف.

هل يعرف أحد كيف يفعل ذلك بطريقتي؟

هل كانت مفيدة؟

المحلول

سوف تحتاج إلى إجراء User-Defined Function إذا كنت تريد أن يكون لديك بناء جملة مشابه لمثالك، ولكن هل يمكنك أن تفعل ما تريد القيام به، بشكل مضمن، بسهولة إلى حد ما باستخدام CASE البيان، كما قال الآخرون.

ال UDF يمكن أن يكون شيء من هذا القبيل:

create function dbo.InlineMax(@val1 int, @val2 int)
returns int
as
begin
  if @val1 > @val2
    return @val1
  return isnull(@val2,@val1)
end

...وسوف تسميها هكذا ...

SELECT o.OrderId, dbo.InlineMax(o.NegotiatedPrice, o.SuggestedPrice) 
FROM Order o

نصائح أخرى

إذا كنت تستخدم SQL Server 2008 (أو أعلى)، فهذا هو الحل الأفضل:

SELECT o.OrderId,
       (SELECT MAX(Price)
        FROM (VALUES (o.NegotiatedPrice),(o.SuggestedPrice)) AS AllPrices(Price))
FROM Order o

يجب أن يذهب كل الفضل والأصوات إلى إجابة سفين على سؤال ذي صلة، "SQL MAX لأعمدة متعددة؟"
أقول إنه "افضل جواب" لأن:

  1. لا يتطلب الأمر تعقيد الكود الخاص بك مع إحصائيات حالة Union و Pivot's و Unpivot's و UDF و Crazy Long.
  2. إنها لا تعاني من مشكلة التعامل مع القيم الخالية، بل تتعامل معها بشكل جيد.
  3. من السهل تبديل "MAX" بـ "MIN" أو "AVG" أو "SUM".يمكنك استخدام أي دالة تجميعية للعثور على التجميع عبر العديد من الأعمدة المختلفة.
  4. أنت لا تقتصر على الأسماء التي استخدمتها (أي."كل الأسعار" و"السعر").يمكنك اختيار الأسماء الخاصة بك لتسهيل القراءة والفهم للرجل التالي.
  5. يمكنك العثور على مجاميع متعددة باستخدام SQL Server 2008 مشتق_جداول مثل ذلك:
    حدد MAX(a)، MAX(b) من (القيم (1، 2)، (3، 4)، (5، 6)، (7، 8)، (9، 10)) AS MyTable(a, b)

يمكن القيام به في سطر واحد:

-- the following expression calculates ==> max(@val1, @val2)
SELECT 0.5 * ((@val1 + @val2) + ABS(@val1 - @val2)) 

يحرر: إذا كنت تتعامل مع أعداد كبيرة جدًا، فسيتعين عليك تحويل متغيرات القيمة إلى Bigint لتجنب تجاوز الأعداد الصحيحة.

أنا لا أعتقد ذلك.أردت هذا في اليوم الآخر.أقرب ما وصلت إليه كان:

SELECT
  o.OrderId,
  CASE WHEN o.NegotiatedPrice > o.SuggestedPrice THEN o.NegotiatedPrice 
     ELSE o.SuggestedPrice
  END
FROM Order o

لماذا لا تحاول معهد التمويل الدولي الوظيفة (تتطلب SQL Server 2012 والإصدارات الأحدث)

IIF(a>b, a, b)

هذا كل شيء.

(تَلمِيح:كن حذرا بشأن أي منهما سيكون null, ، منذ نتيجة a>b سيكون خطأ عندما يكون أي منهما فارغًا.لذا b ستكون النتيجة في هذه الحالة)

DECLARE @MAX INT
@MAX = (SELECT MAX(VALUE) 
               FROM (SELECT 1 AS VALUE UNION 
                     SELECT 2 AS VALUE) AS T1)

الإجابات الأخرى جيدة، ولكن إذا كان عليك القلق بشأن وجود قيم NULL، فقد تحتاج إلى هذا المتغير:

SELECT o.OrderId, 
   CASE WHEN ISNULL(o.NegotiatedPrice, o.SuggestedPrice) > ISNULL(o.SuggestedPrice, o.NegotiatedPrice)
        THEN ISNULL(o.NegotiatedPrice, o.SuggestedPrice)
        ELSE ISNULL(o.SuggestedPrice, o.NegotiatedPrice)
   END
FROM Order o

يمكن للاستعلامات الفرعية الوصول إلى الأعمدة من الاستعلام الخارجي حتى تتمكن من استخدامها هذا النهج لاستخدام المجاميع مثل MAX عبر الأعمدة.(ربما يكون أكثر فائدة عندما يكون هناك عدد أكبر من الأعمدة المعنية)

;WITH [Order] AS
(
SELECT 1 AS OrderId, 100 AS NegotiatedPrice, 110 AS SuggestedPrice UNION ALL
SELECT 2 AS OrderId, 1000 AS NegotiatedPrice, 50 AS SuggestedPrice
)
SELECT
       o.OrderId, 
       (SELECT MAX(price)FROM 
           (SELECT o.NegotiatedPrice AS price 
            UNION ALL SELECT o.SuggestedPrice) d) 
        AS MaxPrice 
FROM  [Order]  o

تم تقديم SQL Server 2012 IIF:

SELECT 
    o.OrderId, 
    IIF( ISNULL( o.NegotiatedPrice, 0 ) > ISNULL( o.SuggestedPrice, 0 ),
         o.NegotiatedPrice, 
         o.SuggestedPrice 
    )
FROM 
    Order o

يوصى بالتعامل مع القيم الخالية عند الاستخدام IIF, ، لأن أ NULL على جانبي الخاص بك boolean_expression سوف يسبب IIF لإرجاع false_value (في مقابل NULL).

سأذهب مع الحل المقدم من kcrumleyفقط قم بتعديله قليلاً للتعامل مع القيم الخالية

create function dbo.HigherArgumentOrNull(@val1 int, @val2 int)
returns int
as
begin
  if @val1 >= @val2
    return @val1
  if @val1 < @val2
    return @val2

 return NULL
end

يحررتم التعديل بعد التعليق من علامة.كما أشار بشكل صحيح في المنطق ثلاثي القيمة x > NULL أو x < NULL يجب دائمًا إرجاع NULL.وبعبارة أخرى نتيجة غير معروفة.

انها بسيطة مثل هذا:

CREATE FUNCTION InlineMax
(
    @p1 sql_variant,
    @p2 sql_variant
)  RETURNS sql_variant
AS
BEGIN
    RETURN CASE 
        WHEN @p1 IS NULL AND @p2 IS NOT NULL THEN @p2 
        WHEN @p2 IS NULL AND @p1 IS NOT NULL THEN @p1
        WHEN @p1 > @p2 THEN @p1
        ELSE @p2 END
END;

عفوًا، لقد نشرت للتو خدعة هذا السؤال...

الجواب هو أنه لا يوجد مثل هذه الوظيفة المضمنة أوراكل أعظم, ، ولكن يمكنك تحقيق نتيجة مماثلة لعمودين باستخدام UDF، لاحظ أن استخدام sql_variant مهم جدًا هنا.

create table #t (a int, b int) 

insert #t
select 1,2 union all 
select 3,4 union all
select 5,2

-- option 1 - A case statement
select case when a > b then a else b end
from #t

-- option 2 - A union statement 
select a from #t where a >= b 
union all 
select b from #t where b > a 

-- option 3 - A udf
create function dbo.GREATEST
( 
    @a as sql_variant,
    @b as sql_variant
)
returns sql_variant
begin   
    declare @max sql_variant 
    if @a is null or @b is null return null
    if @b > @a return @b  
    return @a 
end


select dbo.GREATEST(a,b)
from #t

كريستوف

نشرت هذا الرد :

create table #t (id int IDENTITY(1,1), a int, b int)
insert #t
select 1,2 union all
select 3,4 union all
select 5,2

select id, max(val)
from #t
    unpivot (val for col in (a, b)) as unpvt
group by id

إليك مثال حالة يجب أن يتعامل مع القيم الخالية وسيعمل مع الإصدارات الأقدم من MSSQL.يعتمد هذا على الوظيفة المضمنة في أحد الأمثلة الشائعة:

case
  when a >= b then a
  else isnull(b,a)
end

ربما لن أفعل ذلك بهذه الطريقة، لأنها أقل كفاءة من بنيات CASE المذكورة بالفعل - إلا إذا كان لديك فهارس تغطي كلا الاستعلامين.وفي كلتا الحالتين، إنها تقنية مفيدة لمشاكل مماثلة:

SELECT OrderId, MAX(Price) as Price FROM (
   SELECT o.OrderId, o.NegotiatedPrice as Price FROM Order o
   UNION ALL
   SELECT o.OrderId, o.SuggestedPrice as Price FROM Order o
) as A
GROUP BY OrderId

إليك إصدار IIF مع معالجة NULL (استنادًا إلى إجابة Xin):

IIF(a IS NULL OR b IS NULL, ISNULL(a,b), IIF(a > b, a, b))

المنطق كما يلي، إذا كانت إحدى القيمتين NULL، قم بإرجاع القيمة التي ليست NULL (إذا كانت كلاهما NULL، فسيتم إرجاع NULL).وإلا رد الأعظم.

يمكن فعل الشيء نفسه بالنسبة لـ MIN.

IIF(a IS NULL OR b IS NULL, ISNULL(a,b), IIF(a < b, a, b))
SELECT o.OrderId,   
--MAX(o.NegotiatedPrice, o.SuggestedPrice)  
(SELECT MAX(v) FROM (VALUES (o.NegotiatedPrice), (o.SuggestedPrice)) AS value(v)) as ChoosenPrice  
FROM Order o

يمكنك القيام بشيء مثل هذا:

select case when o.NegotiatedPrice > o.SuggestedPrice 
then o.NegotiatedPrice
else o.SuggestedPrice
end
SELECT o.OrderID
CASE WHEN o.NegotiatedPrice > o.SuggestedPrice THEN
 o.NegotiatedPrice
ELSE
 o.SuggestedPrice
END AS Price
CREATE FUNCTION [dbo].[fnMax] (@p1 INT, @p2 INT)
RETURNS INT
AS BEGIN

    DECLARE @Result INT

    SET @p2 = COALESCE(@p2, @p1)

    SELECT
        @Result = (
                   SELECT
                    CASE WHEN @p1 > @p2 THEN @p1
                         ELSE @p2
                    END
                  )

    RETURN @Result

END

بالنسبة للإجابة أعلاه فيما يتعلق بالأعداد الكبيرة، يمكنك إجراء الضرب قبل الجمع/الطرح.إنها أضخم قليلاً ولكنها لا تتطلب طاقمًا.(لا أستطيع التحدث عن السرعة ولكني أفترض أنها لا تزال سريعة جدًا)

حدد 0.5 * (( @Val1 + @Val2) + ABS ( @Val1 - @Val2))

تغيير الى

حدد @Val1*0.5 + @Val2*0.5 + ABS ( @Val1*0.5 - @Val2*0.5)

بديل على الأقل إذا كنت تريد تجنب الصب.

في أبسط صوره...

CREATE FUNCTION fnGreatestInt (@Int1 int, @Int2 int )
RETURNS int
AS
BEGIN

    IF @Int1 >= ISNULL(@Int2,@Int1)
        RETURN @Int1
    ELSE
        RETURN @Int2

    RETURN NULL --Never Hit

END

بالنسبة لخادم SQL 2012:

SELECT 
    o.OrderId, 
    IIF( o.NegotiatedPrice >= o.SuggestedPrice,
         o.NegotiatedPrice, 
         ISNULL(o.SuggestedPrice, o.NegiatedPrice) 
    )
FROM 
    Order o

في SQL Server 2012 أو الإصدارات الأحدث، يمكنك استخدام مجموعة من IIF و ISNULL (أو COALESCE) للحصول على قيمتين كحد أقصى.
حتى عندما يكون واحد منهم فارغًا.

IIF(col1 >= col2, col1, ISNULL(col2, col1)) 

أو إذا كنت تريد إرجاع 0 عندما يكون كلاهما NULL

IIF(col1 >= col2, col1, COALESCE(col2, col1, 0)) 

مقتطف من المثال:

-- use table variable for testing purposes
declare @Order table 
(
  OrderId int primary key identity(1,1),
  NegotiatedPrice decimal(10,2),
  SuggestedPrice decimal(10,2)
);

-- Sample data
insert into @Order (NegotiatedPrice, SuggestedPrice) values
(0, 1),
(2, 1),
(3, null),
(null, 4);

-- Query
SELECT 
     o.OrderId, o.NegotiatedPrice, o.SuggestedPrice, 
     IIF(o.NegotiatedPrice >= o.SuggestedPrice, o.NegotiatedPrice, ISNULL(o.SuggestedPrice, o.NegotiatedPrice)) AS MaxPrice
FROM @Order o

نتيجة:

OrderId NegotiatedPrice SuggestedPrice  MaxPrice
1       0,00            1,00            1,00
2       2,00            1,00            2,00
3       3,00            NULL            3,00
4       NULL            4,00            4,00

إليك إجابة @ Scott Langham بمعالجة NULL بسيطة:

SELECT
      o.OrderId,
      CASE WHEN (o.NegotiatedPrice > o.SuggestedPrice OR o.SuggestedPrice IS NULL) 
         THEN o.NegotiatedPrice 
         ELSE o.SuggestedPrice
      END As MaxPrice
FROM Order o
select OrderId, (
    select max([Price]) from (
        select NegotiatedPrice [Price]
        union all
        select SuggestedPrice
    ) p
) from [Order]

في المعزوفة يمكنك استخدام الاستخدام

SELECT array_max(ARRAY[o.NegotiatedPrice, o.SuggestedPrice])
 -- Simple way without "functions" or "IF" or "CASE"
 -- Query to select maximum value
 SELECT o.OrderId
  ,(SELECT MAX(v)
   FROM (VALUES (o.NegotiatedPrice), (o.SuggestedPrice)) AS value(v)) AS MaxValue
  FROM Order o;

بالتوسع في إجابة Xin وافتراض أن نوع قيمة المقارنة هو INT، فإن هذا الأسلوب يعمل أيضًا:

SELECT IIF(ISNULL(@A, -2147483648) > ISNULL(@B, -2147483648), @A, @B)

هذا اختبار كامل مع أمثلة للقيم:

DECLARE @A AS INT
DECLARE @B AS INT

SELECT  @A = 2, @B = 1
SELECT  IIF(ISNULL(@A, -2147483648) > ISNULL(@B, -2147483648), @A, @B)
-- 2

SELECT  @A = 2, @B = 3
SELECT  IIF(ISNULL(@A, -2147483648) > ISNULL(@B, -2147483648), @A, @B)
-- 3

SELECT  @A = 2, @B = NULL
SELECT  IIF(ISNULL(@A, -2147483648) > ISNULL(@B, -2147483648), @A, @B)
-- 2    

SELECT  @A = NULL, @B = 1
SELECT  IIF(ISNULL(@A, -2147483648) > ISNULL(@B, -2147483648), @A, @B)
-- 1
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top