使用JOIN来避免数字ID是一件坏事吗?

时间:2014-02-20 12:50:31

标签: sql oracle join

昨天我正在查看这样的查询:

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。

5 个答案:

答案 0 :(得分:3)

主键值应在查询中编码为文字。

原因是:

  • 关系理论认为PK不应该传达任何意义。甚至没有特定的身份。它们应该是严格的行标识符,而不是依赖于特定值
  • 由于操作原因,PK在不同环境(例如dev,qa和prod)中通常是不同的,即使对于“查找”表也是如此

由于这些原因,在查询中编码文字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;

有时创建全局包(没有主体)是有用的,其中定义了所有常用的常量。如果文字发生变化,您只需要在一个地方修改常量定义。