select 案例表现
select case performance
假设我们有这个 table:
|------------------------------|
| col | data type |
|------------------------------|
| id | bigint |
| first_name | nvarchar |
| last_name | nvarchar |
| photo | binary |
|------------------------------|
好的,现在我们需要两个查询 table:
- 列出没有照片列的所有 table 行(将其加载为 null)
- 加载带有照片列的特定行
为此我们要使用 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:
我想知道第一个查询是否总是不区分大小写执行?因为它正在检查一个不更改值且与任何列值无关的变量。
你可以试试这个,它避免了 CASE
和 IF
。不过,我承认这有点神秘。
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
方法。似乎是最安全的方式。
假设我们有这个 table:
|------------------------------|
| col | data type |
|------------------------------|
| id | bigint |
| first_name | nvarchar |
| last_name | nvarchar |
| photo | binary |
|------------------------------|
好的,现在我们需要两个查询 table:
- 列出没有照片列的所有 table 行(将其加载为 null)
- 加载带有照片列的特定行
为此我们要使用 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:
我想知道第一个查询是否总是不区分大小写执行?因为它正在检查一个不更改值且与任何列值无关的变量。
你可以试试这个,它避免了 CASE
和 IF
。不过,我承认这有点神秘。
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
方法。似乎是最安全的方式。