select 案例表现

select case performance

假设我们有这个 table:

|------------------------------|
| col           | data type    | 
|------------------------------|
| id            | bigint       |
| first_name    | nvarchar     |
| last_name     | nvarchar     |
| photo         | binary       |
|------------------------------|

好的,现在我们需要两个查询 table:

为此我们要使用 SP

第一种方法是这样的:

SELECT Top 50
    id,
    first_name,
    last_name,
    CASE WHEN @id IS NULL THEN null ELSE photo END as photo
FROM MyTable
WHERE @id IS NULL OR id=@id

第二种方法是:

IF (@id IS NULL)
    SELECT Top 50
        id,
        first_name,
        last_name,
        null as photo
    FROM MyTable
ELSE
    SELECT 
        id,
        first_name,
        last_name,
        photo
    FROM MyTable
    WHERE id=@id
END

第二种方法显然检查一次 if 条件并完成工作。

但我喜欢像第一种方法一样编写我的 SP,我不知道 sql 服务器是否会检查每一行的 CASE WHEN @id IS NULL 或查询优化器是否会在没有的情况下优化查询CASE?

编辑 1:

我想知道第一个查询是否总是不区分大小写执行?因为它正在检查一个不更改值且与任何列值无关的变量。

你可以试试这个,它避免了 CASEIF。不过,我承认这有点神秘。

SELECT Top 50
       id,
       first_name,
       last_name,
       null as photo
FROM   MyTable
WHERE  @id IS NULL
UNION ALL
SELECT id,
       first_name,
       last_name,
       photo
FROM   MyTable
WHERE  id = @id

执行此操作的标准方法(将 null 参数默认为所有)是​​这样的:

SELECT 
    id,
    first_name,
    last_name,
    photo
FROM MyTable
WHERE id=COALESCE(@id,id)

根据我的经验,如果 ID

上有索引,这会在所有主要平台上提供良好的性能

由于发布了一些答案,我决定自己检查一下。

在 SQL Server 2014

上安装
-- table is not identical to yours, but it should do.
create table t1 (
  id int identity primary key,
  first_name varchar(50),
  last_name varchar(50)
)
go

-- insert ~10,000,000 rows of randomly generated data.
with cte as (
  select 1 as rn, newid() as first_name, newid() as last_name
  union all
  select t.rn + 1 as rn, newid() as first_name, newid() as last_name
    from cte t
   where t.rn < 10000000
)
insert into t1 (first_name, last_name)
select first_name, last_name
from cte
option (maxrecursion 0)
go

update statistics
go

查询 #1 - OP 的统一查询方法:

declare @id int = 5000000

SELECT Top 50
    id,
    first_name,
    last_name
FROM t1
WHERE @id IS NULL OR id = @id
go

执行时间:13秒

执行计划:

查询 #2 - ELSE 子句中的 OP 查询:

declare @id int = 5000000

SELECT 
   id,
   first_name,
   last_name
FROM t1
WHERE id=@id
go

执行时间:0秒

执行计划:

查询 #3 - @Ann 的查询:

declare @id int = 5000000

SELECT Top 50
       id,
       first_name,
       last_name
FROM   t1
WHERE  @id IS NULL
UNION ALL
SELECT id,
       first_name,
       last_name
FROM   t1
WHERE  id = @id
go

执行时间:0秒

执行计划:

查询 #4 - @Hogan 的查询:

declare @id int = 5000000

SELECT Top 50
       id,
       first_name,
       last_name
FROM   t1
WHERE  id = COALESCE(@id, id)
go

执行时间:14秒

执行计划:

结论

您和 Hogan 在单个查询中统一逻辑的尝试都没有在 @id 具有值的情况下进行单独查询的效果好。在这两种情况下,您都可以看到查询基本上扫描了整个聚簇索引,而不是执行更直接的索引查找。

有趣的是,尽管 Ann 的执行计划似乎是所有执行计划中最复杂的,但性能表明优化器能够以某种方式检测到 @id 不为 null 并使昂贵的集群索引扫描短路那种情况。

不过,由于您已经在存储过程中,在我看来您应该坚持使用 if-else 方法。似乎是最安全的方式。