选择案例表现

时间:2016-08-17 17:22:04

标签: sql sql-server database rdbms

假设我们有这个表:

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

好了,现在我们想要对这个表进行两次查询:

  • 列出没有照片col的所有表格行(将其加载为null)
  • 使用照片col加载特定行

为此,我们要使用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 server是否要检查每行的CASE WHEN @id IS NULL,或者查询优化器是否会在没有CASE的情况下优化查询?

编辑1:

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

3 个答案:

答案 0 :(得分:2)

你可以试试这个,这可以避免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

答案 1 :(得分:1)

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

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秒

执行计划:

enter image description here

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

declare @id int = 5000000

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

执行时间:0秒

执行计划:

enter image description here

查询#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秒

执行计划:

enter image description here

查询#4 - @ Hogan的查询:

declare @id int = 5000000

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

执行时间:14秒

执行计划:

enter image description here

结论

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

有趣的是,虽然Ann的执行计划似乎是最复杂的,但性能表明优化器能够以某种方式检测到@id不为空并且使昂贵的集群索引短路在这种情况下扫描。

尽管如此,由于您已经在存储过程中,我觉得您应该坚持使用if-else方法。似乎是最安全的方式。

答案 2 :(得分:0)

执行此操作的标准方法(将null参数默认为all)如下所示:

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

根据我的经验,如果ID上有索引

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