มีฟังก์ชั่นสูงสุดใน SQL Server ที่ใช้สองค่าเช่น Math.Max ​​ใน. NET?


488

ฉันต้องการเขียนแบบสอบถามเช่นนี้

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

แต่นี่ไม่ใช่วิธีการMAXทำงานของฟังก์ชั่นใช่มั้ย มันเป็นฟังก์ชั่นรวมดังนั้นจึงคาดว่าพารามิเตอร์เดียวแล้วส่งกลับค่า MAX ของแถวทั้งหมด

ไม่มีใครรู้วิธีที่จะทำในแบบของฉัน?


13
ที่นำมาใช้ในฐานข้อมูลอื่น ๆ ส่วนใหญ่เป็นGREATESTฟังก์ชั่น; SQLite จำลองการสนับสนุนโดยอนุญาตให้มีหลายคอลัมน์ในการMAXรวม
OMG Ponies

7
สำเนาซ้ำที่เป็นไปได้ของstackoverflow.com/questions/71022/sql-max-of-multiple-columns
Michael Freidgeim

เมื่อค้นหาวิธีแก้ปัญหาสำหรับ max (a, b) ด้านล่างโปรดคำนึงถึงคำถามเกี่ยวกับว่าคุณต้องการไวยากรณ์หรือการคำนวณสำหรับ "a" และ / หรือ "b" ซ้ำอีกครั้งหรือไม่ คือถ้า "b" ได้มาจากการคำนวณที่ซับซ้อนที่เกี่ยวข้องกับไวยากรณ์มากมายคุณอาจต้องการโซลูชันที่มี "b" ปรากฏเพียงครั้งเดียว เช่นโซลูชัน "IIF (a> b, a, b)" หมายถึงการทำซ้ำ "b" - ซึ่งอาจน่าเกลียด syntactically อย่างไรก็ตามวิธีการแก้ปัญหาต่อไปนี้หมายถึง "b" (และ "a") ปรากฏเพียงครั้งเดียวเท่านั้น: SELECT MAX (VALUE) จาก (เลือกค่าที่เหมาะสมสหภาพเลือกเป็นค่า) AS T1
Andrew Jens

คำตอบ:


158

คุณจะต้องทำ a 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

24
ฉันจะสนับสนุนทางออกของคุณสิ่งเดียวที่ฉันจะเพิ่มคือการสนับสนุนค่า NULL หากคุณเพียงแค่ปรับเปลี่ยนบรรทัดสุดท้าย: "return @ value2" เพื่ออ่านเป็น: "return isnull (@ val2, @ val1)" ดังนั้นหากหนึ่งในค่านั้นเป็นโมฆะฟังก์ชันจะส่งคืนค่าไม่ใช่ null มิฉะนั้นจะทำงานตาม ปกติ
kristof

1
ประเภทข้อมูลอื่น ๆ เช่นฉันจะต้องเขียน HigherIntegerArgument และ HigherDateTimeArgument และ HigherVarcharArgument และ a ... ?
onedaywhen

9
สิ่งนี้จะช้าอย่างไม่น่าเชื่อเพราะทุกสิ่งมีขนาดเป็น UDF ใช้ UDF แบบอินไลน์แทน
AK

12
@xan ฉันไม่มีเงื่อนงำสิ่งที่เกิดขึ้นในใจของฉันเมื่อฉันถามคำถามนั้น ไม่มากเกินไปอย่างเห็นได้ชัด ขอบคุณสำหรับคำตอบต่อไป
โทมัส

13
@ ภาพโทมัสภาระผูกพัน (ไม่มีความผิดสำหรับคุณ!) flickr.com/photos/16201371@N00/2375571206
xan

468

หากคุณใช้ SQL Server 2008 (หรือสูงกว่า) นี่คือทางออกที่ดีกว่า:

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

เครดิตและคะแนนโหวตทั้งหมดควรไปที่คำตอบของ Sven สำหรับคำถามที่เกี่ยวข้อง "SQL MAX ของหลายคอลัมน์?"
ฉันพูดว่ามันเป็น " คำตอบที่ดีที่สุด " เพราะ:

  1. ไม่จำเป็นต้องมีความซับซ้อนของรหัสของคุณด้วยรูปแบบของ UNION, PIVOT, UNPIVOT, UDF และรูปแบบที่ยาวเหยียด
  2. มันไม่ได้เกิดจากปัญหาของการจัดการโมฆะ แต่มันจัดการได้ดี
  3. การสลับ "MAX" กับ "MIN", "AVG" หรือ "SUM" เป็นเรื่องง่าย คุณสามารถใช้ฟังก์ชันการรวมเพื่อค้นหาการรวมในหลาย ๆ คอลัมน์
  4. คุณไม่ จำกัด ชื่อที่ฉันใช้ (เช่น "AllPrices" และ "Price") คุณสามารถเลือกชื่อของคุณเองเพื่อให้ง่ายต่อการอ่านและเข้าใจสำหรับคนต่อไป
  5. คุณสามารถค้นหาการรวมหลายรายการโดยใช้valid_tablesของ SQL Server 2008 ดังนี้:
    SELECT MAX (a), MAX (b) FROM (ค่า (1, 2), (3, 4), (5, 6), (7, 8), (9, 10)) AS MyTable (a, b)

27
+1 คำตอบเดียวที่ไม่ต้องการการเข้าถึงเพื่อสร้างขั้นตอน / ฟังก์ชั่น!
อเล็กซ์

6
ประเภทคำตอบที่ฉันต้องการ การใช้ฟังก์ชั่นช้าและจะใช้งานได้ตามวันที่ซึ่งเป็นสิ่งที่ฉันต้องการ
Johann Strydom

3
+1 ทำงานได้สมบูรณ์แบบโดยเฉพาะอย่างยิ่งเมื่อเปรียบเทียบมากกว่า 2 คอลัมน์!
JanW

11
สิ่งนี้มีประสิทธิภาพต่ำกว่าโซลูชัน CASE เมื่อซึ่งจำเป็นต้องคำนวณสเกลาร์เท่านั้น
tekumara

5
แม้ว่าไวยากรณ์ที่ง่ายกว่านั้นอาจไม่คุ้มค่ากับประสิทธิภาพการทำงานเมื่อพิจารณาค่าสูงสุด MAX 2 แต่อาจเป็นเรื่องที่แตกต่างกับค่ามากขึ้น แม้ว่าจะได้รับค่าสูงสุด 4 ค่าแล้วส่วนคำสั่ง CASE จะยาว แต่เงอะงะและข้อผิดพลาดเกิดขึ้นได้ง่ายหากสร้างขึ้นด้วยมือในขณะที่ค่า VALUES ยังคงเรียบง่ายและชัดเจน
Typhlosaurus

221

สามารถทำได้ในหนึ่งบรรทัด:

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

แก้ไข: หากคุณกำลังจัดการกับตัวเลขที่มีขนาดใหญ่มากคุณจะต้องแปลงตัวแปรค่าให้เป็นใหญ่เพื่อหลีกเลี่ยงการล้นจำนวนเต็ม


18
+1 ฉันเชื่อว่าคุณให้วิธีที่ถูกต้องที่สุด "SELECT ((@ val1 + @ val2) + ABS (@ val1- @ val2)) / 2 เป็น MAX_OF_TWO" โปรดจำไว้ว่า "SELECT ((@ val1 + @ val2)) - ABS (@ val1- @ val2)) / 2 เป็น MIN_OF_TWO "
tom

6
วิธีนี้จะทำให้เกิดข้อผิดพลาดมากเกินไปหากผลรวมมากกว่าที่สามารถเก็บไว้ใน int: ประกาศ @ val1 int ประกาศ @ val2 int set @ val1 = 1500000000 set @ val2 = 1500000000 SELECT 0.5 * ((@ val1 + @ val2) + ABS (@ val1 - @ val2)) - => ข้อผิดพลาดมาก
เกินไป

89
นี่เป็นกลอุบาย "สกปรก" อย่างยิ่ง " เมื่อการเขียนโปรแกรมรหัสของคุณควรแสดงจุดมุ่งหมายอย่างชัดเจนอย่างไรก็ตามในกรณีของคุณดูเหมือนว่ารหัสที่ได้จากการประกวดที่ทำให้งงงวย
greenoldman

24
อาจเป็น "สกปรก" แต่อาจเป็นตัวเลือกเดียวสำหรับฐานข้อมูลที่มีภาษา SQL อย่างง่าย
splattne

12
ฉันไม่เห็นด้วยกับ Marcias รหัสไม่จำเป็นต้องแสดงจุดมุ่งหมายอย่างชัดเจนตราบใดที่ความคิดเห็นอนุญาตให้ใช้งานได้ หากคุณกำลังทำสมการทางคณิตศาสตร์ที่ซับซ้อนในรหัส (หรือที่ใดก็ได้) บางครั้งมันยากที่จะทำให้มันอธิบายด้วยตนเอง ตราบใดที่มันแยกออกเป็นส่วนที่ง่ายและเข้าใจได้ง่ายกว่านั้นคือการตั้งโปรแกรมที่ถูกต้อง
Rob

127

ฉันไม่คิดอย่างนั้น ฉันต้องการสิ่งนี้ในวันอื่น สิ่งที่ฉันได้ใกล้ที่สุดคือ:

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

4
นี่เป็นวิธีที่ฉันชอบ คุณไม่เสี่ยงต่อการล้นและมันเป็นความลับน้อยกว่าโซลูชันของ splattne (ซึ่งเย็นมาก) และฉันไม่ต้องวุ่นวายกับการสร้าง UDF เคสมีประโยชน์มากในหลาย ๆ สถานการณ์
แลนซ์ฟิชเชอร์

เลือก o. คำสั่งซื้อ, กรณีที่ o. ราคาต่อรอง> o ราคาที่แนะนำหรือราคาที่เสนอเป็นโมฆะ o ราคาต่อรองราคาจะลดลงจากการสั่งซื้อ o
mohghaderi

เมื่อแทนที่จะ "o.NegotiatedPrice" คุณมีคำเช่น "(ลงวันที่ (วัน, แปลง (วันที่, adr_known_since, 120), getdate ()) - 5) * 0.3" คุณต้องทำซ้ำรหัสนี้ การเปลี่ยนแปลงคำศัพท์ในอนาคตจะต้องทำสองครั้ง ฟังก์ชันประเภทนาที (x, y, ... ) น่าจะดีกว่ามาก
Daniel

87

ทำไมไม่ลองใช้ฟังก์ชั่นIIF (ต้องใช้ SQL Server 2012 และใหม่กว่า)

IIF(a>b, a, b)

แค่นั้นแหละ.

(คำแนะนำ: โปรดระมัดระวังเกี่ยวกับสิ่งใดสิ่งหนึ่งnullเนื่องจากผลลัพธ์a>bจะเป็นเท็จเมื่อใดก็ตามที่เป็นโมฆะดังนั้นbจะเป็นผลลัพธ์ในกรณีนี้)


7
หากหนึ่งในค่าคือNULLผลลัพธ์จะเป็นค่าที่สองเสมอ
jahu

4
IIF () เป็นน้ำตาลประโยคสำหรับคำสั่ง CASE หากค่าของเงื่อนไข CASE เป็น NULL ผลลัพธ์จะเป็นค่าที่สอง (ELSE)
xxyzzy

@xxyzzy นั่นเป็นเพราะNULL > 1234ข้อความเท็จ
Xin

8
ดังนั้นIIF(a>b, a, COALESCE(b,a))เพื่อให้ค่าเมื่อมีอยู่เพียงคนเดียว
mpag

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

ฉันให้ +1 นี้เนื่องจากสอดคล้องกับ DRY (ไม่ต้องทำซ้ำตัวเอง) โดยไม่จำเป็นต้องเขียน UDF นอกจากนี้ยังเป็นสิ่งที่ดีหากค่าทั้งสองที่คุณต้องการตรวจสอบเป็นผลลัพธ์ของ sql อื่น ๆ เช่นในกรณีของฉันฉันต้องการค้นหาคำสั่ง count (*) จำนวน 2 รายการที่เลือกมากขึ้น
MikeKulls

1
ฉันเกลียดที่ฉันต้องใช้วิธีนี้ แต่แน่นอนว่าวิธีที่ดีที่สุดที่จะทำใน SQL Server จนกว่าพวกเขาจะเพิ่มการสนับสนุนดั้งเดิมสำหรับ GREATEST หรือในบรรทัดสูงสุด ขอบคุณที่โพสต์ - +1 ถึงคุณ!
SqlRyan

10

คำตอบอื่น ๆ เป็นสิ่งที่ดี แต่ถ้าคุณต้องกังวลเกี่ยวกับการมีค่า 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

1
ISNULL ที่จำเป็นเท่านั้นคือหลังจาก ELSE การเปรียบเทียบ ">" เริ่มต้นจะส่งกลับค่าเท็จและไปที่ ELSE หากค่าใดค่าหนึ่งเป็นค่าว่างแล้ว
Phil B

10

ใน SQL Server 2012 หรือสูงกว่าคุณสามารถใช้การรวมกันของIIFและISNULL(หรือCOALESCE) เพื่อรับค่าสูงสุด 2
แม้ว่าหนึ่งในนั้นคือ NULL

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

แต่ถ้าต้องการ SUM หลายค่า?
ถ้าอย่างนั้นฉันขอแนะนำให้นำ CROSS ไปใช้กับการรวมค่า
นอกจากนี้ยังมีประโยชน์ที่สามารถคำนวณสิ่งอื่น ๆ ในเวลาเดียวกัน

ตัวอย่าง:

SELECT t.*
, ca.[Total]
, ca.[Maximum]
, ca.[Minimum]
, ca.[Average]
FROM SomeTable t
CROSS APPLY (
   SELECT 
    SUM(v.col) AS [Total], 
    MIN(v.col) AS [Minimum], 
    MAX(v.col) AS [Maximum], 
    AVG(v.col) AS [Average]
   FROM (VALUES (t.Col1), (t.Col2), (t.Col3), (t.Col4)) v(col)
) ca

8

แบบสอบถามย่อยสามารถเข้าถึงคอลัมน์จากการสืบค้นภายนอกเพื่อให้คุณสามารถใช้วิธีนี้ในการใช้การรวมเช่น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

ดี! มันชั่งได้ดีมาก
greenoldman

+1 เพื่อแสดงความรักสำหรับคนที่ยังอยู่ในปี 2005 ฉันไม่รู้ว่าฉันมองข้ามคำตอบนี้อย่างไร ภายใต้ฝาครอบฉันคิดว่ามันทำงานได้ดีเหมือนกับที่ฉันโพสต์ในอีก 2 ปีต่อมา เมื่อมองย้อนกลับไปฉันควรจะเข้าใจสิ่งนี้และปรับปรุงคำตอบของคุณเพื่อให้มีรูปแบบที่ใหม่กว่าในเวลานั้น ขออภัยฉันต้องการแบ่งปันคะแนนกับคุณตอนนี้
MikeTeeVee

@MikeTeeVee - ขอบคุณ! ใช่ภายใต้ความครอบคลุมแผนจะเหมือนกัน แต่VALUESไวยากรณ์คือ nicer
Martin Smith

6

แนะนำ 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งานเพราะ a NULLที่ด้านใดด้านหนึ่งของคุณboolean_expressionจะทำให้เกิดIIFการคืนค่าfalse_value(ตรงข้ามกับNULL)


วิธีแก้ปัญหาของคุณจะไม่จัดการ NULL ได้ดีเมื่อค่าอื่น ๆ เป็นลบสิ่งนี้จะส่งกลับค่า null
t-clausen.dk

5

ฉันจะไปกับวิธีแก้ปัญหาโดยkcrumley เพียงแก้ไขเล็กน้อยเพื่อจัดการ NULLs

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 เสมอ ผลลัพธ์ที่ไม่รู้จักอีกนัยหนึ่งคือ


1
Nulls มีความสำคัญ และเป็นสิ่งสำคัญที่จะจัดการกับพวกเขาอย่างสม่ำเสมอ คำตอบที่เหมาะสมสำหรับ Is NULL> x คือ NULL
Mark Brackett

คุณมีสิทธิที่ฉันจะแก้ไขคำตอบของฉันที่จะสะท้อนให้เห็นว่าขอบคุณสำหรับการชี้ว่า
Kristof

ถ้าเราผ่าน int และ NULL ฉันคิดว่ามันเป็นเรื่องธรรมดามากกว่าที่จะต้องการคืนค่า non-null ดังนั้นฟังก์ชั่นจึงทำหน้าที่เป็นการรวมกันของ Max (x, y) และ ISNULL (x, y) ดังนั้นผมเองจะเปลี่ยนบรรทัดสุดท้ายจะเป็น: การกลับมา ISNULL (@ VAL1 @ VAL2) - ซึ่งเป็นที่ยอมรับอาจเป็นสิ่งที่คุณต้องเริ่มต้นด้วย :)
redcalx

@ the-locster ดูความคิดเห็นโดย Mark
kristof

1
สิ่งนี้จะช้าอย่างไม่น่าเชื่อเพราะทุกสิ่งมีขนาดเป็น UDF ใช้ UDF แบบอินไลน์แทน
AK

4

มันง่ายอย่างนี้:

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;

ดูความคิดเห็น @Neil เพื่อคำตอบก่อนหน้า SELECT dbo.InlineMax (CAST (0.5 AS FLOAT), 100) ผิด
Luca

4
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

สำหรับคำอธิบายโปรดอ่านบทความนี้: red-gate.com/simple-talk/sql/sql-training/ …
Tom Arleth

2
กรุณาอย่าใส่ข้อมูลที่จำเป็นในรหัสของคุณเพียงแค่ลิงค์ ลองจินตนาการว่าลิงค์นี้จะหมดอายุในหนึ่งวันและคำตอบของคุณจะไร้ประโยชน์แล้ว ดังนั้นโปรดไปข้างหน้าและเพิ่มข้อมูล essentiell โดยตรงในคำตอบของคุณ แต่คุณยังคงสามารถให้ลิงค์นั้นเป็นแหล่งอ้างอิงสำหรับผู้อื่นเพื่อค้นหาข้อมูลเพิ่มเติม
L. Guthardt

3

โอ๊ะฉันเพิ่งโพสต์ดักฟังคำถามนี้ ...

คำตอบคือไม่มีฟังก์ชั่นในตัวเช่นOracle's Greatestแต่คุณสามารถได้ผลลัพธ์ที่คล้ายกันสำหรับ 2 คอลัมน์ด้วย 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

Kristof

โพสต์คำตอบนี้:

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

1
หมายเหตุ: การใช้งานฟังก์ชั่น GREATEST จะตรงกับพฤติกรรมของ oracle สำหรับ 2 params ถ้า param ใด ๆ เป็นโมฆะมันจะส่งคืนค่าว่างเปล่า
Sam Saffron

2
คุณควรระวังเมื่อใช้ sql_variant ฟังก์ชันของคุณจะให้ผลลัพธ์ที่ไม่คาดคิดในสถานการณ์ต่อไปนี้: SELECT dbo.greatest (นักแสดง (0.5 AS FLOAT), 100)
Neil

@ Neil ถูกต้อง (ฉันเรียนรู้วิธีที่ยาก) คุณจะปรับปรุงฟังก์ชั่นนี้เพื่อป้องกันปัญหาประเภทนี้อย่างไร?
Luca

3

นี่คือตัวอย่างกรณีที่ควรจัดการกับค่า Null และจะทำงานกับ MSSQL รุ่นเก่ากว่า สิ่งนี้ขึ้นอยู่กับฟังก์ชันอินไลน์ในหนึ่งในตัวอย่างยอดนิยม:

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

2

ฉันอาจจะไม่ทำแบบนี้เนื่องจากประสิทธิภาพน้อยกว่าโครงสร้าง 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

2

สำหรับคำตอบข้างต้นเกี่ยวกับจำนวนมากคุณสามารถทำการคูณก่อนการบวก / การลบ มันค่อนข้างหนา แต่ไม่ต้องใช้งานเลย (ฉันไม่สามารถพูดความเร็วได้ แต่ฉันคิดว่ามันยังค่อนข้างเร็ว)

เลือก 0.5 * ((@ val1 + @ val2) + ABS (@ val1 - @ val2))

เปลี่ยนเป็น

SELECT @ val1 * 0.5 + @ val2 * 0.5 + ABS (@ val1 * 0.5 - @ val2 * 0.5)

อย่างน้อยเป็นทางเลือกถ้าคุณต้องการหลีกเลี่ยงการส่ง


2

นี่คือเวอร์ชัน 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))



1
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

1

ในรูปแบบที่ง่ายที่สุด ...

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


1

นี่คือคำตอบของ @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


0
 -- 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;

ในขณะที่การใช้งานที่น่าสนใจของVALUESอินไลน์เช่นนั้นผมไม่แน่ใจว่านี้จะง่ายกว่าหรือCASE IFFฉันสนใจที่จะดูว่าประสิทธิภาพของโซลูชันนี้เทียบกับตัวเลือกอื่น ๆ ได้อย่างไร
Chris Schaller

0

การขยายคำตอบของซินและสมมติว่าประเภทการเปรียบเทียบคือ 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

0

ใน MemSQL ให้ทำดังนี้:

-- DROP FUNCTION IF EXISTS InlineMax;
DELIMITER //
CREATE FUNCTION InlineMax(val1 INT, val2 INT) RETURNS INT AS
DECLARE
  val3 INT = 0;
BEGIN
 IF val1 > val2 THEN
   RETURN val1;
 ELSE
   RETURN val2;
 END IF; 
END //
DELIMITER ;

SELECT InlineMax(1,2) as test;

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.