Cách hiệu quả nhất để có được tối thiểu nhiều cột trên SQL Server 2005 là gì?


29

Tôi đang ở trong một tình huống mà tôi muốn nhận được giá trị tối thiểu từ 6 cột.

Tôi đã tìm thấy ba cách cho đến nay để thực hiện điều này, nhưng tôi lo ngại về hiệu suất của các phương pháp này và muốn biết cách nào sẽ tốt hơn cho hiệu suất.

Phương pháp đầu tiên là sử dụng một tuyên bố trường hợp lớn . Đây là một ví dụ với 3 cột, dựa trên ví dụ trong liên kết ở trên. Báo cáo trường hợp của tôi sẽ dài hơn nhiều vì tôi sẽ xem xét 6 cột.

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

Tùy chọn thứ hai là sử dụng UNIONtoán tử với nhiều câu lệnh chọn . Tôi sẽ đặt cái này trong UDF chấp nhận tham số Id.

select Id, dbo.GetMinimumFromMyTable(Id)
from MyTable

select min(col)
from
(
    select col1 [col] from MyTable where Id = @id
    union all
    select col2 from MyTable where Id = @id
    union all
    select col3 from MyTable where Id = @id
) as t

Và tùy chọn thứ 3 tôi tìm thấy là sử dụng toán tử UNPIVOT , cái mà tôi thậm chí không biết đã tồn tại cho đến tận bây giờ

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

Do kích thước bảng và tần suất mà bảng này được truy vấn và cập nhật, tôi lo ngại về tác động hiệu suất mà các truy vấn này sẽ có trên cơ sở dữ liệu.

Truy vấn này thực sự sẽ được sử dụng trong một liên kết với một bảng có vài triệu bản ghi, tuy nhiên các bản ghi được trả về sẽ bị giảm xuống còn khoảng một trăm bản ghi cùng một lúc. Nó sẽ được chạy nhiều lần trong ngày và 6 cột tôi đang truy vấn thường xuyên được cập nhật (chúng chứa các số liệu thống kê hàng ngày). Tôi không nghĩ có bất kỳ chỉ mục nào trên 6 cột tôi đang truy vấn.

Phương pháp nào trong số các phương pháp này tốt hơn cho hiệu suất khi cố gắng lấy tối thiểu nhiều cột? Hoặc có một phương pháp nào khác tốt hơn mà tôi không biết?

Tôi đang sử dụng SQL Server 2005

Dữ liệu mẫu & Kết quả

Nếu dữ liệu của tôi chứa các bản ghi như thế này:

Id Col1 Col2 Col3 Col4 Col5 Col6
1 3 4 0 2 1 5
2 2 6 10 5 7 9
3 1 1 2 3 4 5
4 9 5 4 6 8 9

Kết quả cuối cùng phải là

Giá trị Id
1 0
2 2
3 1
4 4

Câu trả lời:


22

Tôi đã thử nghiệm hiệu suất của cả 3 phương pháp và đây là những gì tôi tìm thấy:

  • 1 bản ghi: Không có sự khác biệt đáng chú ý
  • 10 hồ sơ: Không có sự khác biệt đáng chú ý
  • 1.000 hồ sơ: Không có sự khác biệt đáng chú ý
  • 10.000 hồ sơ: UNIONtruy vấn con chậm hơn một chút. Các CASE WHENtruy vấn là một chút nhanh hơn so với UNPIVOTmột.
  • 100.000 bản ghi: UNIONtruy vấn con chậm hơn đáng kể, nhưng UNPIVOTtruy vấn trở nên nhanh hơn một chút so với CASE WHENtruy vấn
  • 500.000 bản ghi: UNIONtruy vấn con vẫn chậm hơn đáng kể, nhưng UNPIVOTtrở nên nhanh hơn nhiều so với CASE WHENtruy vấn

Vì vậy, kết quả cuối cùng dường như là

  • Với các bộ hồ sơ nhỏ hơn dường như không có quá nhiều sự khác biệt. Sử dụng bất cứ điều gì dễ dàng nhất để đọc và duy trì.

  • Khi bạn bắt đầu nhận được các bộ bản ghi lớn hơn, UNION ALLtruy vấn con bắt đầu thực hiện kém so với hai phương thức còn lại.

  • Câu CASElệnh thực hiện tốt nhất cho đến một điểm nhất định (trong trường hợp của tôi, khoảng 100 nghìn hàng) và điểm nào UNPIVOTtruy vấn trở thành truy vấn có hiệu suất tốt nhất

Số thực tế mà một truy vấn trở nên tốt hơn một truy vấn khác có thể sẽ thay đổi do kết quả của phần cứng, lược đồ cơ sở dữ liệu, dữ liệu và tải máy chủ hiện tại của bạn, vì vậy hãy chắc chắn kiểm tra với hệ thống của chính bạn nếu bạn quan tâm đến hiệu suất.

Tôi cũng đã chạy một số bài kiểm tra bằng câu trả lời của Mikael ; tuy nhiên, nó chậm hơn cả 3 phương thức khác đã thử ở đây đối với hầu hết các kích cỡ recordset. Ngoại lệ duy nhất là nó đã làm tốt hơn một UNION ALLtruy vấn cho các kích thước recordset rất lớn. Tôi thích thực tế là nó hiển thị tên cột ngoài giá trị nhỏ nhất.

Tôi không phải là một dba, vì vậy tôi có thể không tối ưu hóa các bài kiểm tra của mình và bỏ lỡ điều gì đó. Tôi đã thử nghiệm với dữ liệu trực tiếp thực tế, do đó có thể ảnh hưởng đến kết quả. Tôi đã cố gắng giải thích điều đó bằng cách chạy từng truy vấn một vài lần khác nhau, nhưng bạn không bao giờ biết. Tôi chắc chắn sẽ quan tâm nếu ai đó viết ra một bài kiểm tra rõ ràng về điều này và chia sẻ kết quả của họ.


6

Không biết về những gì nhanh nhất nhưng bạn có thể thử một cái gì đó như thế này.

declare @T table
(
  Col1 int,
  Col2 int,
  Col3 int,
  Col4 int,
  Col5 int,
  Col6 int
)

insert into @T values(1, 2, 3, 4, 5, 6)
insert into @T values(2, 3, 1, 4, 5, 6)

select T4.ColName, T4.ColValue
from @T as T1
  cross apply (
                select T3.ColValue, T3.ColName
                from (
                       select row_number() over(order by T2.ColValue) as rn,
                              T2.ColValue,
                              T2.ColName
                       from (
                              select T1.Col1, 'Col1' union all
                              select T1.Col2, 'Col2' union all
                              select T1.Col3, 'Col3' union all
                              select T1.Col4, 'Col4' union all
                              select T1.Col5, 'Col5' union all
                              select T1.Col6, 'Col6'
                            ) as T2(ColValue, ColName)
                     ) as T3
                where T3.rn = 1
              ) as T4

Kết quả:

ColName ColValue
------- -----------
Col1    1
Col3    1

Nếu bạn không quan tâm đến cột nào có giá trị tối thiểu, bạn có thể sử dụng cột này để thay thế.

declare @T table
(
  Id int,
  Col1 int,
  Col2 int,
  Col3 int,
  Col4 int,
  Col5 int,
  Col6 int
)

insert into @T
select 1,        3,       4,       0,       2,       1,       5 union all
select 2,        2,       6,      10,       5,       7,       9 union all
select 3,        1,       1,       2,       3,       4,       5 union all
select 4,        9,       5,       4,       6,       8,       9

select T.Id, (select min(T1.ColValue)
              from (
                      select T.Col1 union all
                      select T.Col2 union all
                      select T.Col3 union all
                      select T.Col4 union all
                      select T.Col5 union all
                      select T.Col6
                    ) as T1(ColValue)
             ) as ColValue
from @T as T

Một truy vấn đơn giản đơn giản.

select Id, min(ColValue) as ColValue
from @T
unpivot (ColValue for Col in (Col1, Col2, Col3, Col4, Col5, Col6)) as U
group by Id

6

Thêm một cột được tính toán bền vững sử dụng một CASE câu lệnh để thực hiện logic bạn cần.

Giá trị tối thiểu sau đó sẽ luôn có sẵn một cách hiệu quả khi bạn cần thực hiện tham gia (hoặc bất cứ điều gì khác) dựa trên giá trị đó.

Giá trị sẽ được tính toán lại mỗi khi bất kỳ giá trị nguồn nào thay đổi ( INSERT/ UPDATE/ MERGE). Tôi không nói rằng đây nhất thiết là giải pháp tốt nhất cho khối lượng công việc, tôi chỉ cung cấp nó như một giải pháp, giống như các câu trả lời khác. Chỉ OP mới có thể xác định cái nào là tốt nhất cho khối lượng công việc.


1

Báo cáo trường hợp trong 6 ngày. Để làm ít hơn, sao chép nhánh thực từ câu lệnh đầu tiên. Trường hợp xấu nhất là khi Date1 là giá trị thấp nhất, trường hợp tốt nhất là khi Date6 là giá trị thấp nhất, vì vậy hãy đặt ngày có khả năng nhất vào Date6. Tôi đã viết điều này vì những hạn chế của các cột được tính toán.

CASE WHEN Date1 IS NULL OR Date1 > Date2 THEN
        CASE WHEN Date2 IS NULL OR Date2 > Date3 THEN
            CASE WHEN Date3 IS NULL OR Date3 > Date4 THEN
                CASE WHEN Date4 IS NULL OR Date4 > Date5 THEN
                    CASE WHEN Date5 IS NULL OR Date5 > Date6 THEN
                        Date6
                    ELSE
                        Date5
                    END
                ELSE
                    CASE WHEN Date4 IS NULL OR Date4 > Date6 THEN
                        Date6
                    ELSE
                        Date4
                    END
                END
            ELSE
                CASE WHEN Date3 IS NULL OR Date3 > Date5 THEN
                    CASE WHEN Date5 IS NULL OR Date5 > Date6 THEN
                        Date6
                    ELSE
                        Date5
                    END
                ELSE
                    CASE WHEN Date3 IS NULL OR Date3 > Date6 THEN
                        Date6
                    ELSE
                        Date3
                    END
                END
            END
        ELSE
            CASE WHEN Date2 IS NULL OR Date2 > Date4 THEN
                CASE WHEN Date4 IS NULL OR Date4 > Date5 THEN
                    CASE WHEN Date5 IS NULL OR Date5 > Date6 THEN
                        Date6
                    ELSE
                        Date5
                    END
                ELSE
                    CASE WHEN Date4 IS NULL OR Date4 > Date5 THEN
                        CASE WHEN Date5 IS NULL OR Date5 > Date6 THEN
                            Date6
                        ELSE
                            Date5
                        END
                    ELSE
                        CASE WHEN Date4 IS NULL OR Date4 > Date6 THEN
                            Date6
                        ELSE
                            Date4
                        END
                    END
                END
            ELSE
                CASE WHEN Date2 IS NULL OR Date2 > Date5 THEN
                    CASE WHEN Date5 IS NULL OR Date5 > Date6 THEN
                        Date6
                    ELSE
                        Date5
                    END
                ELSE
                    CASE WHEN Date2 IS NULL OR Date2 > Date6 THEN
                        Date6
                    ELSE
                        Date2
                    END
                END
            END
        END
ELSE
    CASE WHEN Date1 IS NULL OR Date1 > Date3 THEN
        CASE WHEN Date3 IS NULL OR Date3 > Date4 THEN
            CASE WHEN Date4 IS NULL OR Date4 > Date5 THEN
                CASE WHEN Date5 IS NULL OR Date5 > Date6 THEN
                    Date6
                ELSE
                    Date5
                END
            ELSE
                CASE WHEN Date4 IS NULL OR Date4 > Date6 THEN
                    Date6
                ELSE
                    Date4
                END
            END
        ELSE
            CASE WHEN Date3 IS NULL OR Date3 > Date5 THEN
                CASE WHEN Date5 IS NULL OR Date5 > Date6 THEN
                    Date6
                ELSE
                    Date5
                END
            ELSE
                CASE WHEN Date3 IS NULL OR Date3 > Date6 THEN
                    Date6
                ELSE
                    Date3
                END
            END
        END
    ELSE
        CASE WHEN Date1 IS NULL OR Date1 > Date4 THEN
            CASE WHEN Date4 IS NULL OR Date4 > Date5 THEN
                CASE WHEN Date5 IS NULL OR Date5 > Date6 THEN
                    Date6
                ELSE
                    Date5
                END
            ELSE
                CASE WHEN Date4 IS NULL OR Date4 > Date6 THEN
                    Date6
                ELSE
                    Date4
                END
            END
        ELSE
            CASE WHEN Date1 IS NULL OR Date1 > Date5 THEN
                CASE WHEN Date5 IS NULL OR Date5 > Date6 THEN
                    Date6
                ELSE
                    Date5
                END
            ELSE
                CASE WHEN Date1 IS NULL OR Date1 > Date6 THEN
                    Date6
                ELSE
                    Date1
                END
            END
        END
    END
END

Nếu bạn đi qua trang này chỉ đơn giản là muốn so sánh ngày và không quan tâm đến hiệu suất hoặc khả năng tương thích, bạn có thể sử dụng Trình xây dựng giá trị bảng, có thể được sử dụng ở bất cứ nơi nào được phép chọn (SQL Server 2008 trở lên):

Lowest =    
(
    SELECT MIN(TVC.d) 
    FROM 
    (
        VALUES
            (Date1), 
            (Date2), 
            (Date3), 
            (Date4), 
            (Date5), 
            (Date6)
    ) 
    AS TVC(d)
)

1

caseTuyên bố của bạn không hiệu quả. Bạn đang thực hiện 5 so sánh trong trường hợp xấu nhất và 2 trong trường hợp tốt nhất; trong khi đó việc tìm kiếm tối thiểu nnên làm ở hầu hết các n-1so sánh.

Đối với mỗi hàng, trung bình bạn đang thực hiện 3,5 so sánh thay vì 2. Do đó, nó mất nhiều thời gian cpu hơn và chậm. Hãy thử kiểm tra lại bằng cách sử dụng casetuyên bố dưới đây . Nó chỉ sử dụng 2 so sánh mỗi hàng và sẽ hiệu quả hơn unpivotunion all.

Select Id, 
       Case 
           When Col1 <= Col2 then case when Col1 <= Col3 Then Col1  else col3 end
            When  Col2 <= Col3 Then Col2  
            Else Col3 
            End As TheMin 
From   YourTableNameHere

Các union all phương pháp là sai trong trường hợp của bạn như bạn đang nhận được giá trị tối thiểu không cho mỗi hàng nhưng đối với toàn bộ bảng. Ngoài ra, nó sẽ không hiệu quả vì bạn sẽ quét cùng một bảng 3 lần. Khi bảng nhỏ, I / O sẽ không tạo ra nhiều khác biệt, nhưng đối với các bảng lớn thì sẽ như vậy. Không sử dụng phương pháp đó.

Unpivotlà tốt và cũng cố gắng hủy bỏ thủ công bằng cách sử dụng chéo tham gia bảng của bạn với (select 1 union all select 2 union all select 3). Nó sẽ có hiệu quả nhưunpivot .

Giải pháp tốt nhất sẽ có một cột kiên trì được tính toán, nếu bạn không gặp vấn đề về không gian. Nó sẽ thêm kích thước của hàng thêm 4 byte (tôi cho rằng bạn sẽ cóint loại), điều này sẽ làm tăng kích thước của bảng.

Tuy nhiên, không gian và bộ nhớ là vấn đề trong hệ thống của bạn và CPU không phải là không tồn tại mà sử dụng cột được tính toán đơn giản bằng cách sử dụng câu lệnh tình huống. Nó sẽ làm cho mã đơn giản hơn.


-1

Tôi đoán rằng tùy chọn đầu tiên là nhanh nhất (mặc dù nó không có vẻ rất lắt léo từ góc độ lập trình!). Điều này là do Nó xử lý chính xác N hàng (trong đó N là kích thước bảng) và không phải tìm kiếm hoặc sắp xếp như phương thức 2 hoặc 3.

Một bài kiểm tra với mẫu lớn sẽ chứng minh điểm.

Như một lựa chọn khác để xem xét (như thể bạn cần nhiều hơn!), Là tạo một chế độ xem cụ thể trên bảng của bạn. nếu kích thước bảng của bạn là từ 100 ngàn trở lên. Theo cách này, giá trị tối thiểu được tính trong khi hàng được thay đổi và toàn bộ bảng sẽ không phải xử lý với mọi truy vấn. Trong SQL Server, các khung nhìn cụ thể hóa được gọi là Khung nhìn được lập chỉ mục


-1
Create table #temp
   (
    id int identity(1,1),
    Name varchar(30),
    Year1 int,
    Year2 int,
    Year3 int,
    Year4 int
   )

   Insert into #temp values ('A' ,2015,2016,2014,2010)
   Insert into #temp values ('B' ,2016,2013,2017,2018)
   Insert into #temp values ('C' ,2010,2016,2014,2017)
   Insert into #temp values ('D' ,2017,2016,2014,2015)
   Insert into #temp values ('E' ,2016,2016,2016,2016)
   Insert into #temp values ('F' ,2016,2017,2018,2019)
   Insert into #temp values ('G' ,2016,2017,2020,2019)

   Select *, Case 
                 when Year1 >= Year2 and Year1 >= Year3 and Year1 >= Year4 then Year1
                 when Year2 >= Year3 and Year2 >= Year4 and Year2 >= Year1 then Year2
                 when Year3 >= Year4 and Year3 >= Year1 and Year3 >= Year2 then Year3
                 when Year4 >= Year1 and Year4 >= Year2 and Year4 >= Year3 then Year4  
                 else Year1 end as maxscore  
                 from #temp

Bạn không hạch toán NULL - điều đó làm cho biểu thức CASE của bạn tương đối đơn giản. Tuy nhiên, nếu ít nhất một trong các cột thực sự là NULL, thì giải pháp của bạn sẽ trả về Year1kết quả, điều này có thể không nhất thiết đúng.
Andriy M
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.