Mendapatkan minimal dua nilai dalam SQL

180

Saya memiliki dua variabel, satu disebut PaidThisMonth, dan yang lainnya disebut OwedPast. Keduanya merupakan hasil dari beberapa subquery di SQL. Bagaimana saya bisa memilih yang lebih kecil dari keduanya dan mengembalikannya sebagai nilai berjudul PaidForPast?

The MINfungsi bekerja pada kolom, bukan variabel.

Malfist
sumber
1
Jika Anda menggunakan Postgres atau MySQL, lewati ke jawaban @ Gil_Margolin.
Noumenon

Jawaban:

127

Gunakan Kasus:

   Select Case When @PaidThisMonth < @OwedPast 
               Then @PaidThisMonth Else @OwedPast End PaidForPast

Sebagai tabel Inline dihargai UDF

CREATE FUNCTION Minimum
(@Param1 Integer, @Param2 Integer)
Returns Table As
Return(Select Case When @Param1 < @Param2 
                   Then @Param1 Else @Param2 End MinValue)

Pemakaian:

Select MinValue as PaidforPast 
From dbo.Minimum(@PaidThisMonth, @OwedPast)

TAMBAHKAN LAMPIRAN: Ini mungkin yang terbaik untuk ketika membahas hanya dua nilai yang mungkin, jika ada lebih dari dua, pertimbangkan jawaban Craig menggunakan klausa Nilai.

Charles Bretana
sumber
sintaks yang lebih baik dimengerti: return (pilih minValue = case ketika @@ param1 <@@ param2 lalu @@ param1 else @@ param2 end). Ok ini mungkin tidak dinormalisasi, saya tidak tahu. Tapi itu jauh lebih dimengerti dan harus dinormalisasi.
Softlion
1
Alasan lain untuk memilih jawaban @ Craig di bawah ini adalah karena penanganan nol. Jika nilai yang dibandingkan adalah nullable, dan salah satu nilai yang dibandingkan adalah null, switch-case yang ditampilkan mungkin mengembalikan nol atau nilainya, tergantung pada urutan tes WHEN (kecuali jika Anda menambahkan penggunaan ISNULL). Pendekatan Craig akan selalu memilih pemilihan nilai bukan-nol yang tampaknya lebih tepat bagi saya, setidaknya dalam kasus penggunaan saya saat ini dalam perbandingan tanggal yang dapat dibatalkan.
Nij
148

SQL Server 2012 dan 2014 mendukung fungsi IIF (lanjutan, benar, salah). Jadi untuk seleksi minimal Anda bisa menggunakannya seperti

SELECT IIF(first>second, second, first) the_minimal FROM table

Sementara IIF hanya singkatan untuk menulis CASE...WHEN...ELSE, lebih mudah untuk menulis.

Mert Gülsoy
sumber
8
IIFhanyalah gula sintaksis untuk CASE...WHEN...ELSE.
Salman A
55
Mungkin ya. Tetapi lebih mudah untuk menulis.
Mert Gülsoy
1
@ MertGülsoy Dan lebih mudah dibaca, yang seharusnya ada di bagian atas daftar prioritas semua orang, tepat setelah kebenaran.
Daniel
118

Solusi menggunakan CASE, IIF, dan UDF memadai, tetapi tidak praktis ketika memperluas masalah ke kasus umum menggunakan lebih dari 2 nilai perbandingan. Solusi umum dalam SQL Server 2008+ menggunakan aplikasi aneh dari klausa VALUES:

SELECT
PaidForPast=(SELECT MIN(x) FROM (VALUES (PaidThisMonth),(OwedPast)) AS value(x))

Penghargaan karena situs web ini: http://sqlblog.com/blogs/jamie_thomson/archive/2012/01/20/use-values-clause-to-get-the-maximum-value-from-some-columns-sql- server-t-sql.aspx

Craig
sumber
12
Ini adalah jawaban terbaik
FindOutIslamNow
jika Anda ingin min non-nol:MIN(x*(case x when 0 then null else 1 end))
mpag
Kecuali MartinC memberikan jawaban yang sama empat tahun sebelumnya, dan benar-benar menunjukkannya dengan lebih dari dua nilai ...
Auspex
4
Auspex, jawaban MartinC tidak berhubungan. Jawaban ini tidak menggunakan serikat pekerja.
Craig
30

Saya baru saja mengalami situasi di mana saya harus menemukan maks 4 pilihan kompleks dalam pembaruan. Dengan pendekatan ini Anda dapat memiliki sebanyak yang Anda suka!

Anda juga dapat mengganti angka-angka dengan pemilihan tambahan

select max(x)
 from (
 select 1 as 'x' union
 select 4 as 'x' union
 select 3 as 'x' union
 select 2 as 'x' 
 ) a

Penggunaan lebih kompleks

 @answer = select Max(x)
           from (
                select @NumberA as 'x' union
                select @NumberB as 'x' union
                select @NumberC as 'x' union
                select (
                       Select Max(score) from TopScores
                       ) as 'x' 
     ) a

Saya yakin UDF memiliki kinerja yang lebih baik.

MartinC
sumber
Saya suka yang paling karena itu SQL dasar. Selain itu, UDF tidak perlu lebih cepat. Untuk sebagian besar toko kolom, setiap atribut (saya anggap Anda juga akan memfilter pada atribut) dapat dihitung secara paralel dan hanya set kualifikasi yang disatukan. Jadi serikat tidak lambat per se.
Bouncner
sederhana & luar biasa.
ashleedawg
22

Untuk MySQL atau PostgreSQL 9.3+, cara yang lebih baik adalah dengan menggunakan LEASTdan GREATESTfungsinya.

SELECT GREATEST(A.date0, B.date0) AS date0, 
       LEAST(A.date1, B.date1, B.date2) AS date1
FROM A, B
WHERE B.x = A.x

Dengan:

  • GREATEST(value [, ...]): Mengembalikan argumen terbesar (bernilai maksimum) dari nilai yang diberikan
  • LEAST(value [, ...])Mengembalikan argumen terkecil (bernilai minimum) dari nilai yang diberikan

Tautan dokumentasi:

Gil Margolin
sumber
Ini juga berfungsi di PostgreSQL (dan itulah yang saya cari :) Lihat: postgresql.org/docs/9.5/static/functions-conditional.html
Albert Vaca Cintora
1
Ini adalah jawaban terbaik sejauh ini.
Roberto Rodriguez
2
@RobertoRodriguez akan menjadi yang terbaik jika pertanyaan memiliki tag MySQL atau PostgreSQL sebagai bagian dari pertanyaan. Pertanyaannya secara khusus tentang tsql sehingga jawaban ini tidak membantu sama sekali.
Jmaurier
ini bukan jawaban untuk MSSQL
Mujah Maskey
13

Ini adalah trik jika Anda ingin menghitung maksimum (bidang, 0):

SELECT (ABS(field) + field)/2 FROM Table

mengembalikan 0 jika fieldnegatif, jika tidak, kembali field.

mathatix
sumber
3
Jadi, untuk menghitung minimum (@a, @b), Anda dapat menggunakan:SELECT @a - ( ABS(@a-@b) + (@a-@b) ) / 2
scottyc
1
dan jangan lupa tentang tipe overflow;)
pkuderov
Apakah ini menghemat dari sudut pandang presisi floating-point? Apakah sudah pasti, bahwa hasilnya tidak akan pernah mendekati nol tetapi negatif?
zuraff
6

Gunakan pernyataan KASUS.

Contoh B di halaman ini harus dekat dengan apa yang Anda coba lakukan:
http://msdn.microsoft.com/en-us/library/ms181765.aspx

Berikut kode dari halaman:

USE AdventureWorks;
GO
SELECT   ProductNumber, Name, 'Price Range' = 
      CASE 
         WHEN ListPrice =  0 THEN 'Mfg item - not for resale'
         WHEN ListPrice < 50 THEN 'Under $50'
         WHEN ListPrice >= 50 and ListPrice < 250 THEN 'Under $250'
         WHEN ListPrice >= 250 and ListPrice < 1000 THEN 'Under $1000'
         ELSE 'Over $1000'
      END
FROM Production.Product
ORDER BY ProductNumber ;
GO
Mike Cole
sumber
2

Gunakan tabel temp untuk memasukkan rentang nilai, lalu pilih min / maks tabel temp dari dalam prosedur tersimpan atau UDF. Ini adalah konstruksi dasar, jadi silakan revisi sesuai kebutuhan.

Sebagai contoh:

CREATE PROCEDURE GetMinSpeed() AS
BEGIN

    CREATE TABLE #speed (Driver NVARCHAR(10), SPEED INT);
    '
    ' Insert any number of data you need to sort and pull from
    '
    INSERT INTO #speed (N'Petty', 165)
    INSERT INTO #speed (N'Earnhardt', 172)
    INSERT INTO #speed (N'Patrick', 174)

    SELECT MIN(SPEED) FROM #speed

    DROP TABLE #speed

END
pengguna1970604
sumber
2

Ini berfungsi hingga 5 tanggal dan menangani nol. Hanya tidak bisa membuatnya berfungsi sebagai fungsi Inline.

CREATE FUNCTION dbo.MinDate(@Date1 datetime = Null,
                            @Date2 datetime = Null,
                            @Date3 datetime = Null,
                            @Date4 datetime = Null,
                            @Date5 datetime = Null)
RETURNS Datetime AS
BEGIN
--USAGE select dbo.MinDate('20120405',null,null,'20110305',null)
DECLARE @Output datetime;

WITH Datelist_CTE(DT)
AS (
        SELECT @Date1 AS DT WHERE @Date1 is not NULL UNION
        SELECT @Date2 AS DT WHERE @Date2 is not NULL UNION
        SELECT @Date3 AS DT WHERE @Date3 is not NULL UNION
        SELECT @Date4 AS DT WHERE @Date4 is not NULL UNION
        SELECT @Date5 AS DT WHERE @Date5 is not NULL
   )
Select @Output=Min(DT) FROM Datelist_CTE

RETURN @Output
END
Lawrence
sumber
Baru sadar Anda tidak membutuhkan WHERE Clauses karena MIN akan menghapus Nulls.
Lawrence
2

Membangun logika / kode brilian dari Mathematix dan scottyc, saya serahkan:

DECLARE @a INT, @b INT, @c INT = 0

WHILE @c < 100
    BEGIN
        SET @c += 1
        SET @a = ROUND(RAND()*100,0)-50
        SET @b = ROUND(RAND()*100,0)-50
        SELECT @a AS a, @b AS b,
            @a - ( ABS(@a-@b) + (@a-@b) ) / 2 AS MINab,
            @a + ( ABS(@b-@a) + (@b-@a) ) / 2 AS MAXab,
            CASE WHEN (@a <= @b AND @a = @a - ( ABS(@a-@b) + (@a-@b) ) / 2)
            OR (@a >= @b AND @a = @a + ( ABS(@b-@a) + (@b-@a) ) / 2)
            THEN 'Success' ELSE 'Failure' END AS Status
    END

Meskipun lompatan dari fungsi MIN scottyc ke fungsi MAX seharusnya sudah jelas bagi saya, ternyata tidak, jadi saya sudah menyelesaikannya dan memasukkannya di sini: SELECT @a + (ABS (@ b- @ a) + ( @ b- @ a)) / 2. Angka-angka yang dihasilkan secara acak, meskipun bukan bukti, setidaknya harus meyakinkan skeptis bahwa kedua formula itu benar.

DaveX
sumber