mirror of
https://github.com/treffynnon/sqlstyle.guide.git
synced 2025-03-09 12:49:51 -05:00
commit
ea4d592ed8
2 changed files with 146 additions and 92 deletions
|
@ -1,6 +1,6 @@
|
||||||
# SQL style guide
|
# SQL style guide
|
||||||
|
|
||||||
**[Read the guide](http://www.sqlstyle.guide)**
|
**[Read the guide](_includes/sqlstyle.guide.md)**
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
@ -2,21 +2,17 @@
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
You can use this set of guidelines, [fork them][fork] or make your own - the
|
These are guidelines to help you write SQL queries that will be easier to read.
|
||||||
key here is that you pick a style and stick to it. To suggest changes
|
|
||||||
or fix bugs please open an [issue][] or [pull request][pull] on GitHub.
|
|
||||||
|
|
||||||
These guidelines are designed to be compatible with Joe Celko's [SQL Programming
|
Remember that even if you hate a given style at first, generally speaking it is
|
||||||
Style][celko] book to make adoption for teams who have already read that book
|
far more important that we have _any_ agreed upon style than that we all like it.
|
||||||
easier. This guide is a little more opinionated in some areas and in others a
|
|
||||||
little more relaxed. It is certainly more succinct where [Celko's book][celko]
|
|
||||||
contains anecdotes and reasoning behind each rule as thoughtful prose.
|
|
||||||
|
|
||||||
It is easy to include this guide in [Markdown format][dl-md] as a part of a
|
Queries submitted to the Data teams should follow the style guide, and queries starkly
|
||||||
project's code base or reference it here for anyone on the project to freely
|
contrasting to, or ignorant of, these guidelines may be asked to be reformatted and
|
||||||
read—much harder with a physical book.
|
resubmitted. Feel free to ask about style rationale, or pose a question how you can make
|
||||||
|
your query (or often taking a step back, question) adhere.
|
||||||
|
|
||||||
SQL style guide by [Simon Holywell][simon] is licensed under a [Creative Commons
|
Original SQL style guide by [Simon Holywell][simon] is licensed under a [Creative Commons
|
||||||
Attribution-ShareAlike 4.0 International License][licence].
|
Attribution-ShareAlike 4.0 International License][licence].
|
||||||
Based on a work at [http://www.sqlstyle.guide][self].
|
Based on a work at [http://www.sqlstyle.guide][self].
|
||||||
|
|
||||||
|
@ -45,28 +41,15 @@ Based on a work at [http://www.sqlstyle.guide][self].
|
||||||
* Quoted identifiers—if you must use them then stick to SQL92 double quotes for
|
* Quoted identifiers—if you must use them then stick to SQL92 double quotes for
|
||||||
portability (you may need to configure your SQL server to support this depending
|
portability (you may need to configure your SQL server to support this depending
|
||||||
on vendor).
|
on vendor).
|
||||||
* Object oriented design principles should not be applied to SQL or database
|
|
||||||
structures.
|
|
||||||
|
|
||||||
```sql
|
|
||||||
SELECT file_hash -- stored ssdeep hash
|
|
||||||
FROM file_system
|
|
||||||
WHERE file_name = '.vimrc';
|
|
||||||
```
|
|
||||||
```sql
|
|
||||||
/* Updating the file record after writing to the file */
|
|
||||||
UPDATE file_system
|
|
||||||
SET file_modified_date = '1980-02-22 13:19:01.00000',
|
|
||||||
file_size = 209732
|
|
||||||
WHERE file_name = '.vimrc';
|
|
||||||
```
|
|
||||||
|
|
||||||
## Naming conventions
|
## Naming conventions
|
||||||
|
|
||||||
### General
|
### General
|
||||||
|
|
||||||
* Ensure the name is unique and does not exist as a
|
* Ensure the name is unique and does not exist as a [MySQLreserved keyword][reserved-keywords]
|
||||||
[reserved keyword][reserved-keywords].
|
or [Redshift reserved keyword](http://docs.aws.amazon.com/redshift/latest/dg/r_pg_keywords.html)
|
||||||
|
* Avoid abbreviations and if you have to use them make sure they are commonly
|
||||||
|
understood.
|
||||||
* Keep the length to a maximum of 30 bytes—in practice this is 30 characters
|
* Keep the length to a maximum of 30 bytes—in practice this is 30 characters
|
||||||
unless you are using multi-byte character set.
|
unless you are using multi-byte character set.
|
||||||
* Names must begin with a letter and may not end with an underscore.
|
* Names must begin with a letter and may not end with an underscore.
|
||||||
|
@ -74,8 +57,6 @@ UPDATE file_system
|
||||||
* Avoid the use of multiple consecutive underscores—these can be hard to read.
|
* Avoid the use of multiple consecutive underscores—these can be hard to read.
|
||||||
* Use underscores where you would naturally include a space in the name (first
|
* Use underscores where you would naturally include a space in the name (first
|
||||||
name becomes `first_name`).
|
name becomes `first_name`).
|
||||||
* Avoid abbreviations and if you have to use them make sure they are commonly
|
|
||||||
understood.
|
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
SELECT first_name
|
SELECT first_name
|
||||||
|
@ -84,13 +65,9 @@ SELECT first_name
|
||||||
|
|
||||||
### Tables
|
### Tables
|
||||||
|
|
||||||
* Use a collective name or, less ideally, a plural form. For example (in order of
|
|
||||||
preference) `staff` and `employees`.
|
|
||||||
* Do not prefix with `tbl` or any other such descriptive prefix or Hungarian
|
* Do not prefix with `tbl` or any other such descriptive prefix or Hungarian
|
||||||
notation.
|
notation.
|
||||||
* Never give a table the same name as one of its columns and vice versa.
|
* Never give a table the same name as one of its columns and vice versa.
|
||||||
* Avoid, where possible, concatenating two table names together to create the name
|
|
||||||
of a relationship table. Rather than `cars_mechanics` prefer `services`.
|
|
||||||
|
|
||||||
### Columns
|
### Columns
|
||||||
|
|
||||||
|
@ -126,19 +103,24 @@ SELECT SUM(s.monitor_tally) AS monitor_total
|
||||||
* Do not prefix with `sp_` or any other such descriptive prefix or Hungarian
|
* Do not prefix with `sp_` or any other such descriptive prefix or Hungarian
|
||||||
notation.
|
notation.
|
||||||
|
|
||||||
|
### Uniform prefixes
|
||||||
|
|
||||||
|
* `is_` - denotes a boolean
|
||||||
|
|
||||||
### Uniform suffixes
|
### Uniform suffixes
|
||||||
|
|
||||||
The following suffixes have a universal meaning ensuring the columns can be read
|
The following suffixes have a universal meaning ensuring the columns can be read
|
||||||
and understood easily from SQL code. Use the correct suffix where appropriate.
|
and understood easily from SQL code. Use the correct suffix where appropriate.
|
||||||
|
|
||||||
* `_id`—a unique identifier such as a column that is a primary key.
|
* `_id`—a unique identifier such as a column that is a primary key.
|
||||||
|
* `_at`-denotes a column that contains the time of something.
|
||||||
|
* `_date`—denotes a column that contains the date of something.
|
||||||
* `_status`—flag value or some other status of any type such as
|
* `_status`—flag value or some other status of any type such as
|
||||||
`publication_status`.
|
`publication_status`.
|
||||||
* `_total`—the total or sum of a collection of values.
|
* `_total`—the total or sum of a collection of values.
|
||||||
* `_num`—denotes the field contains any kind of number.
|
* `_num`—denotes the field contains any kind of number.
|
||||||
* `_name`—signifies a name such as `first_name`.
|
* `_name`—signifies a name such as `first_name`.
|
||||||
* `_seq`—contains a contiguous sequence of values.
|
* `_seq`—contains a contiguous sequence of values.
|
||||||
* `_date`—denotes a column that contains the date of something.
|
|
||||||
* `_tally`—a count.
|
* `_tally`—a count.
|
||||||
* `_size`—the size of something such as a file size or clothing.
|
* `_size`—the size of something such as a file size or clothing.
|
||||||
* `_addr`—an address for the record could be physical or intangible such as
|
* `_addr`—an address for the record could be physical or intangible such as
|
||||||
|
@ -170,22 +152,6 @@ spacing is used. Do not crowd code or remove natural language spaces.
|
||||||
|
|
||||||
#### Spaces
|
#### Spaces
|
||||||
|
|
||||||
Spaces should be used to line up the code so that the root keywords all end on
|
|
||||||
the same character boundary. This forms a river down the middle making it easy for
|
|
||||||
the readers eye to scan over the code and separate the keywords from the
|
|
||||||
implementation detail. Rivers are [bad in typography][rivers], but helpful here.
|
|
||||||
|
|
||||||
```sql
|
|
||||||
SELECT f.average_height, f.average_diameter
|
|
||||||
FROM flora AS f
|
|
||||||
WHERE f.species_name = 'Banksia'
|
|
||||||
OR f.species_name = 'Sheoak'
|
|
||||||
OR f.species_name = 'Wattle';
|
|
||||||
```
|
|
||||||
|
|
||||||
Notice that `SELECT`, `FROM`, etc. are all right aligned while the actual column
|
|
||||||
names and implementation specific details are left aligned.
|
|
||||||
|
|
||||||
Although not exhaustive always include spaces:
|
Although not exhaustive always include spaces:
|
||||||
|
|
||||||
* before and after equals (`=`)
|
* before and after equals (`=`)
|
||||||
|
@ -205,6 +171,7 @@ SELECT a.title, a.release_date, a.recording_date
|
||||||
Always include newlines/vertical space:
|
Always include newlines/vertical space:
|
||||||
|
|
||||||
* before `AND` or `OR`
|
* before `AND` or `OR`
|
||||||
|
* after WITH subqueries
|
||||||
* after semicolons to separate queries for easier reading
|
* after semicolons to separate queries for easier reading
|
||||||
* after each keyword definition
|
* after each keyword definition
|
||||||
* after a comma when separating multiple columns into logical groups
|
* after a comma when separating multiple columns into logical groups
|
||||||
|
@ -240,42 +207,127 @@ SELECT a.title,
|
||||||
To ensure that SQL is readable it is important that standards of indentation
|
To ensure that SQL is readable it is important that standards of indentation
|
||||||
are followed.
|
are followed.
|
||||||
|
|
||||||
|
**ONLY** the fundamental keywords - `SELECT`, `FROM`, `WHERE`, `GROUP BY`, `HAVING`, `LIMIT`,
|
||||||
|
and `ORDER BY`should be fully left justified. Other clauses should be indented to the end of
|
||||||
|
that keyword.
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SELECT first_name,
|
||||||
|
last_name,
|
||||||
|
is_still_tippin_on_four_fours,
|
||||||
|
is_still_wrapped_in_four_vogues
|
||||||
|
FROM rappers
|
||||||
|
WHERE first_name = 'Mike'
|
||||||
|
AND last_name = 'Jones'
|
||||||
|
```
|
||||||
|
|
||||||
|
This allows the reader to quickly scan for the important building blocks of the query.
|
||||||
|
|
||||||
#### Joins
|
#### Joins
|
||||||
|
|
||||||
Joins should be indented to the other side of the river and grouped with a new
|
Joins should be indented 2 spaces right from the `FROM` keyword
|
||||||
line where necessary.
|
|
||||||
|
Single line `JOIN`s are fine for simple situations
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SELECT r.last_name
|
||||||
|
FROM riders AS r
|
||||||
|
INNER JOIN bikes AS b ON r.bike_vin_num = b.vin_num
|
||||||
|
INNER JOIN crew AS c ON r.crew_chief_last_name = c.last_name
|
||||||
|
```
|
||||||
|
|
||||||
|
Multi line JOINs should be indented the same as base keywords:
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
SELECT r.last_name
|
SELECT r.last_name
|
||||||
FROM riders AS r
|
FROM riders AS r
|
||||||
INNER JOIN bikes AS b
|
INNER JOIN bikes AS b
|
||||||
ON r.bike_vin_num = b.vin_num
|
ON r.bike_vin_num = b.vin_num
|
||||||
AND b.engines > 2
|
AND r.bike_lane = r.lane
|
||||||
|
INNER JOIN crew c ON r.crew_chief_last_name = c.last_name
|
||||||
|
WHERE id = 5
|
||||||
|
```
|
||||||
|
|
||||||
INNER JOIN crew AS c
|
#### WITH statements (PostgreSQL only)
|
||||||
ON r.crew_chief_last_name = c.last_name
|
|
||||||
AND c.chief = 'Y';
|
Indent them until the closing parentheses.
|
||||||
|
|
||||||
|
```sql
|
||||||
|
WITH my_tmp_table AS (
|
||||||
|
SELECT r.last_name
|
||||||
|
FROM riders AS r
|
||||||
|
INNER JOIN bikes AS b ON r.bike_vin_num = b.vin_num
|
||||||
|
WHERE id = 10
|
||||||
|
),
|
||||||
|
|
||||||
|
my_other_tmp_table AS (
|
||||||
|
SELECT last_name
|
||||||
|
FROM staff
|
||||||
|
)
|
||||||
|
|
||||||
|
SELECT *
|
||||||
|
FROM my_tmp_table
|
||||||
|
JOIN my_other_tmp_table ON my_other_tmp_table.last_name = my_tmp_table.last_name
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Sub-queries
|
#### Sub-queries
|
||||||
|
|
||||||
|
In PostgreSQL you should be doing subqueries with `WITH` clauses.
|
||||||
|
|
||||||
Sub-queries should also be aligned to the right side of the river and then laid
|
Sub-queries should also be aligned to the right side of the river and then laid
|
||||||
out using the same style as any other query. Sometimes it will make sense to have
|
out using the same style as a `WITH` statement w/r/t parentheses.
|
||||||
the closing parenthesis on a new line at the same character position as it's
|
|
||||||
opening partner—this is especially true where you have nested sub-queries.
|
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
SELECT r.last_name,
|
SELECT r.last_name,
|
||||||
(SELECT MAX(YEAR(championship_date))
|
(
|
||||||
|
SELECT MAX(YEAR(championship_date))
|
||||||
FROM champions AS c
|
FROM champions AS c
|
||||||
WHERE c.last_name = r.last_name
|
WHERE c.last_name = r.last_name
|
||||||
AND c.confirmed = 'Y') AS last_championship_year
|
AND c.confirmed = 'Y'
|
||||||
|
) AS last_championship_year
|
||||||
FROM riders AS r
|
FROM riders AS r
|
||||||
WHERE r.last_name IN
|
WHERE r.last_name IN
|
||||||
(SELECT c.last_name
|
(
|
||||||
|
SELECT c.last_name
|
||||||
FROM champions AS c
|
FROM champions AS c
|
||||||
WHERE YEAR(championship_date) > '2008'
|
WHERE YEAR(championship_date) > '2008'
|
||||||
AND c.confirmed = 'Y');
|
AND c.confirmed = 'Y'
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Case statements (PostreSQL)
|
||||||
|
|
||||||
|
`CASE` and `END` can either be inline:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SELECT CASE WHEN x > y THEN 1 ELSE 0 END
|
||||||
|
FROM table
|
||||||
|
```
|
||||||
|
|
||||||
|
or should have the same left justification, and `WHEN`/`THEN` should be indented the same as the `ELSE`/`value`.
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SELECT CASE
|
||||||
|
WHEN x > y AND x < z
|
||||||
|
THEN 'x more than y but less than z'
|
||||||
|
WHEN x > y AND x > z
|
||||||
|
THEN 'x more than y and more than z'
|
||||||
|
ELSE
|
||||||
|
'x and y not related'
|
||||||
|
END AS city
|
||||||
|
FROM office_locations
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Case statements (MySql)
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SELECT CASE postcode
|
||||||
|
WHEN 'BN1'
|
||||||
|
THEN 'Brighton'
|
||||||
|
WHEN 'EH1'
|
||||||
|
THEN 'Edinburgh'
|
||||||
|
END AS city
|
||||||
|
FROM office_locations
|
||||||
```
|
```
|
||||||
|
|
||||||
### Preferred formalisms
|
### Preferred formalisms
|
||||||
|
@ -291,8 +343,10 @@ SELECT r.last_name,
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
SELECT CASE postcode
|
SELECT CASE postcode
|
||||||
WHEN 'BN1' THEN 'Brighton'
|
WHEN 'BN1'
|
||||||
WHEN 'EH1' THEN 'Edinburgh'
|
THEN 'Brighton'
|
||||||
|
WHEN 'EH1'
|
||||||
|
THEN 'Edinburgh'
|
||||||
END AS city
|
END AS city
|
||||||
FROM office_locations
|
FROM office_locations
|
||||||
WHERE country = 'United Kingdom'
|
WHERE country = 'United Kingdom'
|
||||||
|
|
Loading…
Reference in a new issue