使SQL更易于阅读的几个小技巧

简介: 无论是数仓开发还是数据分析,写一手好的SQL是一项基本的技能。毋庸置疑,编写性能较好的SQL是非常重要的,但是,SQL的可读性同样是不容小觑的。一个有着混乱格式的SQL脚本,往往需要花费较长的时间去弄清楚脚本的具体逻辑。

 

无论是数仓开发还是数据分析,写一手好的SQL是一项基本的技能。毋庸置疑,编写性能较好的SQL是非常重要的,但是,SQL的可读性同样是不容小觑的。一个有着混乱格式的SQL脚本,往往需要花费较长的时间去弄清楚脚本的具体逻辑。如果你曾经被祖传的毫无章法的SQL脚本狂虐过,你一定心有感触。本文将分享几个SQL格式的规范,当然仁者见仁智者见智,其实没有严格的标准,如果有,那就是保证易于阅读和易于维护。

大小写保持一致

可以对SQL关键字使用不同的大小写,但是要保持一致。看看这个:

SELECT customer_city,count(*) from dim_customer WHERE customerProvince = '上海' Group by customer_city

上面的SQL语句是不是很让人抓狂,大小写混用,看起来很不规范。总结起来,要注意下面几点:

  • SQL的关键字可以大写,也可以小写,但是不要大小写混用。上面的SQL查询既有完全大写,也有首字母大写,更有小写。看似是不拘小节,但是万万使不得。
  • 由于大小写是混合的,因此很难区分小写的关键字实际上是关键字还是列。此外,阅读也很烦人。
  • 字段命名要保持一致的风格,上面的SQL与中customer_city是小写加下划线,而customerProvince字段是驼峰命名法,这种不一致性显然是不可取的。

进行一些规范之后后,查询应如下所示:

SELECT customer_city,
       count(*)
FROM dim_customer
WHERE customer_province = '上海'
GROUP BY customer_city

使用缩进

再来看看下面的一条查询语句:

SELECT dp.region_name,count(*) FROM user_behavior_log ubl JOIN dim_province dp ON ubl.province = dp.province_name WHERE ubl.province = '上海市' GROUP BY dp.region_name

将上面的SQL语句格式化下面的形式:

SELECT dp.region_name, count(*)
FROM user_behavior_log ubl
JOIN dim_province dp ON ubl.province = dp.province_name
WHERE ubl.province = '上海市'
GROUP BY dp.region_name

上面的格式化形式似乎清晰了很多,但是如果语句中包含了子查询、多个JOIN以及窗口函数时,同样会显得对阅读不是很友好。

再换一种格式化方式,如下:

SELECT
    dp.region_name, 
    count(*)
FROM user_behavior_log ubl
    JOIN dim_province dp ON ubl.province = dp.province_name
WHERE ubl.province = '上海市'
GROUP BY
    dp.region_name

-- 或者下面的形式
SELECT
    dp.region_name 
    ,count(*)
FROM user_behavior_log ubl
    JOIN dim_province dp ON ubl.province = dp.province_name
WHERE ubl.province = '上海市'
GROUP BY
    dp.region_name

尖叫提示:对于第二种形式,在SELECT字段中,从第二个字段开始,每个字段前面添加一个逗号,而不是每个字段后面使用逗号结尾。这种方式可以很方便地识别FROM前面是否存在逗号,从而造成语法错误。当然,这个只是个人习惯问题,并不是硬性的规定。

另外上面的SQL语句使用了4个字符缩进,当然也可以选择2个字符缩进,这个也是个人习惯问题。

在group by 和order by之后使用字段的排列序号

同样,这种书写风格也是个人的一种偏好,并不是一条硬性规定。应该有很多的初学者对此种写法并不是很清楚。

看下面的这条SQL:

SELECT
    dp.region_name, 
    dp.province_name,
    count(*)
FROM user_behavior_log ubl
    JOIN dim_province dp ON ubl.province = dp.province_name
GROUP BY
    dp.region_name,
    dp.province_name
ORDER BY
    count(*) desc -- Hive不支持

可以写成下面的形式:

-- 注意:MySQL、Impala支持这种写法,Hive不支持

SELECT
    dp.region_name, 
    dp.province_name,
    count(*)
FROM user_behavior_log ubl
    JOIN dim_province dp ON ubl.province = dp.province_name
GROUP BY 1,2
ORDER BY 3 

这样写有如下的好处:

  • 可以节省行:通过许多字段进行分组不仅会在SELECT子句中添加更多行,还会在GROUP BY和ORDER BY子句中添加更多行,甚至可能使查询中的行数增加一倍。
  • 可维护性:如果想改变分组字段,只需在SELECT子句中进行操作,在GROUP BY语句中不需要修改。
  • 方便:只需要GROUP BY 1,2,3,…,n,其中n为分组列的字段序号。

使用Common Table表达式(with语句)

该方式称之为Common Table Expressions(CTE),用来简化复杂查询。它们可以定义为临时视图,因为它们仅在整个查询执行期间存在。

看一个简单的例子:

-- 注意Hive、Impala支持这种语法,低版本的MySQL不支持(高版本支持)
WITH employee_by_title_count AS (
    SELECT
        t.name as job_title
        , COUNT(e.id) as amount_of_employees
    FROM employees e
        JOIN job_titles t on e.job_title_id = t.id
    GROUP BY 1
),
salaries_by_title AS (
     SELECT
         name as job_title
         , salary
     FROM job_titles
)
SELECT *
FROM employee_by_title_count e
    JOIN salaries_by_title s ON s.job_title = e.job_title

上面的语句中,最终的查询使用employee_by_title和salaries_by_title的两个结果集进行JOIN产生最终结果。这比在SELECT子句中或直接在FROM子句中进行子查询更具可读性和可维护性。

使用具有描述性的别名

这一点非常重要,如果查询的列字段很多,肯能会存在一些id,count(*)等,很难辨识代表什么含义,所以需要为每个查询列加上可读的、易于理解的别名,能够让其他人一眼就能看出代表什么含义,这样可以增加脚本的可维护性。

总结

文中提到的一些规范有些是必须要遵守的,有些是个人的编码习惯,无论你是开发人员、数据分析师、数仓开发,遵循一些规范可以避免不必要的麻烦。值得注意的是,关于SQL的格式,没有一个标准的约定,需要与团队的其他成员达成共识,一起按照相同的约定进行开发,从而可以大大提高脚本的可读性和可维护性。


本文作者:西贝木土

目录
相关文章
|
SQL 存储 程序员
【Sql Server】创建触发器,以及通过触发器来统计阅读数量(下)
在本篇文章中,主要来讲讲数据库中的触发器功能 在实际项目中,触发器多数用到在数据添加、修改、删除时,额外去做的一些操作,比如:数据删除了,会通过触发器将删除数据备份到另一个表,再或者文章被阅读,通过触发器更新阅读数量
203 0
【Sql Server】创建触发器,以及通过触发器来统计阅读数量(下)
|
SQL 存储 自然语言处理
|
SQL 分布式计算 HIVE
Spark修炼之道(高级篇)——Spark源码阅读:第十三节 Spark SQL之SQLContext(一)
作者:周志湖 1. SQLContext的创建 SQLContext是Spark SQL进行结构化数据处理的入口,可以通过它进行DataFrame的创建及SQL的执行,其创建方式如下: //sc为SparkContext val sqlContext = new org.apache.spark.sql.SQLContext(sc) 其对应的源码为: def
2885 0
|
SQL 分布式计算 Spark
Spark修炼之道(高级篇)——Spark源码阅读:第十二节 Spark SQL 处理流程分析
作者:周志湖 下面的代码演示了通过Case Class进行表Schema定义的例子: // sc is an existing SparkContext. val sqlContext = new org.apache.spark.sql.SQLContext(sc) // this is used to implicitly convert an RDD to a Da
3360 0
|
15天前
|
SQL 人工智能 算法
【SQL server】玩转SQL server数据库:第二章 关系数据库
【SQL server】玩转SQL server数据库:第二章 关系数据库
52 10
|
1月前
|
SQL 数据库 数据安全/隐私保护
Sql Server数据库Sa密码如何修改
Sql Server数据库Sa密码如何修改
|
25天前
|
SQL
启动mysq异常The server quit without updating PID file [FAILED]sql/data/***.pi根本解决方案
启动mysq异常The server quit without updating PID file [FAILED]sql/data/***.pi根本解决方案
17 0
|
15天前
|
SQL 算法 数据库
【SQL server】玩转SQL server数据库:第三章 关系数据库标准语言SQL(二)数据查询
【SQL server】玩转SQL server数据库:第三章 关系数据库标准语言SQL(二)数据查询
88 6
|
2天前
|
SQL 数据管理 关系型数据库
如何在 Windows 上安装 SQL Server,保姆级教程来了!
在Windows上安装SQL Server的详细步骤包括:从官方下载安装程序(如Developer版),选择自定义安装,指定安装位置(非C盘),接受许可条款,选中Microsoft更新,忽略警告,取消“适用于SQL Server的Azure”选项,仅勾选必要功能(不包括Analysis Services)并更改实例目录至非C盘,选择默认实例和Windows身份验证模式,添加当前用户,最后点击安装并等待完成。安装成功后关闭窗口。后续文章将介绍SSMS的安装。
6 0
|
10天前
|
SQL 安全 网络安全
IDEA DataGrip连接sqlserver 提示驱动程序无法通过使用安全套接字层(SSL)加密与 SQL Server 建立安全连接的解决方法
IDEA DataGrip连接sqlserver 提示驱动程序无法通过使用安全套接字层(SSL)加密与 SQL Server 建立安全连接的解决方法
24 0