Apakah SQL Server mendukung GREATEST dan LEAST, jika tidak apa solusi yang umum?

15

Meninjau pertanyaan ini sepertinya banyak pekerjaan yang seharusnya tidak diperlukan. Mereka mencoba memperluas rentang dengan tanggal. Di database lain, Anda hanya akan menggunakan greatestdan least..

least(extendDate,min), greatest(extendDate,max)

Ketika saya mencoba menggunakan ini, saya mengerti

'least' is not a recognized built-in function name.
'greatest' is not a recognized built-in function name.

Itu akan mencakup ekstensi di kedua arah.

Untuk keperluan pertanyaan, Anda masih harus melakukan penggantian rentang eksklusif.

Saya hanya ingin tahu bagaimana pengguna SQL Server menerapkan pola kueri untuk meniru leastdan greatestfungsionalitas.

Apakah Anda membuka gulungan syarat ke dalam CASEpernyataan atau apakah ada ekstensi, add-on pihak ketiga, atau lisensi dari Microsoft yang memungkinkan fungsi ini?

Evan Carroll
sumber
Sungguh luar biasa bahwa MSSQL tidak memiliki implementasi untuk LEAST/ GREATESTfungsi - hampir semua pesaing RDBMS memiliki setidaknya yang setara. Satu-satunya pengecualian yang dapat saya temukan adalah Sybase, tetapi itu juga telah dihentikan selama bertahun-tahun pada saat ini.
bsplosion
1
feedback.azure.com/forums/908035-sql-server/suggestions/… jika Anda ingin memilih ini
J Brune

Jawaban:

32

Salah satu metode umum adalah dengan menggunakan VALUESklausa, dan CROSS APPLYdua kolom alias sebagai satu kolom, lalu dapatkan MINdanMAX masing-masing.

SELECT MIN(x.CombinedDate) AS least, MAX(x.CombinedDate) AS greatest
FROM   dbo.Users AS u
CROSS APPLY ( VALUES ( u.CreationDate ), ( u.LastAccessDate )) AS x ( CombinedDate );

Ada cara lain untuk menulisnya, misalnya menggunakan UNION ALL

SELECT MIN(x.CombinedDate) AS least, MAX(x.CombinedDate) AS greatest
FROM   dbo.Users AS u
CROSS APPLY ( SELECT u.CreationDate UNION ALL SELECT u.LastAccessDate ) AS x(CombinedDate);

Namun, rencana kueri yang dihasilkan tampaknya sama.

Erik Darling
sumber
12

Anda juga bisa meletakkan nilai-nilai sebaris dalam subquery. Seperti ini:

select (select max(i) from (values (1), (2), (5), (1), (6)) AS T(i)) greatest,
       (select min(i) from (values (1), (2), (5), (1), (6)) AS T(i)) least
David Browne - Microsoft
sumber
3

Ini akan menjadi awal yang baik -

CASE WHEN A > B THEN A ELSE B END
Jim Gettma
sumber
Ini saran yang bagus tetapi disebutkan dalam pertanyaan dengan "membuka gulungan syarat kondisi menjadi pernyataan KASUS"
Evan Carroll
3

Setara dengan LEAST:

IIF(@a < @b, @a, @b)

Setara TERBESAR:

IIF(@a > @b, @a, @b)
Elnur
sumber
3
Bagaimana Anda melakukannya untuk tiga atau lebih nilai, misalnya least(5,6,7,8,9)?
a_horse_with_no_name
@a_horse_with_no_name Gunakan nested IIF's
Elnur
Pendekatan ini akan dengan cepat menjadi menantang untuk dibaca dan diverifikasi ... Bagaimana caranya dalam hal kinerja?
Dodecaphone
0

Saya membuat fungsi yang ditentukan pengguna, misalnya

create function dbo.udf_LeastInt(@a int, @b int)
returns int
with schemabinding
as
begin
  return case when @a <= @b then @a 
              when @b < @a  then @b
              else null
         end
end

Meskipun mungkin berhasil dalam kasus-kasus sederhana, namun ada beberapa masalah dengan pendekatan ini:

  • Mengganggu Anda harus membuat fungsi terpisah untuk setiap tipe data.
  • Ini hanya menangani 2 parameter, jadi orang mungkin memerlukan lebih banyak fungsi untuk menangani banyak parameter atau menggunakan panggilan bersarang dari fungsi yang sama.
  • Akan lebih baik (lebih efisien) sebagai TVF sebaris daripada fungsi skalar. Itu ada hubungannya dengan implementasi fungsi skalar di hati. Ada banyak blog tentang itu, lihat misalnya SQL 101: Parallelism Inhibitors - Scalar User Defined Functions (oleh John Kehayias .
  • Jika salah satu argumennya nol, ia mengembalikan nol. Ini sesuai dengan apa yang dilakukan oleh leastoperator di Oracle dan MySQL, tetapi berbeda dari Postgres. Tapi lapis baja terhadap nol ini membuatnya lebih verbose (jika Anda tahu mereka tidak akan menjadi nol, sebuah dataran case when @a <= @b then @a else @b endakan bekerja).

Secara keseluruhan mungkin lebih baik untuk menulis casepernyataan itu secara langsung jika kinerja penting. Saya bahkan terpaksa membuat casepernyataan bersarang di sisi klien ketika ada beberapa nilai untuk dibandingkan.

Ed Avis
sumber
0

Saya bermaksud untuk menambahkan komentar pada jawaban @ ed-avis, tetapi tidak dapat melakukannya, karena kurangnya reputasi, jadi memposting ini sebagai ekstensi untuk jawabannya.

Saya telah menghilangkan kerugian dari "Mengganggu Anda harus membuat fungsi terpisah untuk setiap tipe data." Menggunakan SQL_VARIANT .

Inilah implementasi saya:

CREATE OR ALTER FUNCTION my_least(@a SQL_VARIANT, @b SQL_VARIANT)
returns SQL_VARIANT
with schemabinding
as
begin
  return case when @a <= @b then @a 
              when @b < @a  then @b
              WHEN @a IS NULL THEN @b
              WHEN @b IS NULL THEN @a
              else null
         end
END;

Juga fungsi ini menangani NULL seperti versi postgresql.

Fungsi ini dapat ditambahkan ke DB untuk kenyamanan, tetapi 10 kali lebih lambat , daripada menggunakan bawaan IIF. Tes saya menunjukkan, bahwa fungsi tersebut dengan tipe yang tepat ( datetime ) berkinerja sama dengan versi sql_variant .

PS Saya menjalankan beberapa tes pada data-set nilai 350k, dan tampaknya kinerjanya sama, sql_variant sedikit lebih cepat, tapi saya percaya itu hanya kegelisahan.

Tapi bagaimanapun juga versi IIF 10x kali lebih cepat !!!

Saya belum menguji inline CASE WHENtetapi pada dasarnya untuk t-sql IIF sama dengan case , dan jika saya bisa dikonversi oleh optimizer ke case case.

Fakta bahwa IIF diterjemahkan ke dalam CASE juga berdampak pada aspek lain dari perilaku fungsi ini.

KESIMPULAN: Lebih cepat menggunakan IIF jika kinerja penting, tetapi untuk prototipe, atau jika kejelasan kode lebih dibutuhkan, dan tidak ada perhitungan besar yang terlibat, asalkan fungsi dapat digunakan.

Bogdan Mart
sumber
1
Anda mengatakan bahwa "sqlvariant sedikit lebih cepat" dan bahwa "versi IIF adalah 10x kali lebih cepat". lebih cepat dari apa?
ypercubeᵀᴹ
Varian Sql ver sekitar kecepatan yang sama dengan versi concreete, seperti yang disediakan oleh jawaban lain. Dalam pengujian saya itu adalah 80ms fater (keluar dari 15sec), saya berasumsi bahwa itu hanya kesalahan statistik. Dan menggunakan iif(a<b, a, b) adalah 10 kali lebih cepat dari fungsi yang ditentukan pengguna.
Bogdan Mart
Agar jelas, saya menggunakan kode saya dengan sql_variant diganti dengan datetime, sebagai fungsi kedua. Setelah tes tampaknya sql_variant tidak menambahkan overhead, tetapi fungsi yang ditentukan pengguna jauh lebih lambat, daripada
bawaan
Tetapi apakah ada dari fungsi-fungsi ini - termasuk IIF()- lebih cepat daripada menggunakan CASEekspresi? Maksud saya adalah, bahwa karena Anda mengalami kesulitan dalam pengujian kinerja, Anda harus menguji semua metode / jawaban yang disarankan.
ypercubeᵀᴹ
1
@ yper-crazyhat-cubeᵀᴹ jawaban yang diperbarui. Tidak akan mengeditnya lagi, hanya ingin menambahkan komentar tentang sql_variant ke jawaban ed-avis, tetapi karena kurangnya pint harus menulis jawaban yang diperluas :-)
Bogdan Mart