开发反模式(GUID) - 伪键洁癖
一、目标:整理数据
有的人有强迫症,他们会为一系列数据的断档而抓狂。
一方面,Id为3这一行确实发生过一些事情,为什么这个查询不返回Id为3的这一行?这条记录数据丢失了吗?那个Column到底是什么?我要为这条数据的丢失负责吗?
二、反模式:填充角落
大多数人对于断档的第一反应就是想要填补其中的空缺。对此,可能有两种做法:
1、不按照顺序分配编号
你可能想要在插入新行时,通过遍历表,将找到的第一个未分配的主键编号分配给新行,来代替原来自动分配的伪主键机制。随着你不断地插入新行,断档就被填补起来了。
然而,为了找到第一个未被使用的值,你不得不执行一次自联合查询:
SELECT TOP 1 c1.ColumnId + 1
FROM [Columns] AS c1
LEFT OUTER JOIN [Columns] AS c2 ON (c1.ColumnId + 1 = c2.ColumnId)
WHERE c2.ColumnId IS NULL
ORDER BY c1.ColumnId
如果用SELECT MAX(ColumnId) + 1 这种查询语句,则会出现并发访问的问题,如果有两个程序同时想要找最小的未使用值时,也会出现同样的问题,结果就是一个成功
,另一个失败。况且,这个方法既低效,也容易导致问题。
UPDATE [Columns] SET ColumnId = 3 WHERE ColumnId = 4
2、为现有行重新编号
在某些情况下,你可能急着想要让所有的主键变得连续,而等着新行来填补空缺不够快。你可能会考虑更新现有行的主键值,让其变得连续,从而消除断档。通常这样的做法是找到主键最大的行,然后用最小的未被使用的值来更新它。比如,你可以将4更新为3:
要完成这一步,你需要使用和前面一种方法插入新行类似的方法。先找到一个未被使用的值,随后执行UPDATE语句来重新分配主键值。这两步都会引起并发访问的问题。而且,你需要重复执行很多次这样的操作,才能将较大的断档填满。
你必须同时更新所有引用了你重新分配了主键的行的子记录。如果你在定义外键时,使用了ON UPDATE CASCADE选项,这一步会变得很方便,但如果你没这么做,就必须先禁用约束,手动更新所有的子记录,然后恢复约束。这是一个费时费力且容易出错的操作,并且可能会影响到数据库的服务,正常人都会避免这么做。
即使你完成了清理操作,“整洁”也是个“短命鬼”,如果如果Id是自动生成的,那么对不起,你懂的,Id自增还是按照原来的最大数值增加,比如最大是4,你将4改成了3,但是下一个自动生成的主键依然是5。
3、制造数据差异
重用主键并不是一个好主意,因为断档往往是由于一些合理的删除或者回滚数据引起的。假设一个AccountId为789的用户由于发送带有政治问题的邮件而被封号,产品策略要求删除了这个账号,如果你重用了主键,就会在某一时间点将789分配给另一个用户。而收件人可能在删除后的某一个时间点才打开这些邮件,他们还会投诉789这个Id账号的用户。尽管这个用户本身没有做错任何事情,但是它被分配了一个需要为此负责的编号。
三、解决方案:克服心理障碍
1、定义行号
大多数伪键返回的数字看起来就和行号一样,因为他们就是一次递增的,但这只是由于伪键实现机制所造成的巧合而已。按照这样的方式生成的主键值能比较方便地确保唯一性。
别把主键值和行号混为一谈。主键是用来标识表中记录的,而行号是用来标识查询结果集中记录的。查询结果集的行号和主键值没有一丁点关系,尤其是当你使用了JOIN、GROUP BY或者ORDER BY这些操作符的时候。
有很多使用行号的好理由,比如返回一个查询结果集的子集。通常我们称之为分页,就像在网络搜索时的一页。要选择一个子集,你需要使用到实际的连续增长的行号,但和查询的形式无关。
SQL Server定义了包括ROW_NUMBER()在内的一些窗口函数,返回一个查询结果集中一段连续的行。通常使用行号的作用是将查询结果限制在一个特定的范围内。
2、使用GUID
当然我们还可以生成随机伪键值,只要你不会重复使用任何数字。有些数据库提供全局唯一标识符(GUID)来达到这个目的。
GUID是一个128位的伪随机数(通常用32个),由于GUID的定义及其所要实现的目的,它被设计成具有唯一,因此你可以用其作为伪键。
下面给出用SQL Server使用GUID作为主键的例子:
CREATE TABLE Person(
Id UNIQUEIDENTIFIER DEFAULT NEWID(),
Name nvarchar(50)
)
效果如下:
使用GUID相对于整型自增主键来说有如下两个优势:
- 可以在多个数据库服务器上并发地生成伪键,而不用担心生成同样的值。
- 没有人会在抱怨有断档-他们会忙于抱怨输入32个十六进制字符做主键。
下面是使用GUID带来的不便:
- GUID的值太长,不便于输入。
- GUID的值是随机的,因此找不到任何规则或者依靠最大值来判断哪一行是最新插入的。
- GUID的存储需要16字节,这比传统的4字节整型伪键占用更多的空间,并且查询的速度更慢。
SQL:UNIQUEIDENTIFIER <=> .Net GUID
开发反模式(GUID) - 伪键洁癖的更多相关文章
- SQL反模式学习笔记22 伪键洁癖,整理数据
目标:整理数据,使不连续的主键Id数据记录变的连续. 反模式:填充断档的数据空缺. 1.不按照顺序分配编号 在插入新行时,通过遍历表,找到的第一个未分配的主键编号分配给新行,来代替原来自动分配的伪主键 ...
- 开发反模式 - SQL注入
一.目标:编写SQL动态查询 SQL常常和程序代码一起使用.我们通常所说的SQL动态查询,是指将程序中的变量和基本SQL语句拼接成一个完整的查询语句. string sql = SELECT * FR ...
- SQL反模式学习笔记5 外键约束【不用钥匙的入口】
目标:简化数据库架构 一些开发人员不推荐使用引用完整性约束,可能不使用外键的原因有一下几点: 1.数据更新有可能和约束冲突: 2.当前的数据库设计如此灵活,以至于不支持引用完整性约束: 3.数据库为外 ...
- SQL反模式学习笔记4 建立主键规范【需要ID】
目标:建立主键规范 反模式:每个数据库中的表都需要一个伪主键Id 在表中,需要引入一个对于表的域模型无意义的新列来存储一个伪值,这一列被用作这张表的主键, 从而通过它来确定表中的一条记录,即便其他的列 ...
- 《SQL 反模式》 学习笔记
第一章 引言 GoF 所著的的<设计模式>,在软件领域引入了"设计模式"(design pattern)的概念. 而后,Andrew Koenig 在 1995 年造了 ...
- SQL反模式学习笔记1 开篇
什么是“反模式” 反模式是一种试图解决问题的方法,但通常会同时引发别的问题. 反模式分类 (1)逻辑数据库设计反模式 在开始编码之前,需要决定数据库中存储什么信息以及最佳的数据组织方式和内在关联方式. ...
- SQL反模式学习笔记9 元数据分裂
目标:支持可扩展性.优化数据库的结构来提升查询的性能以及支持表的平滑扩展. 反模式:克隆表与克隆列 1.将一张很长的表拆分成多张较小的表,使用表中某一个特定的数据字段来给这些拆分出来的表命名. 2.将 ...
- SQL反模式学习笔记19 使用*号,隐式的列
目标:减少输入 反模式:捷径会让你迷失方向 使用通配符和未命名的列能够达到减少输入的目的,但是这个习惯会带来一些危害. 1.破坏代码重构:增加一列后,使用隐式的Insert插入语句报错: 2.查询中使 ...
- SQL反模式学习笔记3 单纯的树
2014-10-11 在树形结构中,实例被称为节点.每个节点都有多个子节点与一个父节点. 最上层的节点叫做根(root)节点,它没有父节点. 最底层的没有子节点的节点叫做叶(leaf). 中间的节点简 ...
随机推荐
- 构建WDK驱动出现fatal error U1087: cannot have : and :: dependents for same target
原因:WDK在编译驱动时,是不允许源文件所在的路径(全路径)中包含空格的,如果你包含了空格,就会出现上述错误. 解决方法:把源文件放在一个没有空格的路径下. reference: http://blo ...
- hdu4085
http://acm.hdu.edu.cn/showproblem.php?pid=4085 斯坦纳树. 用状压DP. 一共有2K个关键点:1,2...,K和N-K+1,N-K+2...,N,我们用一 ...
- <转载>C++的链接错误LNK2005
转载http://bbs.csdn.net/topics/70346371 编程中经常能遇到LNK2005错误——重复定义错误,其实LNK2005错误并不是一个很难解决的错误.弄清楚它形成的原因,就可 ...
- global.asax?app.config?webconfig??
一.Global.asax 1.global.asax是什么? 一个文本文件,至于它包含写什么内容?顾名思义,global 肯定是掌管一个应用程序(application)的全局性的东西,例如应用程序 ...
- hdu 2817 A sequence of numbers(快速幂)
Problem Description Xinlv wrote some sequences on the paper a long time ago, they might be arithmeti ...
- Git冲突解决方案
Git冲突解决方案 1. 在代码提交时,先更新,若有冲突.先解决冲突.若提交之后在review时才发现无法合并代码时有冲突,需要abandon此次提交的代码. 2. 解决冲突的基本做法,保存本地代 ...
- epoll的原理和使用方法
设想一个场景:有100万用户同一时候与一个进程保持着TCP连接,而每个时刻仅仅有几十个或几百个TCP连接时活跃的(接收到TCP包),也就是说,在每一时刻,进程值须要处理这100万连接中的一小部分连接. ...
- 浅谈管道模型(Pipeline)
本篇和大家谈谈一种通用的设计与处理模型--Pipeline(管道). Pipeline简单介绍 Pipeline模型最早被使用在Unix操作系统中.据称,假设说Unix是计算机文明中最伟大的发明,那么 ...
- [置顶] ./build_native 时出现please define NDK_ROOT
在一次帮朋友弄cygwin交叉编译时出现了这个问题 cygwin是按照成功了,make-v,以及gcc-v都没出现问题,就是在./build_native 时出现please define NDK_R ...
- Android中Service类onStartCommand
Android开发的过程中,每次调用startService(Intent)的时候,都会调用该Service对象的onStartCommand(Intent,int,int)方法,然后在onStart ...