问题:在SQL脚本或存储过程中避免使用幻数或硬编码值的其他策略是什么?
考虑一个存储过程,其作用是根据其StatusID
或其他一些FK查找表或值范围来检查/更新记录的值。
考虑一个Status
表,其中ID最重要,因为它是另一个表的FK:
要避免使用的SQL脚本类似:
DECLARE @ACKNOWLEDGED tinyint
SELECT @ACKNOWLEDGED = 3 --hardcoded BAD
UPDATE SomeTable
SET CurrentStatusID = @ACKNOWLEDGED
WHERE ID = @SomeID
这里的问题是这不可移植,并且明确依赖于硬编码值。将此部署到其他具有标识插入的环境时,存在细微的缺陷。
还要根据状态的文字说明/名称来避免SELECT
:
UPDATE SomeTable
SET CurrentStatusID = (SELECT ID FROM [Status] WHERE [Name] = 'Acknowledged')
WHERE ID = @SomeID
问题:在SQL脚本或存储过程中避免使用幻数或硬编码值的其他策略是什么?
关于如何实现这一目标的其他一些想法:
bit
列(名称为'IsAcknowledged')和一组规则,其中只有一行的值为1
。这有助于找到唯一的行:SELECT ID FROM [Status] WHERE [IsAcknowledged] = 1)
答案 0 :(得分:12)
对于像状态表这样的情况,我创建了所谓的“静态”数据集。这些表包含
的数据也就是说,在创建表的同时,也使用脚本填充表,以确保值始终相同。此后,无论数据库在何处或何时,您都知道值是什么,并且可以相应地进行相应的硬编码。 (在这些情况下,我永远不会使用代理键或标识列属性。)
您不必使用数字,您可以使用字符串 - 或二进制文件或日期,或任何最简单,最简单和最合适的。 (当我可以的时候,我使用char字符串 - 而不是varchars - 例如“RCVD”,“DLVR”,“ACKN”等等,比0,2和3更容易编码硬编码值。)
此系统适用于不可扩展的值集。如果可以修改这些值(这样0不再意味着“已确认”),那么您就会遇到安全访问问题。如果您的系统中有用户可以添加新代码,那么您有一个不同且棘手的设计问题需要解决
答案 1 :(得分:10)
我最近发现魔术数字可以是implemented by views:
CREATE VIEW V_Execution_State AS
SELECT 10 AS Pending, 20 AS Running, 30 AS Done
DECLARE @state INT
SELECT @state = Pending FROM V_Execution_State
答案 2 :(得分:7)
在某种程度上,会有一些“硬编码”的价值观。消除它们的想法来自两个方面:
'Acknowledged'
而不是3
可能 会让读者的意图更加明显。为各种状态制作bit
列可能是一个好主意或坏主意;它真的只取决于数据。如果数据经历了各种“阶段”(接收,确认,审核,拒绝,接受,响应等),那么这种方法很快就会超出可行性(更不用说必须确保的烦躁过程)在任何给定时间,只有一个列设置为1)。另一方面,如果状态真的像你描述的那样简单,那么这样做可以使代码更具可读性,索引性能更好。
硬编码值中最大的禁忌值是引用其他实体的硬编码值(换句话说,硬编码相应对象的主键)。字符串'Acknowledged
'仍然是一个硬编码的值,它的含义更加透明,而不是对其他内容的引用。对我而言,归结为:如果你能(合理地)查找它,那么。如果您不能(或者从性能或可维护性的角度来看某些事情使其成为不合理的任务),那就硬编码吧。使用此功能,您可以使用Acknowledged
查找值3;你不能从其他任何东西中查找Acknowledged
。
答案 3 :(得分:3)
我就是这样做的。 (因为它比你的例子快得多)
UPDATE SomeTable
SET CurrentStatusID = [Status].[ID]
FROM SomeTable
RIGHT JOIN [Status] ON [Name] = 'Acknowledged'
WHERE SomeTable.[ID] = @SomeID
(未经测试可能有拼写错误)
答案 4 :(得分:3)
不要依赖IDENTITY来获取所有ID。例如,如果查找表的行数少于50行,则将这些查找定义为具有特定ID或使用字符串值代码是完全合理的。无论哪种情况,“硬编码”都不再是问题。
答案 5 :(得分:3)
一个想法:
CREATE FUNC dbo.CONST_ACKNOWLEDGED()
RETURNS tinyint
AS
BEGIN
RETURN 3
END
然而,只有你没有自动编号,恕我直言才有意义
答案 6 :(得分:1)
如果您的情况如上所述,IsAcknowledged位可以工作,我会走那条路。我从来没有遇到任何问题。如果你有更复杂的场景,你最终会得到十几个字段,只要你完全控制它,我就没有看到使用“幻数”的任何问题。如果您担心在移植数据库时身份列未正确映射,您可以创建另一个(非身份)唯一列,其中包含您的ID值,整数或guid或其他任何有用的列。
答案 7 :(得分:1)
如果构成域模型一部分的“状态”实体具有预定义的值,其中一些需要由存储过程以特定方式处理,那么将引用硬编码到那些特定值是完全可以的。你的代码。这里的问题是,您可能会混淆可能是您的域模型中具有含义的值的抽象键(ID标识列)。虽然可以保留您的ID标识列,但在代码中引用它时,您应该使用域实体的有意义的属性,这可以是名称,也可以是数字别名。但是,此数字别名应在您的域模型中定义,例如3表示'已确认',它不应与抽象ID字段混淆,正如您所说,它可能是某些数据库实例中的标识列。
答案 8 :(得分:0)
首先,应该避免在存储层中使用业务逻辑。
因为在使用诸如Sql Server之类的数据库时,这似乎是不可避免的,其中很多 BL 可以存在于 DB 中,我想你可能宁愿恢复使用字符串ID 而不是自动ID 。
这将比auto ID更加管理,并且可以以更好的方式处理,即使在使用实际的应用程序层时也是如此。
例如,使用.Net方法,许多唯一字符串ID可以存储在配置文件的任何位置,以及使用所选db,XML文件的其他查找。
答案 9 :(得分:0)
想象
table dbo.Status
(
Id int PK
,Description varchar
)
values
1, Received
2, Acknowledged
3, Under Review
etc
所以,只是
declare @StatusReceived int = 1
declare @StatusAcknowledged int = 2
declare @StatusUnderReview = 3
etc
正如其他人所说,这假设未设置IDENTITY。
我也常常在查找表上加入,但这会使SELECT更短,更容易阅读。
这种方法适用于自动化,所以我在一个单独的查询中生成一个完整的表,然后复制我需要的元素(不是全部)。
答案 10 :(得分:0)
我们采用了一种简单的方法:添加一个名为“代码”的列,其中包含一个字符串,一旦创建了记录,该字符串就永远不会更改。
如果将ID值硬编码,则会遇到问题-如果将某些功能(例如,对ID硬编码并假设您将使用该ID值创建对应记录的存储过程)部署到另一个数据库实例,该怎么办?已经有另一个记录使用的ID值了吗?或将您的代码部署为用于创建表和数据的SQL脚本-您不能保证每次部署都会以相同的方式生成ID值。
如果您对“名称”之类的字段进行硬编码-很有可能在某处显示“名称”。如果该字段是用户可维护的,该怎么办?如果有人决定更改Name值,则会破坏您的过程和功能。即使它不是用户可维护的,将来也会做一些更改,说“我们必须更改名称”,这将由SQL完成,破坏您的从属代码并要求搜索所有依赖项,对其进行修复并再次进行测试
但是,如果您创建一个永不更改的“代码”列,那么谁在乎“名称”值是否更改?向用户显示名称-他们可以看到他们想要的。该代码仅对开发人员可见,并且在绝对必要的情况下,他们可以稍后在代码中注明“代码值X表示Y-名称更改为请求编号Z的一部分”。
非常简单的方法。