子串连接或附加表,哪个更快?

Spe*_*und 6 sql-server sql-server-2014

我有一个在工作中经常出现的案例。我有很多表使用 4 个字符串作为外键:G191

TheG是一种类别,the19是年份,the1是一个实例。获取类别的所有行G是我们经常做的事情。通常喜欢:

SELECT * FROM [Table] 
WHERE Left([ID], 1) = 'G'
Run Code Online (Sandbox Code Playgroud)

有一种方法可以在不操作字符串的情况下获得这种效果,方法是加入定义了该类别的表:

SELECT * FROM [Table]
JOIN [Categories] ON [Table].CategoryID = [Categories].CategoryID
WHERE [Categories].Letter = 'G'
Run Code Online (Sandbox Code Playgroud)

我的同事坚持认为第一种方法的性能更高,并且因为我采用第二种方法而对我翻白眼。

哪一个更好?通过另一个表加入是否真的比检查字符串的第一个字符增加了更多的工作?

Eri*_*ing 16

为什么你的更好

一般来说,你的模式是一个更好的主意。

不过,性能将取决于索引、谓词选择性和表大小。

你的模式是一个更好的主意的原因归结为 SARGability 的概念,如果你的搜索参数可以用作搜索谓词,甚至完全推送谓词,这是一个花哨的词(即在访问一个指数)。

这可能会在连接和 where 子句中造成伤害的一些示例是:

  • 功能(列)=东西
  • 列 + 列 = 某物
  • 列 + 值 = 某物
  • 列 = @something 或 @something IS NULL
  • 像“%something%”这样的列
  • 列 = 情况…

当你做这样的事情时,你的查询最终可能会产生各种不良的副作用:

  • 增加CPU(烧宝贝烧)
  • 索引扫描(当您可以进行搜索时)
  • 隐式转换(如果您的谓词产生不同的数据类型)
  • 差的基数估计(戳眼中的优化器)
  • 不合适的计划选择(因为优化器现在是盲目的,你这个混蛋)
  • 长时间运行的查询(是的工作保障!)

更好的选择

您正在寻找的 SARGable 选项包括:

WHERE [ID] LIKE 'G%'

或者

WHERE [ID] >= 'G' AND [ID] < 'H'

另一种解决方案是在您正在搜索的表中添加一个计算列:

ALTER TABLE [Table] 
    ADD Lefty AS Left([ID], 1);

CREATE INDEX ix_whatever 
    ON [Table] (CategoryID , Lefty);
Run Code Online (Sandbox Code Playgroud)

虽然就像我之前说的,对于较小的表,性能差异可能并不显着。

也可能不会使用此索引,因为您的示例查询选择了所有表列,而此索引未涵盖它们。但那是另一天的故事。