SQL Server には、.NET の Math.Max のような 2 つの値を取る Max 関数はありますか?
-
02-07-2019 - |
質問
次のようなクエリを書きたいと思います。
SELECT o.OrderId, MAX(o.NegotiatedPrice, o.SuggestedPrice)
FROM Order o
しかし、これはそうではありません MAX
機能は動作しますよね?これは集計関数であるため、単一のパラメータを期待し、すべての行の 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?」に対する Sven の回答。
それは「」だと私は言います。ベストアンサー" なぜなら:
- ユニオン、ピボット、UNPIVOT、UDF、およびクレイジーなケースステートでコードを複雑にする必要はありません。
- null の処理の問題に悩まされることはなく、null を問題なく処理します。
- 「MAX」を「MIN」、「AVG」、または「SUM」に置き換えるのは簡単です。任意の集計関数を使用して、さまざまな列の集計を見つけることができます。
- 私が使用した名前に限定されるわけではありません (つまり、「すべての価格」および「価格」)。次の人が読みやすく理解しやすいように、独自の名前を付けることができます。
- SQL Server 2008 を使用すると、複数の集計を見つけることができます。 派生テーブル そのようです:
SELECT MAX(a)、MAX(b) FROM (VALUES (1, 2)、(3, 4)、(5, 6)、(7, 8)、(9, 10) ) AS MyTable(a, b)
1 行で実行できます。
-- 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
試してみませんか IIF 関数 (SQL Server 2012 以降が必要)
IIF(a>b, a, b)
それでおしまい。
(ヒント:どちらかになることに注意してください null
, の結果以来、 a>b
どちらかが null の場合は常に false になります。それで 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
を使用する場合は、NULL を処理することをお勧めします。 IIF
, 、なぜなら NULL
あなたのどちらかの側に boolean_expression
引き起こします IIF
を返す false_value
(とは対照的に NULL
).
私はによって提供されたソリューションを使用します ククラムリーNULL を処理できるように少し変更するだけです
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
編集からのコメント後に変更されました マーク. 。彼が 3 値ロジックで正しく指摘したように、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 を使用すると 2 つの列に対して同様の結果を得ることができます。ここでは 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
ここでは、null を処理し、古いバージョンの MSSQL で動作するケース例を示します。これは、一般的な例の 1 つの inline 関数に基づいています。
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
NULL 処理を備えた IIF バージョンは次のとおりです (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 Server 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
) 最大 2 つの値を取得します。
そのうちの 1 つが NULL の場合でも。
IIF(col1 >= col2, col1, ISNULL(col2, col1))
または、両方が NULL の場合に 0 を返すようにしたい場合
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
単純な NULL 処理を使用した @Scott Langham の回答は次のとおりです。
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]
Prestoでは使用できます
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