هل هناك دالة Max في SQL Server تأخذ قيمتين مثل Math.Max في .NET؟
-
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 لأعمدة متعددة؟"
أقول إنه "افضل جواب" لأن:
- لا يتطلب الأمر تعقيد الكود الخاص بك مع إحصائيات حالة Union و Pivot's و Unpivot's و UDF و Crazy Long.
- إنها لا تعاني من مشكلة التعامل مع القيم الخالية، بل تتعامل معها بشكل جيد.
- من السهل تبديل "MAX" بـ "MIN" أو "AVG" أو "SUM".يمكنك استخدام أي دالة تجميعية للعثور على التجميع عبر العديد من الأعمدة المختلفة.
- أنت لا تقتصر على الأسماء التي استخدمتها (أي."كل الأسعار" و"السعر").يمكنك اختيار الأسماء الخاصة بك لتسهيل القراءة والفهم للرجل التالي.
- يمكنك العثور على مجاميع متعددة باستخدام 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