更好的 SQL 模式的 10 条规则
更好的 SQL 模式的 10 条规则
在创建新表和数据仓库时,要做很多决定。一些在当时似乎无关紧要的地方,却让你和用户在数据库的生命期内感到痛苦。
我们和成千上万的人们以及他们的数据库一道工作,经历了长期的读写查询,我们差不多看到了每种情况。下面是创建免去痛苦模式的 10 条规则。
1.只使用小写字母、数字和下划线
不要在数据库、模式、表或列名中使用点(dot)、空格、或连接号【注1】。点用于标示对象,通常以database.schema.table.column的方式。
对象名称中包含点将引起混淆。类似地,在对象名字里使用空格将迫使你在查询语句中添加不必要的引号:
select "user name" from events
-- vs
select user_name from events
如果在表或列名里有大写字母,查询语句将难以书写。如果所有字母都是小写的,人们将不必记住users表是Users还是users。
当你最终修改数据库或把你的表复制到仓库时,你不需要记住哪个表是大小写敏感的。
2.使用简单的、自说明的列名
如果users表需要packages表的外键,就把键命名为 package_id.避免使用简短、晦涩的名字,比如pkg_fk;其他人不知道它代表什么。自说明的名字让其他人更容易理解模式,随着团队规模的增加,这对于维护效率至关重要。
不要为多态的数据使用有歧义的名字。如果你发现自己创建了形如item_type或item_value的列,那么你最好使用带有具体名字的、更多的列,比如photo_count、view_cout、transaction_price。
这样,列的内容就可以常从模式中获悉,而不用依赖于当前行的其它值。
select sum(item_value) as photo_count
from items
where item_type = 'Photo Count'
-- vs
select sum(photo_count) from items
不要把包含表的名字做为列名的前缀。通常,让用户表包含形如user_birthday、user_created_at、user_name的列,没有多少帮助。
避免使用column、tag和user之类的保留字做为列名。你将不得不在查询中使用额外的引号,不这么做将让你对错误信息感到困惑。如果保留字出现在列名应该出现的地方,数据库会极大地错误理解查询。
3.使用简单的、自说明的表名
如果表名由多个单词组成,就使用下划线隔开这些单词。package_deliveries要比packagedeliveries更容易阅读。
如有可能,使用一个单词而不是两个单词:deliveries更易于读。
select * from packagedeliveries
-- vs
select * from deliveries
不要给表加前缀来暗示模式。如果你需要把表分组为范围,就把这些表放入一个模式。store_items、store_transactions、store_coupons之类的表名,和加了前缀的列名一样,通常不值得额外敲键盘。
我们推荐表名使用复数(例如packages),连接表(join table)名字的两个单词都用复数(例如packages_users)。单数的表名更有可能偶尔与保留字相撞,并且在查询语句中通常有着较低的可读性。
4.主键为整数
即使你在用 UUID,它也没有意义(比如对于连接表来说),添加标准的id列、自增整数序列。这种 key 使得某些查询更加容易,比如仅仅选取一组数据的第一行。
如果导入的任务需要复制数据,这种 key 将成为救命稻草,因为你能够删除特定行:
delete from my_table
where id in (select ...) as duplicated_ids
避免多列主键。它们在尽量编写有效查询时难以推断,且难以修改。要使用整数主键、多列 unique 约束、一些单列索引代替。
5.与外键保持一致
有很多关于主键和外键的命名风格。我们推荐,最受欢迎的是,任何表foo,都要拥有一个名叫id的主键,所有的主键命名为foo_id。
另一种受欢迎的风格使用全局唯一键名,表foo有个名叫foo_id的主键,所有外键也叫foo_id。如果你使用缩写(users表的主键用uid),会引起混淆或命名冲突,故不要缩写。
无论你选择什么风格,就保持下去。不要在有的地方使用uid,在另外地方使用user_id或users_fk。
还要留意不能明显匹配表的外键。名叫owner_id的列名或许是users表的外键,或许不是。把列名取为user_id,如有必要,取为owner_user_id。
6.把时间存储为 Datetime
不要把日期保持为 Unix 时间戳或字符串:而是把它们转化为 datetime。虽然 SQL 的 date 数学函数不是最好的,但是你自己处理时间戳甚至更难。使用 SQL date 函数要求每次查询都把时间戳转化为 datetime:
select date(from_unixtime(created_at))
from packages
-- vs
select date(created_at)
from packages
不要在单独的列里存储年、月、日。这使得每一条时间序列【注2】查询非常难以编写,将阻碍大多数刚入门的 SQL 用户使用表格中的日期信息。
select date(created_year || '-'
|| created_month || '-'
|| created_day)
-- vs
select date(created_at)
7.UTC,一直都是 UTC
使用某种时区而非UTC将引起永无止境的问题。优秀的工具(包括Periscope)具备所有你需要的、将数据从 UTC 转换成当前时区的功能。在 Periscope 里,添加:pst就轻松地将 UTC 转换成 Pacific Time:
select [created_at:pst], email_address
from users
数据库的时区应该是 UTC,所有的 datetime 列应该是去除了时区的类型(没有时区的时间戳)。
如果你的数据库的时区不是 UTC,或者你的数据库既有 UTC、又有非 UTC 的 datetime,那么时间序列的分析难度将大为增加。
8.单一的真实数据来源
对于一条数据,应该有且只有一个真实来源【注3】。视图和汇总应该打上标签。这样,数据的使用人员将明白,在他们使用的数据和真实数据之间存在差异。
select *
from daily_usage_rollup
留下废弃的user_id、user_id_old、user_id_v2之类的列,将变成混淆的、永无止境的源头。在日常维护中,要确信 drop 掉了已被抛弃的表、和弃用的列。
9.更喜欢没有 JSON 列的表
你肯定不想要非常宽的表。如果有很多列,且它们有的按顺序命名(比如answer1、answer2、answer3),今后你就会痛苦。
把这种表拆分成没有重复列的模式,这种模式的形态将特别容易查询。例如,获取survey表的、完成的答案的数目:
select
sum(
(case when answer1 is not null
then 1 else 0 end) +
(case when answer2 is not null
then 1 else 0 end) +
(case when answer3 is not null
then 1 else 0 end)
) as num_answers
from surveys
where id = 123
-- vs
select count(response)
from answers
where survey_id = 123
对于分析查询,从 JSON 列提取数据,能够极大地降低查询效率。虽然在生产环境有很多理由使用 JSON 列,但那不是针对分析的。强势地把 JSON 列转换为更简单的数据类型,让分析更加容易、更加快捷。
10.不要过度规范化
日期、邮编和国家,不需要让它们自己的表带有主键查询。如果你带了,每次查询将包含有少量的相同连接。这会给数据库创建大量重复的 SQL,以及大量额外工作。
select
dates.d,
count(1)
from users
join dates on users.created_date_id = dates.id
group by 1
-- vs
select
date(created_at),
count(1)
from users
group by 1
表是有着它们大量自己的数据的第一类对象。其它数据都应该是更加重要的对象上的、另外的列。
期待更好的模式!
有了这些规则武装,你的下一个表或仓库对于你和新团队成员而言,在队伍壮大时,将更易于查询。如果你不同意或有更多的规则方面的建议,请邮件周知我们hello@periscope.io。我们乐于听到你的声音!
注1:连接号(-,〜),表示连接、起止、流程的符号。“两个相关的名词构成一个意义单位,中间用连接号。”、“相关的时间、地点或数目之间用连接号,表示起止。”、“相关的字母、阿拉伯数字等之间,用连接号,表示产品型号。”、“几个相关的项目表示递进式发展,中间用连接号。”http://zh.wikipedia.org/wiki/%E8%BF%9E%E6%8E%A5%E5%8F%B7请注意:连接号和连字号是不同的:http://zh.wikipedia.org/wiki/%E8%BF%9E%E5%AD%97%E5%8F%B7
注2:时间序列是用时间排序的一组随机变量,国内生产毛额(GDP)、消费者物价指数(CPI)、台湾加权股价指数、利率、汇率等等都是时间序列。
时间序列的时间间隔可以是分秒(如高频金融数据),可以是日、周、月、季度、年、甚至更大的时间单位。
更好的 SQL 模式的 10 条规则的更多相关文章
- [译] MVP模式的14条规则
笔者在前文<MVP和MVC>中提到了两者的区别,以及MVP日趋流行的原因:即随着各种给力UI框架的发布,View的功能越来越强,已经足以完成一些简单的不需要与后台或其他view交互的eve ...
- sql查询第10条到第20条数据
select top(10) * from T1 where Id >= (select MAX(Id) from (select top(20) * from T1 order by Id) ...
- NASA的10条编码规则
关于NASA的10条编程规则,他们曾表示:这些规则的作用就像汽车上的安全带:最初,它们可能有点不舒服,但过了一会儿,它们的使用就变成了第二天性,而没有使用它们就变得不可想象. Gerard J. Ho ...
- 转:10条建议让你创建更好的jQuery插件
在开发过很多 jQuery 插件以后,我慢慢的摸索出了一套开发jQuery插件比较标准的结构和模式.这样我就可以 copy & paste 大部分的代码结构,只要专注最主要的逻辑代码就行了.使 ...
- 10 条建议让你创建更好的 jQuery 插件
在开发过很多 jQuery 插件以后,我慢慢的摸索出了一套开发jQuery插件比较标准的结构和模式.这样我就可以 copy & paste 大部分的代码结构,只要专注最主要的逻辑代码就行了. ...
- 10条建议让你创建更好的jQuery插件
在开发过很多 jQuery 插件以后,我慢慢的摸索出了一套开发jQuery插件比较标准的结构和模式.这样我就可以 copy & paste 大部分的代码结构,只要专注最主要的逻辑代码就行了.使 ...
- 10条建议让你创建更好的jQuery插件(转载)
为了避免重复造轮子,自己手动开发jquery 插件,让小组其他成员可以直接使用.学习开发中,看到Phillip Senn 写的 关于jquery 插件开发注意10点,转载之! ------------ ...
- 10条建议帮助你创建更好的jQuery插件
本文总结了帮助你创建更好jQuery插件的10条建议.分享给大家供大家参考.具体说明如下: 在开发过很多 jQuery 插件以后,我慢慢的摸索出了一套开发jQuery插件比较标准的结构和模式.这样我就 ...
- sql 第 10条 到20条
sql 第 10条 到20条 select * from( select *,ROW_NUMBER () over (order by @@servername) as rownum from tb_ ...
随机推荐
- webstorm 如何修改背景颜色
http://www.cnblogs.com/zxyun/p/4744744.html 见文章底部有图文说明 15个必须知道的chrome开发者技巧(GIF):http://www.wtoutiao. ...
- zzzzw_在线考试系统②管理员篇章
今天实现了管理的功能,谈谈遇到的问题!我先上图 图一 管理员的数据库 在action中访问Servlet API的非IoC方式之一:使用apache.struts2.ServletActionCo ...
- webform 转 MVC 飞一般的感觉
前言: 浅谈webform与mvc,让开发变得更加简单,这里主要通过比较webform与mvc的开发方式,以下全属个人看法,不完善的地方可以留言补充. 正文: 废话不多说,直接说工作中经常用到的地方 ...
- Angularjs总结(八)$ cookie和$rootscope
AngularJS 提供了很好的 $cookie 和 $cookieStore API 用来处理 cookies .这两个服务都能够很好的发挥HTML5 cookies,当HTML5 API可用时浏览 ...
- 初步认识 Web Service
Web Service初步认识 Web Service:不是框架,不是一种技术,而是一种跨平台,跨语言的规范. 作用:异构平台之间的交互,解决了不同平台,不同语言所编写的应用之间的相互调用.(远 ...
- 【BZOJ1042】【DP + 容斥】[HAOI2008]硬币购物
Description 硬币购物一共有4种硬币.面值分别为c1,c2,c3,c4.某人去商店买东西,去了tot次.每次带di枚ci硬币,买si的价值的东西.请问每次有多少种付款方法. Input 第一 ...
- 【ADO.NET】7、SQL高级封装
这次是更加简化的进行封装,所有的cmd操作命令都封装到了 Allcmd() 方法里面别外还有一个别点是 每次执行命令完后,都会垃圾回收, cmd.Parameters.Clear();是先将执行返回的 ...
- python 自动化之路 logging日志模块
logging 日志模块 http://python.usyiyi.cn/python_278/library/logging.html 中文官方http://blog.csdn.net/zyz511 ...
- 微信公众号-加解密数据demo坑
demo里面的MsgSignature作为url参数一部分了,demo也不更新下 坑爹的微信! 解密信息部分 include_once "wxBizMsgCrypt.php"; $ ...
- python三级菜单的实现
一.作业要求 1.使用字典实现三级菜单功能 2.直接输入前面数字进入下一级菜单 3.按B返回上一级,按Q退出 二.需要知识点 1.if循环 2.for循环,enumerate的用法 3.while循环 ...