我的T-SQL老师告诉我们,在没有任何进一步说明的情况下,将PK列“ Id”命名为不良做法。
为什么命名表PK列“ Id”被认为是不良做法?
我的T-SQL老师告诉我们,在没有任何进一步说明的情况下,将PK列“ Id”命名为不良做法。
为什么命名表PK列“ Id”被认为是不良做法?
Answers:
我要说出来:这并不是一个不好的做法(即使是这样,也不是那么糟糕)。
您可以使参数(如Chad指出的那样)可以掩盖错误,如以下查询所示:
SELECT *
FROM cars car
JOIN manufacturer mfg
ON mfg.Id = car.ManufacturerId
JOIN models mod
ON mod.Id = car.ModelId
JOIN colors col
ON mfg.Id = car.ColorId
但这可以通过不为表名使用小别名来轻松缓解:
SELECT *
FROM cars
JOIN manufacturer
ON manufacturer.Id = cars.ManufacturerId
JOIN models
ON models.Id = cars.ModelId
JOIN colors
ON manufacturer.Id = cars.ColorId
对我而言,始终使用3个字母的缩写的做法似乎比使用列名的做法差很多id
。(恰当的例子:谁实际上会在表名cars
的缩写后面加上缩写car
?这起了什么作用?)
重点是:保持一致。如果您的公司使用Id,并且通常会出现上述错误,那么请养成使用全表名的习惯。如果您的公司禁止使用ID列,请大步向前,并使用他们喜欢的任何命名约定。
专注于学习实际上是不良做法的事物(例如,多个嵌套的相关子查询),而不是考虑这样的问题。将列命名为“ ID”的问题更像是一种品味问题,而不是一种不好的做法。
编者注:此查询中的错误是有意的,并被用来说明问题。编辑之前,请先阅读完整答案。
cars
-> car
。感谢上帝,您省了我的手指)。不要读得太深。
cars
和混合使用manufacturer
。一个是复数,另一个不是。如果人们想选择数据库,那是应该选择的错误做法。
因为当您有一个带有外键的表时,您不能将该外键命名为“ Id”。您将表命名为TableId
然后你的加入看起来像
SELECT * FROM cars c JOIN manufacturer m ON m.Id = c.ManufacturerId
理想情况下,您的条件应该在两边都具有相同的字段名称
SELECT * FROM cars c JOIN manufacturer m ON m.ManufacturerId = c.ManufacturerId
因此,虽然将Id命名为ManufacturerId似乎是多余的,但随着错误变得明显,在连接条件中出现错误的可能性也就较小。
这似乎很简单,但是当您连接多个表时,很可能会出错,请在下面找到一个表...
SELECT *
FROM cars car
JOIN manufacturer mfg
ON mfg.Id = car.ManufacturerId
JOIN models mod
ON mod.Id = car.ModelId
JOIN colors col
ON mfg.Id = car.ColorId
正确命名后,错误仍然存在...
SELECT *
FROM cars car
JOIN manufacturer mfg
ON mfg.ManufacturerId = car.ManufacturerId
JOIN models mod
ON mod.ModelId = car.ModelId
JOIN colors col
ON mfg.ManufacturerId = car.ColorId
将它们命名为“坏”的另一个原因是,当您从多个表中查询信息时,您将需要重命名ID列,以便区分它们。
SELECT manufacturer.Id as 'ManufacturerId'
,cars.Id as 'CarId'
--etc
FROM cars
JOIN manufacturer
ON manufacturer.Id = cars.Id
使用准确的名称,这不再是问题
SELECT * FROM cars c JOIN manufacturer m ON manufacturer.Id = c.ManufacturerId
。我已经使用id
了多年,但从未发现您所描述的是一个真正的问题。
SELECT manufacturer.id FROM ...
。由此产生的每一个困难id
都可以很容易地克服,这仅仅是一个品味问题。
默认情况下,Ruby的ActiveRecord库和Groovy的GORM使用“ id”作为代理键。我喜欢这种做法。在每个列名中复制表名是多余的,写起来很繁琐,读起来也很繁琐。
通用或关键列名称(如“名称”或“ Id”)应以TableName为前缀。
它消除了歧义,易于搜索,并且在需要两个“ Id”值时意味着更少的列别名。
较少使用或审核的列或非关键(例如LastUpdatedDateTime)无关紧要
name
和id
?为什么每个列都没有前缀表名?选择这两个名称作为前缀似乎是任意的。从概念上讲,您必须具有表才能始终具有列的上下文。为什么不只使用该表名来澄清查询:Person.Name,Animal.Name,Part.Name,...
该线程已死,但我想补充一点,不使用IMO Id
是一种不好的做法。该Id
列是特殊的;它是在主键。任何表可以具有任意数量的外键,但是只能有一个主键。在所有主键都被调用的数据库中Id
,一旦您查看该表,您就会知道哪一列是主键。
相信我,几个月来,我每天整天都在许多大型数据库(Salesforce)中工作,关于模式,我能说的最好的事情是每个表都有一个名为的主键Id
。我可以向您保证,我绝对不会对将主键连接到外键感到困惑,因为PK被称为Id
。人们没有提到的另一件事是表可以有很长的愚蠢名称,例如Table_ThatDoesGood_stuff__c
;这个名字已经够糟糕了,因为架构师早上想起那张桌子时就宿醉了,但是现在您告诉我,不调用主键是一种不好的做法Table_ThatDoesGood_stuff__cId
(记住,SQL列名通常不区分大小写)。
老实说,大多数教计算机编程的人的问题是,即使有多年,他们也没有写过一条生产代码,而且他们不知道软件工程师实际上会做什么。等到您开始工作,然后再下定决心,认为是否是一个好主意。
我认为这不是坏习惯。像往常一样,一致性为王。
我认为这全都取决于上下文。在表本身的上下文中,“ id”仅表示您所期望的含义,它是一个标签,可以帮助与其他可能(或看上去)相同的其他对象唯一地标识。
在联接的上下文中,您有责任以使其对您和您的团队可读的方式构造联接。就像用措辞或命名不当可能使事情看起来很难一样,也可以有效地使用别名甚至注释来构造有意义的查询。
以同样的方式,名为“ Foo”的Java类没有以“ Foo”为前缀的属性,因此不必为表ID加上表名作为前缀。在上下文中通常很清楚所指的ID是什么。
BOOM,问题已回答。
现在去告诉您的老师,SO练习不良的数据库设计。
PostTypeId -> PostTypes.Id
; AcceptedAnswerId -> Answers.Id
; OwnerUserId -> Users.Id
。为什么应该将这种简单的做法视为“不好的”做法?
这使得在表上执行自然连接变得很困难(并且令人困惑),因此,是的,即使不是很糟糕也很糟糕。
Natural Join是SQL Lore(即关系代数)的古老工件,您可能已经看到以下其中一项:perhaps也许在数据库书中。我的意思是,Natrual Join并不是一个新的SQL想法,即使DBMS似乎要花很长时间才能实现它,因此,对于您来说,实现它并不是一个新的想法,您甚至可能会无理理ignore地忽略它如今它的存在。
好吧,如果您命名所有主键的ID,那么您将失去自然联接的便捷性。select * from dudes natural join cars
将需要书面select * from dudes inner join cars where cars.dudeid = dudes.id
或select * from dudes inner join cars where dudes.carid = cars.id
。如果您能够进行自然连接,则可以忽略实际的关系,我认为这非常棒。
在某些情况下,在每个表上粘贴“ ID”并不是最好的主意:USING
如果支持,则使用关键字。我们经常在MySQL中使用它。
例如,如果您具有fooTable
with列fooTableId
和barTable
外键fooTableId
,那么您的查询可以这样构造:
SELECT fooTableId, fooField1, barField2 FROM fooTable INNER JOIN barTable USING (fooTableId)
与其他方法相比,它不仅可以节省键入内容,而且可读性更高:
SELECT fooTable.Id, fooField1, barField2 FROM fooTable INNER JOIN barTable ON (fooTable.Id = barTable.foTableId)
USING
关键字受postgres / mysql / sqlite数据库支持,这意味着键入较少,而其他答案中的某些列为使用的原因id
,最后在我的主观意见中更易读。
为什么不问老师呢?
考虑一下,当所有表PK列都被命名后ID
,使用它们作为外键就成了噩梦。
列名在语义上必须有意义。ID
是通用的。
table.id
是引用id
字段的一种完全可以接受的方式。在表名前面加上字段名是多余的。
ID错误是由于以下原因:
如果您要进行大量报告查询,则要同时查看这两个列,必须始终为列加别名。因此,当您可以正确命名时,就浪费了时间。这些复杂的查询已经足够困难(我编写的查询可能长达数百行)而不会增加不必要的工作负担。
可能会导致代码错误。如果您使用允许使用自然联接的数据库(不是我想您应该使用自然联接,但是当功能可用时,有人会使用它们),如果您得到使用它的开发人员,那么您将加入错误的事物。
如果要复制联接以创建复杂的查询,则很容易忘记将别名更改为所需的别名并获得错误的联接。如果每个ID均以其所在的表命名,则通常会出现语法错误。如果pPK名称和FK名称匹配,则更容易发现复杂查询中的联接是否不正确。
ID
根本无法说服我。
有一些答案可以解决我认为不使用“ id”作为表中主键的列名的最重要原因:一致性和减少的歧义。
但是,对我而言,主要好处是维护程序员(尤其是不参与原始开发的程序员)实现的。如果您在Person表中使用名称“ PersonID”作为ID并始终使用该名称作为外键,那么对模式编写查询以找出哪些表具有PersonID而不用推断“ PersonID”就很简单。是外键时使用的名称。记住,对与错,外键关系并非总是在所有项目中都得到实施。
在一个极端的情况下,一个表可能需要在同一张表上有两个外键,但是在这种情况下,我会将原始键名作为该列的后缀名,因此可以很容易地找到通配符%PersonID这些实例也是如此。
是的,这可以通过具有“ id”并知道始终将其用作“ tableNameID”的标准来完成,但是这既需要知道实践已经到位,又要取决于原始开发者来减少使用“ id”直观的标准做法。
尽管有人指出,写出较长的列名确实需要一些额外的按键操作,但我认为编写代码只是程序活动寿命的一小部分。如果要节省开发人员的击键作为目标,则永远不要写评论。
作为花了很多年维护数百个表的大型项目的人,我强烈希望表中键的名称一致。
Companies
表有2个外键Persons
。一个代表公司总裁;另一个代表公司总裁。另一个代表公司的财务主管。你真的叫列PersonID1
和PersonID2
?称它们为PresidentID
和将更具描述性TreasurerID
。我觉得更容易阅读inner join Person AS Presidents ON Company.PresidentID = Presidents.ID
比inner join Person AS Person1 ON Company.PersonID1 = Person1.PersonID
CompanyOfficer
或CompanyPerson
表,该表允许在关系之间Company
以及Person
关系性质的其他信息之间建立多对多关系。如果要在Company
表中实现它,我将使用列名PresidentPersonID
并TreasurerPersonID
在添加其他描述符时保留名称的* PersonID部分。 inner join Person as Presidents on Company.PresidentPersonID = Presidents.PersonID
使用ID作为主键字段的实践导致将ID添加到每个表的实践。许多表已经具有唯一标识记录的唯一信息。使用THAT作为主键,而不是添加到每个表的id字段。这是关系数据库的基础之一。
这就是使用id的不好习惯的原因:id常常不只是信息的自动增加。
请考虑以下表格:
PK id | Countryid | Countryname
1 | 840 | United States
2 | 528 | the Netherlands
该表的问题是,它使用户可以添加另一行:国家代码为840的美国。它刚刚破坏了关系完整性。当然,您可以在各个列上实施唯一性,也可以只使用已经可用的主键:
PK Countryid | Countryname
840 | United States
528 | the Netherlands
这样,您就可以将已经拥有的信息用作主键,这是关系数据库设计的核心。
如果使用正确,我认为这不是一个坏习惯。通常有一个永远不需要接触的自动递增的ID字段,称为“ ID”,并为应用程序使用一个友好的标识符。编写类似的from tableA a inner join tableB b on a.id = b.a_id
代码可能有点麻烦,但是可以将这些代码隐藏起来。
作为个人喜好,我倾向于在ID的前面加上实体的名称,但是Id
如果使用完全由数据库处理的话,我看不出真正的问题。
ID已经足够普遍了,我认为它不会使任何人感到困惑。您总是想知道这张桌子。将字段名称放在生产代码中而不包含表/别名是一种不好的做法。如果您过于担心能否快速键入即席查询,那您就该靠自己了。
只是希望没有人开发一个ID保留字的sql数据库。
CREATE TABLE CAR (ID);
在一个漂亮的小2字符包中,以1开头的字段名称,主键和自动递增1来处理字段名称。哦,我会叫它CARS,但是如果我们要节省按键次数,谁真的认为叫CAR的表只有一个?
这个问题一遍又一遍地被质疑,但我认为我也将补充我的意见。
我用id表示那是每个表的标识符,所以当我加入一个表并且需要主键时,我会自动加入该主键。
id字段是一个无符号的自动递增(这意味着我永远不必设置其值,并且它不能为负)
对于外键,我使用tablenameid(同样是样式问题),但是我加入的主键是表的id字段,因此一致性意味着我可以随时轻松地查询查询
id也很简短
其他约定-所有表名和列名均使用小写,因此不会因大小写而出现问题
要考虑的另一件事是,如果主键名称与外键名称不同,则无法使用某些第三方工具。
例如,您将无法将架构加载到Visio之类的工具中,而无法生成准确的ERD。
我发现这里的人们几乎涵盖了各个方面,但是我想补充一点,“ id”不是,也不应该被理解为“标识符”,它更多地是“索引”,并且肯定不会声明或描述行的身份。(我在这里可能使用了错误的措辞,请改正我的意思)
人们或多或少如何读取表数据以及如何编写代码。我个人(很可能这是我最常看到的最流行的方式)是table.id
,即使程序员不需要进行联合或/和联接,也可以将完整的引用写为。例如:
SELECT cars.color, cars.model FROM cars WHERE cars.id = <some_var>
这样,您可以将其翻译为英语“给我编号为的那辆汽车的颜色和型号”。而不是“给我识别为数字的那辆汽车的颜色和型号”。ID不会以任何方式代表汽车,它只是汽车的索引,如果可以的话,它是序列号。就像您要从数组中获取第三个元素一样。
因此,总而言之,我想补充的是,这只是一个偏好问题,所描述的读取SQL的方式是最受欢迎的方式。
但是,在某些情况下不使用此功能,例如(非常罕见的示例),当ID是真正描述的字符串时。例如id = "RedFordMustang1970"
或类似的东西。我真的希望我至少可以解释一下以了解这个想法。