昨天我正在查看这样的查询:
SELECT <some fields>
FROM Thing
WHERE thing_type_id = 4
......并且不得不认为这非常“可读”。什么是'4'?这是什么意思?我以前在编码语言中做了同样的事情,但是现在我会使用常量,在THING_TYPE_AVAILABLE或者某个这样的名字中使用4。没有神圣的数字,没有任何意义!
I asked about this on here并获得了如何在SQL中实现此目的的答案。
我主要是将JOINS与现有类型表一起使用,其中你有一个ID和一个代码,当没有这样的表时,可能会使用其他解决方案(不是每个数据库都是完美的......)
SELECT thing_id
FROM Thing
JOIN ThingType USING (thing_type_id)
WHERE thing_type_code IN ('OPENED', 'ONHOLD')
所以我开始在一两个查询中使用它,我的同事很快就找到了我:“嘿,你在查询中有文字代码!” “嗯,你知道,我们通常会选择这个”。
虽然我可以理解这种方法不是通常的方法(嘿,直到现在它都不适合我),它真的很糟糕吗?
以这种方式做事的利弊是什么?我的主要目标是可读性,但我担心性能,并想确认这个想法是否合理。
编辑:请注意,我不是在讨论PL / SQL,而是直接查询,通常以SELECT开头。
编辑2: 为了进一步澄清我的假(但结构相似)的例子,这里有我的表格:
Thing
------------------------------------------
thing_id | <attributes...> | thing_type_id
1 3
4 7
5 3
ThingType
--------------------------------------------------
thing_type_id | thing_type_code | <attributes...>
3 'TYPE_C'
5 'TYPE_E'
7 'TYPE_G'
thing_type_code与thing_type_id一样独特。它目前也被用作显示字符串,这在我看来是个错误,但是现在可以通过添加thing_type_label字段复制thing_type_code来轻松修复,并且如果需要可以在以后随时更改。
据说,使用thing_type_code ='TYPE_C'进行过滤,我肯定会得到恰好是thing_type_id = 3的那一行。连接可以(并且很可能应该)仍然使用数字ID。
答案 0 :(得分:3)
主键值应不在查询中编码为文字。
原因是:
由于这些原因,在查询中编码文字ID很脆弱。
编码'OPENED'
和'ONHOLD'
等数据文字是很好的做法,因为这些值在所有服务器和环境中都是一致的。如果它们确实发生了变化,那么更改查询将成为更改脚本的一部分。
答案 1 :(得分:1)
我假设问题是关于查询的两个版本 - 一个是数字比较,另一个是连接和字符串比较。
您的同事是正确的,where thing_id in (list of ids)
表单的效果优于join
。但是,如果thing_id
未编入索引,则性能差异可能非常小。该查询将需要在原始表上进行全表扫描。
在大多数其他方面,使用join
的版本更好。特别是,它使查询的目的更加清晰,并使整个查询更易于维护。对于小型参考表,性能损失可能不明显。事实上,在某些数据库中,这种形式可能更快。当in
被评估为一系列or
表达式时,会发生这种情况。如果列表很长,则执行索引查找可能会更快。
join
方法有一个缺点。如果列中的值发生更改,则还需要更改代码。如果您建议使用主键的同事有这种经验,我不会感到惊讶。他/她正在开发一个应用程序并使用join
来构建它。大。很多代码。全清。一切都可维护。然后每周,用户决定更改代码的定义。这可以使几乎任何理智的人更喜欢使用主键而不是使用参考表。
答案 2 :(得分:1)
请参阅标记评论。我认为你没事,但可以给我2美分。 如果该值在一个查询的范围内,我想写下这个可读的方式:
declare HOLD int = 4
SELECT <some fields>
FROM Thing
WHERE thing_type_id = HOLD
如果在多个点(查询,SP,视图等)中多次使用这些值 我创建了一个域表。
create table ThingType (id int not null primary key, varchar(50) description)
GO
insert into ThingType values (4,'HOLD'),(5, 'ONHOLD')
GO
这样我可以在我的选择中重用这些类型作为枚举器
declare TYPE int
set TYPE = (select id from ThingType where description = 'HOLD')
SELECT <some fields>
FROM Thing
WHERE thing_type_id = TYPE
这样我保持意义和性能(并且还可以强制域值的关系完整性)
此外,我可以在应用程序级别使用枚举器,只需将数值传递给查询。对调查员生病的快速一瞥给了我这个数字意义。
答案 3 :(得分:0)
在SQL查询中,您肯定会为JOIN引入性能命中(实际上在SQL服务器内发生了多次查询)。问题在于性能影响是否足以抵消其效益。
如果它只是一个可读性的东西,那么你可能更愿意寻求更好的性能并避免使用JOIN,但我建议你考虑潜在的完整性问题(例如,如果你的例子中的类型值4改变了,会发生什么?另一个进程 - 整个应用程序可能会失败)。
如果值永远不会改变,那么使用PK - 这是您作为开发人员的决定 - 没有规则。一个选项可能最适合一个查询而不适用于另一个查询。
答案 4 :(得分:0)
对于PL / SQL,在包中定义常量是有意义的,例如
DECLARE
C_OPENED CONSTANT NUMBER := 3;
C_ONHOLD CONSTANT NUMBER := 4;
BEGIN
SELECT <some fields>
INTO ...
FROM Thing
WHERE thing_type_id in (C_OPENED, C_ONHOLD);
END;
有时创建全局包(没有主体)是有用的,其中定义了所有常用的常量。如果文字发生变化,您只需要在一个地方修改常量定义。