避免把判斷處理放入 WHERE 條件

2021-08-22 04:04:24 字數 4695 閱讀 4638

問題描述

業務需求如下:

有表a,在查詢的時候,需要根據標誌確定是查詢大於某個值的記錄,還是小於某個值的記錄

a、一般的處理方法

if@a = 0

select [trannumber] from a

where [trannumber] < 10000

else

if @a = 1

select [trannumber] from a

where [trannumber] > 10000

b、一句的處理方法

select

[trannumber] from a

where

(@a = 0 and [trannumber] < 10000)

or(@a = 1 and [trannumber] > 10000)

分析

從語句的簡捷性來看,方法b具有技巧性,它們兩者之間,究竟那乙個更好呢?你可能會從效能上來評估,以決定到底用那一種。單純從語句上來看,兩者的效率差別應該不會非常大,實際測試的結果會如我們想象嗎?繼續往下看

建立測試環境(注,此測試環境是為幾個主題服務的,因此結構看起來有些怪異)

usetempdb go

setnocounton

--***********************************===

--建立測試環境

--***********************************===

raiserror

('建立測試環境'

,10, 1)

with nowait

-- table a

create

table [dbo].a(

[trannumber] [int] identity

(1, 1)

notnull,

[invno] [char](8)

notnull,

[item] [char](15)

null

default(''

), primary

key([trannumber]) )

create

index [indexoninvno] on [dbo].a([invno])

create

index [indexonitem] on [dbo].a ([item])

create

index [indexoniteminnvo] on [dbo].a([invno], [item]) go

--***********************************===

--生成測試資料

--***********************************===

raiserror

('生成測試資料'

,10, 1)

with nowait

insert

[dbo].a([invno], [item])

select

left(

newid

(), 8),

right(

newid

(), 15)

from

syscolumns a, syscolumns b go

進行效能測試

declare

@a int

set@a = 0

declare

@t table

(id int

identity

,a int

, b int

)declare

@dt datetime

, @loop int

, @id int

set@loop = 1

while

@loop < 10

begin

set @loop = @loop + 1

raiserror

('test %d'

, 10, 1, @loop)

with nowait

set @dt =

getdate

()if @a = 0

select

*from a

where [trannumber] < 10000

else

if @a = 1

select

*from a

where [trannumber] > 10000

insert @t(a)

values

(datediff

(ms, @dt,

getdate

()))

select @id =

scope_identity

(), @dt =

getdate

()select

*from a

where

(@a = 0 and [trannumber] < 10000)

or(@a = 1 and [trannumber] > 10000)

update @t set b =

datediff

(ms, @dt,

getdate

())where id = @id

endselect

*from @t

union

allselect

null,

sum(a),

sum(b)

from @t

效能測試結果

id a b

----------- ----------- -----------

1 173 173

2 140 170

3 140 173

4 126 170

5 140 173

6 140 173

7 123 170

8 190 170

9 123 190

null 1295 1562

從結果看,兩者有一定效能差異,但還算是在可接受範圍內吧

還有其他問題嗎?

除了效能外,另乙個要考慮的問題是block的問題,下面的測試來反映block的影響

block的測試為表a加鎖(查詢視窗a)

-- run query windows 1

begin

tran

update a set [item] =

right(

newid

(), 4)

where [trannumber] < 100

--rollback tran

block的測試測試查詢方法a(查詢視窗b)

-- run query windows 2

declare

@a int

set@a = 1

if@a = 0

select

*from a

where [trannumber] < 10000

else

if @a = 1

select

*from a

where [trannumber] > 10000

block的測試測試查詢方法b(查詢視窗c)

-- run query windows 3

declare

@a int

set@a = 1

select

*from a

where

(@a = 0 and [trannumber] < 10000)

or(@a = 1 and [trannumber] > 10000)

結果

你會看到,查詢視窗b中的查詢會及時地完成,而查詢視窗c的查詢會一直等待,你可以通過執行儲存過程sp_who2,檢視當前的block狀況來確定查詢視窗c的查詢是否被查詢視窗a的查詢block住

結論

不要使用查詢方法b,它看起來很棒,實際的結果是效能不太好,而且會增加被block的機會

避免把判斷處理放入 WHERE 條件

業務需求如下 有表a 在查詢的時候,需要根據標誌確定是查詢大於某個值的記錄,還是小於某個值的記錄 if a 0 select trannumber from a where trannumber 10000 else if a 1 select trannumber from a where tra...

避免把判斷處理放入 WHERE 條件

問題描述 業務需求如下 有表a,在查詢的時候,需要根據標誌確定是查詢大於某個值的記錄,還是小於某個值的記錄 a 一般的處理方法 if a 0 select trannumber from a where trannumber 10000 else if a 1 select trannumber f...

把SQL Server放入保險箱

安全模式簡介 從系統結構上來講sql server有兩種安全模式。第一種是 僅windows 模式,這種模式只允許擁有受信任的windows nt賬戶的使用者登入,是sql server預設的安全模式,也是較安全的選項,使用者登入sql server的前提是該使用者使用windows nt的域賬戶登...