Cách tốt nhất để chọn giá trị nhỏ nhất từ ​​một số cột là gì?


82

Cho bảng sau trong SQL Server 2005:

ID   Col1   Col2   Col3
--   ----   ----   ----
1       3     34     76  
2      32    976     24
3       7    235      3
4     245      1    792

Cách tốt nhất để viết truy vấn mang lại kết quả sau (tức là một truy vấn cho ra cột cuối cùng - cột chứa các giá trị nhỏ nhất trong số Col1, Col2 và Col 3 cho mỗi hàng )?

ID   Col1   Col2   Col3  TheMin
--   ----   ----   ----  ------
1       3     34     76       3
2      32    976     24      24
3       7    235      3       3
4     245      1    792       1

CẬP NHẬT:

Để làm rõ (như tôi đã nói trong coments) trong kịch bản thực, cơ sở dữ liệu được chuẩn hóa đúng cách . Các cột "mảng" này không nằm trong bảng thực tế nhưng nằm trong tập hợp kết quả được yêu cầu trong báo cáo. Và yêu cầu mới là báo cáo cũng cần có cột MinValue này. Tôi không thể thay đổi tập kết quả cơ bản và do đó tôi đã tìm đến T-SQL để có "thẻ thoát khỏi nhà tù" tiện dụng.

Tôi đã thử cách tiếp cận CASE được đề cập bên dưới và nó hoạt động, mặc dù nó hơi rườm rà. Nó cũng phức tạp hơn được nêu trong các câu trả lời vì bạn cần giải thích cho thực tế là có hai giá trị nhỏ nhất trong cùng một hàng.

Dù sao, tôi nghĩ rằng tôi sẽ đăng giải pháp hiện tại của mình, giải pháp này, với những hạn chế của tôi, hoạt động khá tốt. Nó sử dụng toán tử UNPIVOT:

with cte (ID, Col1, Col2, Col3)
as
(
    select ID, Col1, Col2, Col3
    from TestTable
)
select cte.ID, Col1, Col2, Col3, TheMin from cte
join
(
    select
        ID, min(Amount) as TheMin
    from 
        cte 
        UNPIVOT (Amount for AmountCol in (Col1, Col2, Col3)) as unpvt
    group by ID
) as minValues
on cte.ID = minValues.ID

Tôi sẽ nói thẳng rằng tôi không mong đợi điều này sẽ mang lại hiệu suất tốt nhất, nhưng trong hoàn cảnh (tôi không thể thiết kế lại tất cả các truy vấn chỉ cho yêu cầu cột MinValue mới), đó là một cách khá thanh lịch "thoát khỏi tù Thẻ".


11
IMHO giải pháp UNPIVOT của tác giả vượt trội hơn so với các câu trả lời khác.
Joe Harris

2
Tôi thấy giải pháp của Nizam là giải pháp tốt nhất, ngay cả khi tôi phải mất một lúc để bắt đầu hiểu nó. Lean và rất hữu ích.
Patrick Honorez

Câu trả lời:


59

Có thể có nhiều cách để thực hiện điều này. Đề xuất của tôi là sử dụng Case / Khi nào thì làm. Với 3 cột, nó không quá tệ.

Select Id,
       Case When Col1 < Col2 And Col1 < Col3 Then Col1
            When Col2 < Col1 And Col2 < Col3 Then Col2 
            Else Col3
            End As TheMin
From   YourTableNameHere

6
Đây là suy nghĩ ban đầu của tôi. Nhưng truy vấn thực sự cần 5 cột và số lượng cột có thể tăng lên. Vì vậy, cách tiếp cận CASE trở nên hơi khó sử dụng. Nhưng nó hoạt động.
stucampbell

2
Nếu số lượng cột có thể tăng lên, bạn chắc chắn đang làm sai - hãy xem bài đăng của tôi (lý do tại sao bạn không nên thiết lập lược đồ DB của mình theo cách này :-).
paxdiablo

2
Cảm ơn. Như tôi đã đề cập trong một bình luận khác. Tôi không truy vấn bảng thực tế. Các bảng được chuẩn hóa một cách chính xác. Truy vấn này là một phần của truy vấn đặc biệt phức tạp và đang làm việc trên các kết quả trung gian từ các bảng dẫn xuất.
stucampbell

2
Trong trường hợp đó, bạn có thể lấy chúng theo cách khác để chúng trông bình thường không?
Kev

3
Thêm vào câu trả lời từ @Gmastros khi tôi gặp phải vấn đề rằng một số Cols có dữ liệu khớp nên tôi phải thêm dấu =. Dữ liệu của tôi cũng có khả năng bị null vì vậy tôi phải thêm vào câu lệnh hoặc để giải thích điều đó. Có thể có một cách dễ dàng hơn để làm điều này nhưng tôi đã không tìm thấy cách nào trong 6 tháng qua mà tôi đã tìm kiếm. Cảm ơn tất cả mọi người tham gia ở đây. Chọn Id, Trường hợp Khi (Col1 <= Col2 HOẶC Col2 là rỗng) Và (Col1 <= Col3 HOẶC Col3 là rỗng) Khi đó Col1 Khi (Col2 <= Col1 HOẶC Col1 là rỗng) Và (Col2 <= Col3 HOẶC Col3 là rỗng) Khi đó Col2 Else Col3 End As TheMin From YourTableNameHere
Chad Portman

55

Sử dụng CROSS APPLY:

SELECT ID, Col1, Col2, Col3, MinValue
FROM YourTable
CROSS APPLY (SELECT MIN(d) AS MinValue FROM (VALUES (Col1), (Col2), (Col3)) AS a(d)) A

SQL Fiddle


Trông thú vị nhưng tôi không thể làm cho nó hoạt động. Bạn có thể nói rõ một chút được không? thx
Patrick Honorez

2
@iDevlop tôi chèn Fiddle SQL trong câu trả lời của tôi
Nizam

Điều tôi không biết là hàm vô hướng. Có vẻ như câu trả lời của bạn cũng hoạt động mà không có cross apply. Nó có thêm giá trị / hiệu suất không? stackoverflow.com/a/14712024/78522
Patrick Honorez

@iDevlop Nếu nó không mang lại hiệu suất, nó sẽ tăng khả năng đọc. Ví dụ, tôi có thể sử dụng một cái gì đó giống như where MinValue > 10, mà tôi không thể làm được mà không cầnCROSS APPLY
Nizam

2
thực sự, tôi đã có cơ hội hiểu được lợi ích "khả năng tái sử dụng" của nó trong thời gian chờ đợi. Cảm ơn. Tôi đã học được 2 điều hôm nay ;-)
Patrick Honorez

31
SELECT ID, Col1, Col2, Col3, 
    (SELECT MIN(Col) FROM (VALUES (Col1), (Col2), (Col3)) AS X(Col)) AS TheMin
FROM Table

1
Cảm ơn vì đã bắt được. Tôi đã bỏ lỡ thẻ đó. Tôi thực sự không biết, và cũng không có khả năng kiểm tra nó. Sẽ thận trọng hơn trong việc kiểm tra thẻ trong tương lai.
dsz

3
Đưa ra giải pháp thanh lịch hơn - không chắc tại sao nó không có nhiều lượt ủng hộ hơn.
jwolf

Đối với max / calcs phút inline này đến nay là cách tốt nhất để làm điều này
Saxman

Giải pháp tuyệt vời.
Phani

16

Trên MySQL, sử dụng cái này:

select least(col1, col2, col3) FROM yourtable

Có thể không phải là một câu lệnh SQL.
Tino Jose Thannippara

4
nhưng trong một số trường hợp thì đúng như vậy. đối với những người đó, đây là một câu trả lời tuyệt vời
Kirby

1
Có sẵn trong Postgres kể từ 8.1: postgresql.org/docs/8.1/functions-conditional.html#AEN12704
swrobel

1
Phần mở rộng SQL không chuẩn này được hỗ trợ bởi khá nhiều cơ sở dữ liệu ngoại trừ máy chủ Microsoft SQL.
Mikko Rantalainen

LEASThoạt động trong phiên bản mới nhất của Microsoft SQL Server Managed Instances, kể từ ~ 12 ngày trước. reddit.com/r/SQLServer/comments/k0dj2r/…
John Zabroski

10

Bạn có thể sử dụng cách tiếp cận "bạo lực" với một bước ngoặt:

SELECT CASE
    WHEN Col1 <= Col2 AND Col1 <= Col3 THEN Col1
    WHEN                  Col2 <= Col3 THEN Col2
    ELSE                                    Col3
END AS [Min Value] FROM [Your Table]

Khi điều kiện đầu tiên không thành công, nó đảm bảo rằng Col1 không phải là giá trị nhỏ nhất, do đó bạn có thể loại bỏ nó khỏi các điều kiện còn lại. Tương tự như vậy cho các điều kiện tiếp theo. Đối với năm cột, truy vấn của bạn sẽ trở thành:

SELECT CASE
    WHEN Col1 <= Col2 AND Col1 <= Col3 AND Col1 <= Col4 AND Col1 <= Col5 THEN Col1
    WHEN                  Col2 <= Col3 AND Col2 <= Col4 AND Col2 <= Col5 THEN Col2
    WHEN                                   Col3 <= Col4 AND Col3 <= Col5 THEN Col3
    WHEN                                                    Col4 <= Col5 THEN Col4
    ELSE                                                                      Col5
END AS [Min Value] FROM [Your Table]

Lưu ý rằng nếu có sự ràng buộc giữa hai hoặc nhiều cột thì <=đảm bảo rằng chúng ta thoát khỏi CASEcâu lệnh càng sớm càng tốt.


2
Sử dụng <=thay thế, nếu không, giá trị tối thiểu phù hợp cuối cùng sẽ được sử dụng thay vì giá trị đầu tiên.
chezy525

6

Nếu các cột là số nguyên như trong ví dụ của bạn, tôi sẽ tạo một hàm:

create function f_min_int(@a as int, @b as int) 
returns int
as
begin
    return case when @a < @b then @a else coalesce(@b,@a) end
end

sau đó khi tôi cần sử dụng nó, tôi sẽ làm:

select col1, col2, col3, dbo.f_min_int(dbo.f_min_int(col1,col2),col3)

nếu bạn có 5 dấu hai chấm thì ở trên trở thành

select col1, col2, col3, col4, col5,
dbo.f_min_int(dbo.f_min_int(dbo.f_min_int(dbo.f_min_int(col1,col2),col3),col4),col5)

4
Với hiệu suất tồi tệ đến mức nực cười của các hàm vô hướng trong MSSQL, tôi cảm thấy có nghĩa vụ phải đưa ra lời khuyên chống lại phương pháp này. Nếu bạn muốn đi con đường này, ít nhất hãy viết một hàm nhận tất cả 5 cột làm tham số cùng một lúc. Nó vẫn sẽ là xấu, nhưng ít nhất một chút ít xấu = /
deroby

Đệ quy sẽ làm giảm hiệu suất. Nhưng nó sẽ đáp ứng yêu cầu.
Tino Jose Thannippara

6

Cách tốt nhất để làm điều đó có lẽ là không làm điều đó - thật kỳ lạ khi mọi người khăng khăng lưu trữ dữ liệu của họ theo cách yêu cầu SQL "gymnastics" để trích xuất thông tin có ý nghĩa, khi có nhiều cách dễ dàng hơn để đạt được kết quả mong muốn nếu bạn chỉ cấu trúc lược đồ của bạn tốt hơn một chút :-)

Theo tôi, cách đúng để làm điều này là có bảng sau:

ID    Col    Val
--    ---    ---
 1      1      3
 1      2     34
 1      3     76

 2      1     32
 2      2    976
 2      3     24

 3      1      7
 3      2    235
 3      3      3

 4      1    245
 4      2      1
 4      3    792

với ID/Collàm khóa chính (và có thể Collà khóa phụ, tùy thuộc vào nhu cầu của bạn). Sau đó, truy vấn của bạn trở nên đơn giản select min(val) from tblvà bạn vẫn có thể xử lý các 'cột cũ' riêng lẻ bằng cách sử dụng where col = 2trong các truy vấn khác của mình. Điều này cũng cho phép dễ dàng mở rộng nếu số lượng 'cột cũ' tăng lên.

Điều này làm cho truy vấn của bạn để dễ dàng hơn nhiều. Nguyên tắc chung mà tôi có xu hướng sử dụng là, nếu bạn từng có thứ gì đó trông giống như một mảng trong một hàng cơ sở dữ liệu, có thể bạn đang làm sai điều gì đó và nên nghĩ đến việc cơ cấu lại dữ liệu.


Tuy nhiên, nếu vì lý do nào đó mà bạn không thể thay đổi các cột đó, tôi khuyên bạn nên sử dụng trình kích hoạt chèn và cập nhật và thêm một cột khác mà các trình kích hoạt này đặt ở mức tối thiểu Col1/2/3. Điều này sẽ di chuyển 'chi phí' của hoạt động từ lựa chọn sang cập nhật / chèn nơi nó thuộc về - hầu hết các bảng cơ sở dữ liệu theo kinh nghiệm của tôi được đọc thường xuyên hơn nhiều so với được viết, do đó, chi phí ghi có xu hướng hiệu quả hơn theo thời gian.

Nói cách khác, giá trị tối thiểu cho một hàng chỉ thay đổi khi một trong các cột khác thay đổi, vì vậy đó là lúc bạn nên tính toán nó, không phải mỗi khi bạn chọn (điều này sẽ bị lãng phí nếu dữ liệu không thay đổi). Sau đó, bạn sẽ kết thúc với một bảng như sau:

ID   Col1   Col2   Col3   MinVal
--   ----   ----   ----   ------
 1      3     34     76        3
 2     32    976     24       24
 3      7    235      3        3
 4    245      1    792        1

Bất kỳ tùy chọn nào khác phải đưa ra quyết định tại selectthời điểm thường là một ý tưởng tồi về hiệu suất, vì dữ liệu chỉ thay đổi khi chèn / cập nhật - việc thêm một cột khác chiếm nhiều không gian hơn trong DB và sẽ hơi chậm hơn đối với các lần chèn và cập nhật nhưng có thể nhiều nhanh hơn cho Selects - phương pháp ưa thích nên phụ thuộc vào ưu tiên của bạn đó nhưng, như đã nói, hầu hết các bảng được đọc xa hơn thường xuyên hơn họ đang viết.


18
Ừm. Cảm ơn cho diatribe. Cơ sở dữ liệu thực được chuẩn hóa đúng cách. Đây là một ví dụ đơn giản. Truy vấn thực tế phức tạp và 5 cột tôi quan tâm là kết quả trung gian từ các bảng dẫn xuất.
stucampbell

3
Thật không may, diatribe vẫn đứng. Việc tạo các bảng trung gian ở dạng bạn đề xuất cũng khó khăn như việc tạo các bảng vĩnh viễn như vậy. Điều này được chứng minh bằng việc bạn phải thực hiện cái mà tôi thích gọi là môn thể dục dụng cụ SQL để có được kết quả như mong muốn.
paxdiablo

Nếu có lý do chính đáng để cần 'mảng' trong một hàng, vui lòng cho chúng tôi biết, nhưng việc sử dụng nó để chọn giá trị tối thiểu không phải là một trong số đó.
paxdiablo

2
+1 cho đề xuất kích hoạt để bảo toàn cấu trúc bảng ban đầu (nếu có sai sót).
Scott Ferguson

1
Điều gì sẽ xảy ra nếu bạn đang xử lý một bảng Phân cấp, tự tham gia vào bảng đó?
Nathan Tregillus

5

Bạn cũng có thể làm điều này với một truy vấn liên hợp. Khi số lượng cột tăng lên, bạn sẽ cần phải sửa đổi truy vấn, nhưng ít nhất nó sẽ là một sửa đổi thẳng.

Select T.Id, T.Col1, T.Col2, T.Col3, A.TheMin
From   YourTable T
       Inner Join (
         Select A.Id, Min(A.Col1) As TheMin
         From   (
                Select Id, Col1
                From   YourTable

                Union All

                Select Id, Col2
                From   YourTable

                Union All

                Select Id, Col3
                From   YourTable
                ) As A
         Group By A.Id
       ) As A
       On T.Id = A.Id

2
Điều này hoạt động nhưng hiệu suất sẽ giảm khi số hàng tăng lên.
Tomalak

1
Cảm ơn. Có, điều này hoạt động. Như Tomalak nói, trong truy vấn từ khóa của tôi, điều này sẽ khá khó cho hiệu suất. Nhưng +1 cho nỗ lực. :)
stucampbell

4

Đây là vũ lực nhưng hoạt động

 select case when col1 <= col2 and col1 <= col3 then col1
           case when col2 <= col1 and col2 <= col3 then col2
           case when col3 <= col1 and col3 <= col2 then col3
    as 'TheMin'
           end

from Table T

... bởi vì min () chỉ hoạt động trên một cột và không hoạt động trên các cột.


Nó cũng có xu hướng nhanh nhất vì MIN tạo phép nối vòng lặp lồng nhau ngầm.
John Zabroski

2

Cả câu hỏi nàycâu hỏi này hãy cố gắng trả lời câu hỏi này.

Tóm lại là Oracle có một chức năng tích hợp cho việc này, với Sql Server, bạn gặp khó khăn khi xác định một hàm do người dùng xác định hoặc sử dụng các câu lệnh trường hợp.


2

Đối với nhiều cột, tốt nhất nên sử dụng câu lệnh CASE, tuy nhiên đối với hai cột số i và j, bạn có thể sử dụng phép toán đơn giản:

min (i, j) = (i + j) / 2 - abs (ij) / 2

Công thức này có thể được sử dụng để nhận giá trị tối thiểu của nhiều cột nhưng quá khứ thực sự lộn xộn của nó 2, min (i, j, k) sẽ là min (i, min (j, k))


1

Nếu bạn có thể tạo một thủ tục được lưu trữ, nó có thể nhận một mảng giá trị và bạn có thể gọi nó.


Oracle có một hàm gọi là LEAST () thực hiện chính xác những gì bạn muốn.
Kev

Cảm ơn vì đã cọ xát điều đó :) Tôi không thể tin rằng SQL Server không có một phiên bản tương đương!
stucampbell

Tôi thậm chí sẽ nói, "Này, pgsql yêu thích của tôi cũng không có nó," nhưng nó thực sự có. ;) Bản thân hàm sẽ không khó viết.
Kev

Ồ, ngoại trừ việc T-SQL thậm chí không có hỗ trợ mảng (???) Chà, tôi đoán bạn có thể có một hàm năm tham số và nếu bạn cần thêm chỉ cần mở rộng nó ...
Kev

1
select *,
case when column1 < columnl2 And column1 < column3 then column1
when columnl2 < column1 And columnl2 < column3 then columnl2
else column3
end As minValue
from   tbl_example

1
Đây là một bản sao với câu trả lời của G Mastros, vì vậy nếu bạn nên tự hỏi: Tôi đoán đó là nguyên nhân của việc bỏ phiếu từ chối.
Tomalak

1

Một chút thay đổi về truy vấn liên hợp:

DECLARE @Foo TABLE (ID INT, Col1 INT, Col2 INT, Col3 INT)

INSERT @Foo (ID, Col1, Col2, Col3)
VALUES
(1, 3, 34, 76),
(2, 32, 976, 24),
(3, 7, 235, 3),
(4, 245, 1, 792)

SELECT
    ID,
    Col1,
    Col2,
    Col3,
    (
        SELECT MIN(T.Col)
        FROM
        (
            SELECT Foo.Col1 AS Col UNION ALL
            SELECT Foo.Col2 AS Col UNION ALL
            SELECT Foo.Col3 AS Col 
        ) AS T
    ) AS TheMin
FROM
    @Foo AS Foo

1

Nếu bạn sử dụng SQL 2005, bạn có thể làm điều gì đó gọn gàng như sau:

;WITH    res
          AS ( SELECT   t.YourID ,
                        CAST(( SELECT   Col1 AS c01 ,
                                        Col2 AS c02 ,
                                        Col3 AS c03 ,
                                        Col4 AS c04 ,
                                        Col5 AS c05
                               FROM     YourTable AS cols
                               WHERE    YourID = t.YourID
                             FOR
                               XML AUTO ,
                                   ELEMENTS
                             ) AS XML) AS colslist
               FROM     YourTable AS t
             )
    SELECT  YourID ,
            colslist.query('for $c in //cols return min(data($c/*))').value('.',
                                            'real') AS YourMin ,
            colslist.query('for $c in //cols return avg(data($c/*))').value('.',
                                            'real') AS YourAvg ,
            colslist.query('for $c in //cols return max(data($c/*))').value('.',
                                            'real') AS YourMax
    FROM    res

Bằng cách này, bạn không bị lạc trong quá nhiều toán tử :)

Tuy nhiên, điều này có thể chậm hơn so với lựa chọn khác.

Đó là sự lựa chọn của bạn...


Như tôi đã nói, điều này có thể chậm, nhưng nếu bạn có quá nhiều cột (rõ ràng là do thiết kế DB thực sự tồi!), Thì nó có thể đáng để sử dụng (ít nhất là đối với AVG). Bạn đã không cho tôi bất kỳ gợi ý nào nếu đó là một con bò tốt hay xấu :) Có lẽ bạn nên sử dụng phiếu bầu lên / xuống để giúp tôi tìm ra.
leoinfo

Nó không thực sự là tốt hay xấu;). Tôi không phải là chuyên gia cơ sở dữ liệu, vì vậy tôi chỉ nói "con bò thần thánh" vì câu hỏi có vẻ như nó sẽ có một câu trả lời tầm thường. Tôi đoán đó là một giải pháp tốt vì bạn đã quản lý để cung cấp một giải pháp linh hoạt, có thể mở rộng cho vấn đề!
dreamlax

1

Dưới đây, tôi sử dụng một bảng tạm thời để lấy số ngày tối thiểu. Bảng tạm thời đầu tiên truy vấn một số bảng được kết hợp để lấy các ngày khác nhau (cũng như các giá trị khác cho truy vấn), bảng tạm thời thứ hai sau đó lấy các cột khác nhau và ngày tối thiểu bằng cách sử dụng càng nhiều lần chuyển càng có nhiều cột ngày.

Về cơ bản, điều này giống như truy vấn liên hợp, cùng một số lần vượt qua được yêu cầu, nhưng có thể hiệu quả hơn (dựa trên kinh nghiệm, nhưng sẽ cần thử nghiệm). Hiệu quả không phải là một vấn đề trong trường hợp này (8.000 bản ghi). Người ta có thể lập chỉ mục, v.v.

--==================== this gets minimums and global min
if object_id('tempdb..#temp1') is not null
    drop table #temp1
if object_id('tempdb..#temp2') is not null
    drop table #temp2

select r.recordid ,  r.ReferenceNumber, i.InventionTitle, RecordDate, i.ReceivedDate
, min(fi.uploaddate) [Min File Upload], min(fi.CorrespondenceDate) [Min File Correspondence]
into #temp1
from record r 
join Invention i on i.inventionid = r.recordid
left join LnkRecordFile lrf on lrf.recordid = r.recordid
left join fileinformation fi on fi.fileid = lrf.fileid
where r.recorddate > '2015-05-26'
 group by  r.recordid, recorddate, i.ReceivedDate,
 r.ReferenceNumber, i.InventionTitle



select recordid, recorddate [min date]
into #temp2
from #temp1

update #temp2
set [min date] = ReceivedDate 
from #temp1 t1 join #temp2 t2 on t1.recordid = t2.recordid
where t1.ReceivedDate < [min date] and  t1.ReceivedDate > '2001-01-01'

update #temp2 
set [min date] = t1.[Min File Upload]
from #temp1 t1 join #temp2 t2 on t1.recordid = t2.recordid
where t1.[Min File Upload] < [min date] and  t1.[Min File Upload] > '2001-01-01'

update #temp2
set [min date] = t1.[Min File Correspondence]
from #temp1 t1 join #temp2 t2 on t1.recordid = t2.recordid
where t1.[Min File Correspondence] < [min date] and t1.[Min File Correspondence] > '2001-01-01'


select t1.*, t2.[min date] [LOWEST DATE]
from #temp1 t1 join #temp2 t2 on t1.recordid = t2.recordid
order by t1.recordid

1
SELECT [ID],
            (
                SELECT MIN([value].[MinValue])
                FROM
                (
                    VALUES
                        ([Col1]),
                        ([Col1]),
                        ([Col2]),
                        ([Col3])
                ) AS [value] ([MinValue])
           ) AS [MinValue]
FROM Table;

0

Nếu bạn biết những giá trị bạn đang tìm kiếm, thường là mã trạng thái, thì những điều sau có thể hữu ích:

select case when 0 in (PAGE1STATUS ,PAGE2STATUS ,PAGE3STATUS,
PAGE4STATUS,PAGE5STATUS ,PAGE6STATUS) then 0 else 1 end
FROM CUSTOMERS_FORMS

0

Tôi biết câu hỏi đó đã cũ, nhưng tôi vẫn cần câu trả lời và không hài lòng với các câu trả lời khác nên tôi phải nghĩ ra câu trả lời của riêng mình, đó là một sự thay đổi trong câu trả lời của @ paxdiablo .


Tôi đến từ vùng đất của SAP ASE 16.0 và tôi chỉ cần xem qua số liệu thống kê của một số dữ liệu nhất định được IMHO lưu trữ hợp lệ trong các cột khác nhau của một hàng (chúng đại diện cho các thời điểm khác nhau - khi đến một thứ gì đó đã được lên kế hoạch, điều đó được mong đợi khi hành động bắt đầu và cuối cùng là thời gian thực tế). Do đó, tôi đã chuyển đổi các cột thành các hàng của bảng tạm thời và định dạng trước truy vấn của tôi về điều này như thường lệ.

NB Không phải là giải pháp một kích thước phù hợp với tất cả trước mắt!

CREATE TABLE #tempTable (ID int, columnName varchar(20), dataValue int)

INSERT INTO #tempTable 
  SELECT ID, 'Col1', Col1
    FROM sourceTable
   WHERE Col1 IS NOT NULL
INSERT INTO #tempTable 
  SELECT ID, 'Col2', Col2
    FROM sourceTable
   WHERE Col2 IS NOT NULL
INSERT INTO #tempTable 
  SELECT ID, 'Col3', Col3
    FROM sourceTable
   WHERE Col3 IS NOT NULL

SELECT ID
     , min(dataValue) AS 'Min'
     , max(dataValue) AS 'Max'
     , max(dataValue) - min(dataValue) AS 'Diff' 
  FROM #tempTable 
  GROUP BY ID

Quá trình này mất khoảng 30 giây trên tập hợp nguồn gồm 630000 hàng và chỉ sử dụng dữ liệu chỉ mục, vì vậy không phải là thứ để chạy trong quy trình quan trọng về thời gian nhưng đối với những thứ như kiểm tra dữ liệu một lần hoặc báo cáo cuối ngày, bạn có thể tốt (nhưng hãy xác minh điều này với đồng nghiệp hoặc cấp trên của bạn!). Phần thưởng chính của phong cách này đối với tôi là tôi có thể dễ dàng sử dụng nhiều hơn / ít cột hơn và thay đổi nhóm, lọc, v.v., đặc biệt khi dữ liệu được sao chép qua.

Dữ liệu bổ sung ( columnName, các max, ...) đã giúp tôi tìm kiếm, vì vậy bạn có thể không cần chúng; Tôi để chúng ở đây để có thể châm ngòi cho một số ý tưởng :-).

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.