1. 使用小写的SQL

它与大写SQL一样易读,而且不必一直按住shift键。

  1. -- Good
  2. select * from users
  3. -- Bad
  4. SELECT * FROM users
  5. -- Bad
  6. Select * From users

2. 单行查询 和 多行查询

只有当你选择:

  • 所有列(*)或选择1或2列
  • 您的查询没有额外的复杂性 ```sql — Good select * from users

— Good select id from users

— Good select id, email from users

— Good select count(*) from users

  1. 这样做的原因很简单,当所有内容都在一行时,仍然很容易阅读。但一旦你开始添加更多的列或更复杂的代码,如果是多行代码就更容易阅读:
  2. ```sql
  3. -- Good
  4. select
  5. id,
  6. email,
  7. created_at
  8. from users
  9. -- Good
  10. select *
  11. from users
  12. where email = 'example@domain.com'

对于具有1或2列的查询,可以将这些列放在同一行上。对于3+列,将每个列名放在它自己的行上,包括第一项:

  1. -- Good
  2. select id, email
  3. from users
  4. where email like '%@gmail.com'
  5. -- Good
  6. select user_id, count(*) as total_charges
  7. from charges
  8. group by user_id
  9. -- Good
  10. select
  11. id,
  12. email,
  13. created_at
  14. from users
  15. -- Bad
  16. select id, email, created_at
  17. from users
  18. -- Bad
  19. select id,
  20. email
  21. from users

3. 左对齐

  1. -- Good
  2. select id, email
  3. from users
  4. where email like '%@gmail.com'
  5. -- Bad
  6. select id, email
  7. from users
  8. where email like '%@gmail.com'

4. 使用单引号

一些SQL语法,如BigQuery支持使用双引号,但是对于大多数语法,双引号最终将引用列名。因此,单引号更可取:

  1. -- Good
  2. select *
  3. from users
  4. where email = 'example@domain.com'
  5. -- Bad
  6. select *
  7. from users
  8. where email = "example@domain.com"

5.!= 优于 <>

因为!=读起来像“not equal”,更接近我们大声说出来的方式。

  1. -- Good
  2. select count(*) as paying_users_count
  3. from users
  4. where plan_name != 'free'

6. 逗号放在行尾

  1. -- Good
  2. select
  3. id,
  4. email
  5. from users
  6. -- Bad
  7. select
  8. id
  9. , email
  10. from users

7.缩进条件

如果只有一个条件,请将其保留在与以下相同的行

  1. select email
  2. from users
  3. where id = 1234

当有多个缩进时,将每个缩进比where更深一层。将逻辑运算符放在前一个条件的末尾:

  1. select id, email
  2. from users
  3. where
  4. created_at >= '2019-03-01' and
  5. vertical = 'work'

8.避免在括号内使用空格

  1. -- Good
  2. select *
  3. from users
  4. where id in (1, 2)
  5. -- Bad
  6. select *
  7. from users
  8. where id in ( 1, 2 )

9.in 查询 将值多个缩进行

  1. -- Good
  2. select *
  3. from users
  4. where email in (
  5. 'user-1@example.com',
  6. 'user-2@example.com',
  7. 'user-3@example.com',
  8. 'user-4@example.com'
  9. )

10.表名应该是名词的复数形式

  1. -- Good
  2. select * from users
  3. select * from visit_logs
  4. -- Bad
  5. select * from user
  6. select * from visitLog

11. 字段使用 snake_case名字

  1. -- Good
  2. select
  3. id,
  4. email,
  5. timestamp_trunc(created_at, month) as signup_month
  6. from users
  7. -- Bad
  8. select
  9. id,
  10. email,
  11. timestamp_trunc(created_at, month) as SignupMonth
  12. from users

12.列名约定

  • 布尔字段 的前缀应该是 is_has_does_ 。例如,is_customer、has_unsubscribe等
  • 日期的字段 应该以 _date 作为后缀。例如,report_date。
  • 日期+时间字段 应以 _at 为后缀。例如,created_at,posted_at等。

13. 列顺序约定

首先放置主键,然后是外键,然后是所有其他列。如果表有任何系统列(created_at,updated_at,is_deleted等),将它们放在最后。

  1. -- Good
  2. select
  3. id,
  4. name,
  5. created_at
  6. from users
  7. -- Bad
  8. select
  9. created_at,
  10. name,
  11. id,
  12. from users

14. Include inner for inner joins

最好显式,以便连接类型非常清楚:

  1. -- Good
  2. select
  3. email,
  4. sum(amount) as total_revenue
  5. from users
  6. inner join charges on users.id = charges.user_id
  7. -- Bad
  8. select
  9. email,
  10. sum(amount) as total_revenue
  11. from users
  12. join charges on users.id = charges.user_id

15.对于连接条件,设置后立即放置第一个引用的表

通过这种方式,它可以更容易地确定您的连接是否会导致结果散开

  1. -- Good
  2. select
  3. ...
  4. from users
  5. left join charges on users.id = charges.user_id
  6. -- primary_key = foreign_key --> one-to-many --> fanout
  7. select
  8. ...
  9. from charges
  10. left join users on charges.user_id = users.id
  11. -- foreign_key = primary_key --> many-to-one --> no fanout
  12. -- Bad
  13. select
  14. ...
  15. from users
  16. left join charges on charges.user_id = users.id

16.单个连接条件应与连接位于同一行

  1. -- Good
  2. select
  3. email,
  4. sum(amount) as total_revenue
  5. from users
  6. inner join charges on users.id = charges.user_id
  7. group by email
  8. -- Bad
  9. select
  10. email,
  11. sum(amount) as total_revenue
  12. from users
  13. inner join charges
  14. on users.id = charges.user_id
  15. group by email

当你有多个连接条件时,将每个条件放在它们自己的缩进行中:

  1. -- Good
  2. select
  3. email,
  4. sum(amount) as total_revenue
  5. from users
  6. inner join charges on
  7. users.id = charges.user_id and
  8. refunded = false
  9. group by email

17.避免别名表

  1. -- Good
  2. select
  3. email,
  4. sum(amount) as total_revenue
  5. from users
  6. inner join charges on users.id = charges.user_id
  7. -- Bad
  8. select
  9. email,
  10. sum(amount) as total_revenue
  11. from users u
  12. inner join charges c on u.id = c.user_id

唯一的例外是,当需要多次连接到一个表并需要区分它们时。

18. 除非必须,否则不要包含表名

  1. -- Good
  2. select
  3. id,
  4. name
  5. from companies
  6. -- Bad
  7. select
  8. companies.id,
  9. companies.name
  10. from companies

19.始终重命名聚合和函数包装的参数

  1. -- Good
  2. select count(*) as total_users
  3. from users
  4. -- Bad
  5. select count(*)
  6. from users
  7. -- Good
  8. select timestamp_millis(property_beacon_interest) as expressed_interest_at
  9. from hubspot.contact
  10. where property_beacon_interest is not null
  11. -- Bad
  12. select timestamp_millis(property_beacon_interest)
  13. from hubspot.contact
  14. where property_beacon_interest is not null

20.明确布尔条件

  1. -- Good
  2. select * from customers where is_cancelled = true
  3. select * from customers where is_cancelled = false
  4. -- Bad
  5. select * from customers where is_cancelled
  6. select * from customers where not is_cancelled

21. 使用as给字段起别名

  1. -- Good
  2. select
  3. id,
  4. email,
  5. timestamp_trunc(created_at, month) as signup_month
  6. from users
  7. -- Bad
  8. select
  9. id,
  10. email,
  11. timestamp_trunc(created_at, month) signup_month
  12. from users

22.按列名分组,而不是按编号分组

  1. -- Good
  2. select user_id, count(*) as total_charges
  3. from charges
  4. group by user_id
  5. -- Bad
  6. select
  7. user_id,
  8. count(*) as total_charges
  9. from charges
  10. group by 1

23.使用列别名分组

  1. -- Good
  2. select
  3. timestamp_trunc(com_created_at, year) as signup_year,
  4. count(*) as total_companies
  5. from companies
  6. group by signup_year
  7. -- Bad
  8. select
  9. timestamp_trunc(com_created_at, year) as signup_year,
  10. count(*) as total_companies
  11. from companies
  12. group by timestamp_trunc(com_created_at, year)

24.首先对列进行分组

  1. -- Good
  2. select
  3. timestamp_trunc(com_created_at, year) as signup_year,
  4. count(*) as total_companies
  5. from companies
  6. group by signup_year
  7. -- Bad
  8. select
  9. count(*) as total_companies,
  10. timestamp_trunc(com_created_at, year) as signup_year
  11. from mysql_helpscout.helpscout_companies
  12. group by signup_year

25. when 声明的案例

  1. -- Good
  2. select
  3. case
  4. when event_name = 'viewed_homepage' then 'Homepage'
  5. when event_name = 'viewed_editor' then 'Editor'
  6. end as page_name
  7. from events
  8. -- Good too
  9. select
  10. case
  11. when event_name = 'viewed_homepage'
  12. then 'Homepage'
  13. when event_name = 'viewed_editor'
  14. then 'Editor'
  15. end as page_name
  16. from events
  17. -- Bad
  18. select
  19. case when event_name = 'viewed_homepage' then 'Homepage'
  20. when event_name = 'viewed_editor' then 'Editor'
  21. end as page_name
  22. from events

26. Use CTEs, not subqueries

避免子查询;cte将使您的查询更容易阅读和推理。
使用CTEs时,用新行填充查询。
如果您使用任何CTE,请始终使用名为final的CTE,并在末尾选择* from final。这样,您就可以快速检查查询中用于调试结果的其他cte的输出。
关闭CTE圆括号时应使用与with和CTE名称相同的缩进级别。

  1. -- Good
  2. with ordered_details as (
  3. select
  4. user_id,
  5. name,
  6. row_number() over (partition by user_id order by date_updated desc) as details_rank
  7. from billingdaddy.billing_stored_details
  8. ),
  9. final as (
  10. select user_id, name
  11. from ordered_details
  12. where details_rank = 1
  13. )
  14. select * from final
  15. -- Bad
  16. select user_id, name
  17. from (
  18. select
  19. user_id,
  20. name,
  21. row_number() over (partition by user_id order by date_updated desc) as details_rank
  22. from billingdaddy.billing_stored_details
  23. ) ranked
  24. where details_rank = 1

27.使用有意义的CTE名称

  1. -- Good
  2. with ordered_details as (
  3. -- Bad
  4. with d1 as (

28. Window functions

您可以将其全部保留在自己的行中,也可以根据其长度将其分解为多个:

  1. -- Good
  2. select
  3. user_id,
  4. name,
  5. row_number() over (partition by user_id order by date_updated desc) as details_rank
  6. from billingdaddy.billing_stored_details
  7. -- Good
  8. select
  9. user_id,
  10. name,
  11. row_number() over (
  12. partition by user_id
  13. order by date_updated desc
  14. ) as details_rank
  15. from billingdaddy.billing_stored_details

原文:链接