在 SQL Server 2005 上获得最少多列的最有效方法是什么?

Rac*_*hel 29 performance sql-server-2005 sql-server aggregate

我想从 6 列中获取最小值。

到目前为止,我已经找到了三种方法来实现这一点,但我担心这些方法的性能,并想知道哪种方法对性能更好。

第一种方法是使用大 case 语句。这是一个包含 3 列的示例,基于上面链接中的示例。我的案例陈述会更长,因为我将查看 6 列。

Select Id,
       Case When Col1 <= Col2 And Col1 <= Col3 Then Col1
            When Col2 <= Col3 Then Col2 
            Else Col3
            End As TheMin
From   MyTable
Run Code Online (Sandbox Code Playgroud)

第二种选择是将UNION运算符与多个选择语句一起使用。我会把它放在一个接受 Id 参数的 UDF 中。

select Id, dbo.GetMinimumFromMyTable(Id)
from MyTable
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

我发现的第三个选项是使用 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
Run Code Online (Sandbox Code Playgroud)

由于表的大小以及查询和更新该表的频率,我担心这些查询对数据库的性能影响。

这个查询实际上将用于连接到一个有几百万条记录的表,但是返回的记录将减少到一次大约一百条记录。它将在一天中多次运行,并且我查询的 6 列经常更新(它们包含每日统计数据)。我认为我查询的 6 列上没有任何索引。

在尝试获取多列中的最小值时,这些方法中哪一种对性能更好?或者还有另一种我不知道的更好的方法吗?

我正在使用 SQL Server 2005

样本数据和结果

如果我的数据包含这样的记录:

编号 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

最终结果应该是

标识值
1 0
2 2
3 1
4 4

Rac*_*hel 22

我测试了所有 3 种方法的性能,这是我发现的:

  • 1 条记录:无明显差异
  • 10 条记录:无明显差异
  • 1,000 条记录:无明显差异
  • 10,000 条记录:UNION子查询有点慢。该CASE WHEN查询比快一点UNPIVOT的。
  • 100,000 条记录:UNION子查询明显变慢,但UNPIVOT查询变得比CASE WHEN查询快一点
  • 500,000 条记录:UNION子查询仍然慢得多,但UNPIVOTCASE WHEN查询快得多

所以最终结果似乎是

  • 对于较小的记录集,似乎没有足够的区别。使用最容易阅读和维护的内容。

  • 一旦开始进入更大的记录集,UNION ALL与其他两种方法相比,子查询开始表现不佳。

  • CASE语句在某个点(在我的情况下,大约 100k 行)之前执行最佳,并且该UNPIVOT查询成为性能最佳的查询

由于您的硬件、数据库架构、数据和当前服务器负载,一个查询变得比另一个更好的实际数量可能会发生变化,因此如果您担心性能,请务必使用您自己的系统进行测试。

我还使用Mikael 的答案进行了一些测试;但是,对于大多数记录集大小,它比此处尝试的所有其他 3 种方法都慢。唯一的例外是它比UNION ALL非常大的记录集大小的查询做得更好。我喜欢它除了显示最小值之外还显示列名的事实。

我不是 dba,所以我可能没有优化我的测试并遗漏了一些东西。我正在使用实际的实时数据进行测试,因此可能会影响结果。我试图通过将每个查询运行几次不同的时间来解决这个问题,但你永远不知道。如果有人写了一个干净的测试并分享他们的结果,我肯定会感兴趣。


Mik*_*son 6

不知道什么是最快的,但你可以尝试这样的事情。

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
Run Code Online (Sandbox Code Playgroud)

结果:

ColName ColValue
------- -----------
Col1    1
Col3    1
Run Code Online (Sandbox Code Playgroud)

如果您对具有最小值的列不感兴趣,可以改用它。

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
Run Code Online (Sandbox Code Playgroud)

一个简化的逆透视查询。

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


Jon*_*gel 6

添加一个使用CASE语句执行所需逻辑的持久计算列。

当您需要根据该值进行连接(或其他任何操作)时,该最小值将始终有效可用。

该值将被任何的源值的变化每次重新计算(INSERT/ UPDATE/ MERGE)。我不是说这是必然的工作量的最佳解决方案,我只是提供它作为一个解决方案,就像其他的答案。只有 OP 才能确定哪个最适合工作负载。