传递大量逗号分隔值时用自定义表类型替代SQL IN子句是否更优?
结论
优先选择使用自定义TABLE TYPE参数的存储过程1,不管是性能、稳定性还是可维护性都远优于存储过程2的字符串匹配方案。
具体原因
索引利用效率差距极大
存储过程2的
CHARINDEX逻辑需要对SKUs.SKUID列做类型转换(INT转VARCHAR)和函数计算,会直接导致SKUID上的索引完全失效,只能走全表扫描,当SKUs表数据量较大时性能会暴跌,额外的多JOIN逻辑还会进一步放大这个性能损耗。
而存储过程1是INT类型的等值JOIN,SQL Server优化器可以直接使用SKUID上的聚集或非聚集索引,查询效率提升非常明显。完全规避长度限制问题
你遇到的IN子句长度限制、字符串超长截断报错的问题,使用表类型参数完全不存在,哪怕传入几万、几十万条ID都可以正常支持,不需要处理字符串拼接、转义、超长截断等问题,不会出现因为字符串处理异常导致的查询结果错误。
运行开销更低
存储过程2需要对每一行
SKUID做类型转换和字符串匹配,CPU开销非常高,当匹配的ID数量较多时,这个开销会被数倍放大。而表类型参数本身以INT类型存储数据,等值匹配不需要额外的类型转换和字符串运算,CPU占用要低很多。执行计划更稳定
你可以给自定义类型
INTList的ID列添加主键约束,SQL Server优化器可以拿到准确的行数统计信息,在有多JOIN逻辑的场景下,准确的基数估算可以避免优化器生成错误的执行计划。而字符串匹配的方式优化器完全无法估算匹配行数,执行计划出错的概率极高。可维护性更高
不需要处理字符串的边界问题,比如首尾逗号、连续逗号、非法字符等,这些问题都会导致存储过程2的查询结果出错,而表类型参数完全没有这类问题,传入参数只需要往表变量里插入ID即可,不需要额外的字符串处理逻辑。
补充:如果受限于老系统架构没法修改参数类型,也建议先把
@params字符串拆分到临时表/表变量后再做JOIN,性能也会远优于直接用CHARINDEX匹配。
内容的提问来源于stack exchange,提问作者Srijon Chakraborty

