在下面的示例中,假设我们在表上有一个完整的“状态”分类,选项似乎是:
>只是硬编码,可能只是’评论’状态
-- StatusId 87 = Loaded SELECT ... FROM [Table] WHERE StatusId = 87;
>使用状态查找表,然后加入此表,以便WHERE子句引用友好名称.
子查询:
SELECT ... FROM [Table] WHERE StatusId = (SELECT StatusId FROM TableStatus WHERE StatusName = 'Loaded');
或加入
SELECT ... FROM [Table] t INNER JOIN TableStatus ts On t.StatusId = ts.StatusId WHERE ts.StatusName = 'Loaded';
>定义了一堆标量UDF,返回常量,即
CREATE Function LoadedStatus() RETURNS INT AS BEGIN RETURN 87 END;
接着
SELECT ... FROM [Table] WHERE StatusId = LoadedStatus();
(IMO会导致数据库中出现大量污染 – 在Oracle软件包中可能没问题)
>使用表值函数的类似模式将常量值保存为行或列,这些模式是CROSS APPLIED返回[Table]
编辑:赏金 – 根据Remus的回答和评论,有没有人在DBProj DDL / Schema脚本中维护$(变量)的最佳实践方法?
解决方法
使用优化程序在计划生成时检查的常量与使用任何形式的间接(UDF,JOIN,子查询)之间的执行计划的后果通常是戏剧性的. sql’编译’是一个非同寻常的过程(在某种意义上,不像IL代码生成那样’普通’),因为结果不仅由编译的语言结构(即查询的实际文本)决定,而且还由通过数据模式(现有索引)和那些索引中的实际数据(统计数据).当使用硬编码值时,优化器可以提供更好的计划,因为它可以根据索引统计信息实际检查值并获得结果的估计值.
另一个考虑因素是sql应用程序不仅仅是代码,而是大量的代码和数据. “重构”sql程序是……不同的.在C#程序中,可以更改常量或枚举,重新编译并愉快地运行应用程序,在sql中不能这样做,因为该值可能存在于数据库中的数百万条记录中,并且更改常量值意味着还要更改数据的GB,经常在线进行新的操作.
仅仅因为在服务器看到的查询和过程中对值进行了硬编码并不一定意味着必须在原始项目源代码中对值进行硬编码.有各种代码生成工具可以处理这个问题.考虑一下像利用sqlcmd scripting variables一样微不足道的事情:
defines.sql:
:setvar STATUS_LOADED 87
somesource.sql:
:r defines.sql SELECT ... FROM [Table] WHERE StatusId = $(STATUS_LOADED);
someothersource.sql:
:r defines.sql UPDATE [Table] SET StatusId = $(STATUS_LOADED) WHERE ...;