如果我有条件地 select 更多列,为什么这个 SQL 语句的执行时间会增加?

Why does this SQL statement increase in execution time if I conditionally select more columns?

我有一个脚本,其中包含多个写入临时表的查询。每个查询都建立在前一个查询的基础上,获取前一个查询的结果并对其执行其他操作。脚本在写入 #Data2.

后 1 秒内运行

根据名为 @show 的用户参数,有条件地 select 写入 #Data2 的查询之后的下一个查询。当我有条件地 select 只有 1 列(见下文)时,它会在大约 1 秒内运行。

select
    (case when 1 in (select use_object from dbo.DelimitedSplit8K(@show,','))  
             then d.vend_num            
             else null 
     end) vend_num
into 
    #Show
from
    #Data2 d

但是, 当我对 53 列执行此操作时,查询执行时间变为 1 分 39 秒(见下文)。为什么是这样?理论上,查询应该只决定包含或不包含该列。为什么这在计算上会很昂贵?

select

      (case when 1 in(select use_object from dbo.DelimitedSplit8K(@show,','))  then d.vend_num          else null end)          vend_num
    , (case when 2 in(select use_object from dbo.DelimitedSplit8K(@show,','))  then d.terms_code        else null end)          terms_code
    , (case when 3 in(select use_object from dbo.DelimitedSplit8K(@show,','))  then d.last_purch        else null end)          last_purch
    , (case when 4 in(select use_object from dbo.DelimitedSplit8K(@show,','))  then d.purch_ytd         else null end)          purch_ytd
    , (case when 5 in(select use_object from dbo.DelimitedSplit8K(@show,','))  then d.pay_ytd           else null end)          pay_ytd

    , (case when 6 in(select use_object from dbo.DelimitedSplit8K(@show,','))  then d.vend_remit        else null end)          vend_remit
    , (case when 7 in(select use_object from dbo.DelimitedSplit8K(@show,','))  then d.curr_code         else null end)          curr_code
    , (case when 8 in(select use_object from dbo.DelimitedSplit8K(@show,','))  then d.bank_code         else null end)          bank_code
    , (case when 9 in(select use_object from dbo.DelimitedSplit8K(@show,','))  then d.pay_type          else null end)          pay_type
    , (case when 10 in(select use_object from dbo.DelimitedSplit8K(@show,',')) then d.pur_acct          else null end)          pur_acct

    , (case when 11 in(select use_object from dbo.DelimitedSplit8K(@show,',')) then d.vendname          else null end)          vendname
    , (case when 12 in(select use_object from dbo.DelimitedSplit8K(@show,',')) then d.fax_num           else null end)          fax_num
    , (case when 13 in(select use_object from dbo.DelimitedSplit8K(@show,',')) then d.telex_num         else null end)          telex_num
    , (case when 14 in(select use_object from dbo.DelimitedSplit8K(@show,',')) then d.pay_hold          else null end)          pay_hold
    , (case when 15 in(select use_object from dbo.DelimitedSplit8K(@show,',')) then d.pay_hold_date     else null end)          pay_hold_date

    , (case when 16 in(select use_object from dbo.DelimitedSplit8K(@show,',')) then d.pay_hold_reason   else null end)          pay_hold_reason
    , (case when 17 in(select use_object from dbo.DelimitedSplit8K(@show,',')) then d.sitename          else null end)          sitename
    , (case when 18 in(select use_object from dbo.DelimitedSplit8K(@show,',')) then d.siteaddress       else null end)          siteaddress
    , (case when 19 in(select use_object from dbo.DelimitedSplit8K(@show,',')) then d.city              else null end)          city
    , (case when 20 in(select use_object from dbo.DelimitedSplit8K(@show,',')) then d.state             else null end)          state

    , (case when 21 in(select use_object from dbo.DelimitedSplit8K(@show,',')) then d.zip               else null end)          zip
    , (case when 22 in(select use_object from dbo.DelimitedSplit8K(@show,',')) then d.county            else null end)          county
    , (case when 23 in(select use_object from dbo.DelimitedSplit8K(@show,',')) then d.country           else null end)          country
    , (case when 24 in(select use_object from dbo.DelimitedSplit8K(@show,',')) then d.FSMAApproved      else null end)          FSMAApproved
    , (case when 25 in(select use_object from dbo.DelimitedSplit8K(@show,',')) then d.FSMAAuditDatetime else null end)          FSMAAuditDatetime

    , (case when 26 in(select use_object from dbo.DelimitedSplit8K(@show,',')) then d.FSMAAuditEditor   else null end)          FSMAAuditEditor
    , (case when 27 in(select use_object from dbo.DelimitedSplit8K(@show,',')) then d.vend_seq          else null end)          vend_seq
    , (case when 28 in(select use_object from dbo.DelimitedSplit8K(@show,',')) then d.interaction_id    else null end)          interaction_id
    , (case when 29 in(select use_object from dbo.DelimitedSplit8K(@show,',')) then d.interaction_stat  else null end)          interaction_stat
    , (case when 30 in(select use_object from dbo.DelimitedSplit8K(@show,',')) then d.topic             else null end)          topic

    , (case when 31 in(select use_object from dbo.DelimitedSplit8K(@show,',')) then d.Uf_CheckLevel     else null end)          Uf_CheckLevel
    , (case when 32 in(select use_object from dbo.DelimitedSplit8K(@show,',')) then d.approbation_type  else null end)          approbation_type
    , (case when 33 in(select use_object from dbo.DelimitedSplit8K(@show,',')) then d.submitted_date    else null end)          submitted_date
    , (case when 34 in(select use_object from dbo.DelimitedSplit8K(@show,',')) then d.approver          else null end)          approver
    , (case when 35 in(select use_object from dbo.DelimitedSplit8K(@show,',')) then d.approval_date     else null end)          approval_date   

    , (case when 36 in(select use_object from dbo.DelimitedSplit8K(@show,',')) then d.rejector          else null end)          rejector
    , (case when 37 in(select use_object from dbo.DelimitedSplit8K(@show,',')) then d.rejection_date    else null end)          rejection_date
    , (case when 38 in(select use_object from dbo.DelimitedSplit8K(@show,',')) then d.vendstat          else null end)          vendstat
    , (case when 39 in(select use_object from dbo.DelimitedSplit8K(@show,',')) then d.po_num            else null end)          po_num
    , (case when 40 in(select use_object from dbo.DelimitedSplit8K(@show,',')) then d.po_orderdate      else null end)          po_orderdate

    , (case when 41 in(select use_object from dbo.DelimitedSplit8K(@show,',')) then d.po_stat           else null end)          po_stat
    , (case when 42 in(select use_object from dbo.DelimitedSplit8K(@show,',')) then d.po_invnum         else null end)          po_invnum
    , (case when 43 in(select use_object from dbo.DelimitedSplit8K(@show,',')) then d.po_invdate        else null end)          po_invdate
    , (case when 44 in(select use_object from dbo.DelimitedSplit8K(@show,',')) then d.poi_line          else null end)          poi_line
    , (case when 45 in(select use_object from dbo.DelimitedSplit8K(@show,',')) then d.poi_release       else null end)          poi_release

    , (case when 46 in(select use_object from dbo.DelimitedSplit8K(@show,',')) then d.poi_item          else null end)          poi_item
    , (case when 47 in(select use_object from dbo.DelimitedSplit8K(@show,',')) then d.poi_stat          else null end)          poi_stat
    , (case when 48 in(select use_object from dbo.DelimitedSplit8K(@show,',')) then d.qty_ordered       else null end)          qty_ordered
    , (case when 49 in(select use_object from dbo.DelimitedSplit8K(@show,',')) then d.qty_received      else null end)          qty_received
    , (case when 50 in(select use_object from dbo.DelimitedSplit8K(@show,',')) then d.due_date          else null end)          due_date

    , (case when 51 in(select use_object from dbo.DelimitedSplit8K(@show,',')) then d.rcvd_date         else null end)          rcvd_date
    , (case when 52 in(select use_object from dbo.DelimitedSplit8K(@show,',')) then d.poi_itemdesc      else null end)          poi_itemdesc
    , (case when 53 in(select use_object from dbo.DelimitedSplit8K(@show,',')) then d.u_m               else null end)          u_m

into #Show

from

    #Data2 d

您可以通过更简单的方式完成此操作,而无需涉及另一个临时 table 并且无需调用此昂贵的函数 53 次:

DECLARE @show varchar(255) = '1,3';

SELECT vend_num   = MIN(CASE y.value WHEN 1 THEN d.vend_num   END),
       terms_code = MIN(CASE y.value WHEN 2 THEN d.terms_code END),
       last_purch = MIN(CASE y.value WHEN 3 THEN d.last_purch END)--,
       -- ...
FROM #Data2 AS d 
OUTER APPLY dbo.DelimitedSplit8K(@show,',') AS y;

当然我同意上面的评论,你应该考虑 table-valued parameters 而不是传递逗号分隔的字符串;那么你就可以加入反对 TVP 而不是进行任何昂贵的分裂。

如果您真的想使用一个函数,您应该查看其中几个拆分函数的性能测试,并可能考虑一个更好的替代方案(参见 this and this)。

在 SQL Server 2016(和 Azure SQL 数据库)中,您将能够使用 STRING_SPLIT(), which is a surprisingly well-performing native option. See this, this, this, and this.