Last active
          October 14, 2025 08:17 
        
      - 
            
      
        
      
    Star
      
          
          (178)
      
  
You must be signed in to star a gist 
- 
              
      
        
      
    Fork
      
          
          (37)
      
  
You must be signed in to fork a gist 
- 
      
- 
        Save mattmc3/38a85e6a4ca1093816c08d4815fbebfb to your computer and use it in GitHub Desktop. 
Revisions
- 
        mattmc3 revised this gist Jan 17, 2019 . 1 changed file with 4 additions and 4 deletions.There are no files selected for viewingThis file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -2,8 +2,8 @@ layout: default author: mattmc3 title: Modern SQL Style Guide revision: 2019-01-17 version: 1.0.1 description: A guide to writing clean, clear, and consistent SQL. --- @@ -142,7 +142,7 @@ for modern SQL development. Using lowercase keywords is preferred because: [harder to read][shoutcase-typeography]. * SQL syntax is not case-sensitive, and thus lowercase keywords work correctly in all variants of SQL * No other modern languages use ALLCAPS keywords. * Modern editors color code SQL keywords, so there is not a need to distinguish keywords by casing. * If you are in an environment where your keywords are not colored (i.e. as a @@ -176,7 +176,7 @@ sake of consistency rather than mixing styles. * Tables with semantic prefixes are okay if they aid understanding the nature of a table (e.g. in a Data Warehouse where it is common to use prefixes like `Dim` and `Fact`). * Avoid giving a table the same name as one of its columns. * Use a joining word for many-to-many joining tables (cross references) rather than concatenating table names (e.g. `Xref`): 
- 
        mattmc3 revised this gist Dec 19, 2018 . 1 changed file with 51 additions and 46 deletions.There are no files selected for viewingThis file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,25 +1,26 @@ --- layout: default author: mattmc3 title: Modern SQL Style Guide revision: 2018-12-18 version: 1.0.0 description: A guide to writing clean, clear, and consistent SQL. --- # Modern SQL Style Guide ```sql select * from modern.sql_style_guide as guide where guide.attributes in ('clean', 'clear', 'consistent') and guide.look = 'beautiful' ``` ## Purpose These guidelines are designed to make SQL statements easy to write, easy to read, easy to maintain, and beautiful to see. This document is to be used as a guide for anyone who would like to codify a team's preferred SQL style. This guide is opinionated in some areas and relaxed in others. You can use this set of guidelines, fork them, or make your own - the key here is that you pick a @@ -29,16 +30,17 @@ is a guiding principle to achieve cohesion. It is easy to include this guide in Markdown format as a part of a project's code base or reference it here for anyone on the project to freely read. This guide is based on various existing attempts at SQL standards including: [http://www.sqlstyle.guide][sqlstyleguide] and [Kickstarter guide][kickstarter-sql-guide]. Due to its origins, it is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License][license]. The example SQL statements used are based on tables in the [AdventureWorks][adventureworks] database. Note that due to the use of the existing AdventureWorks schema, some of the guidelines in this document are not always followed, especially with regards to naming conventions. Those discrepancies will be called out as they appear. **NOTE**: This style guide is written for use with [Microsoft SQL Server][mssql], but much of it can be applied to any SQL database with some @@ -58,8 +60,9 @@ simple modifications. ## Quick look Before getting into all the specifics, here is a quick look at some examples showing well formatted, beautiful SQL that matches the recommendations in this style guide: ```sql -- basic select example @@ -79,32 +82,25 @@ select p.Name as ProductName ```sql -- basic insert example insert into Sales.Currency (CurrencyCode, Name, ModifiedDate) values ('XBT', 'Bitcoin', getutcdate()) , ('ETH', 'Ethereum', getutcdate()) ``` ```sql -- basic update example update p set p.ListPrice = p.ListPrice * 1.05 , p.ModifiedDate = getutcdate() from Production.Product as p where p.SellEndDate is null and p.SellStartDate is not null ``` ```sql -- basic delete example delete cc from Sales.CreditCard as cc where cc.ExpYear < '2003' and cc.ModifiedDate < dateadd(year, -1, getutcdate()) ``` @@ -145,14 +141,19 @@ for modern SQL development. Using lowercase keywords is preferred because: * UPPERCASE words are harder to type and [harder to read][shoutcase-typeography]. * SQL syntax is not case-sensitive, and thus lowercase keywords work correctly in all variants of SQL * No other modern languages since BASIC use ALLCAPS keywords. * Modern editors color code SQL keywords, so there is not a need to distinguish keywords by casing. * If you are in an environment where your keywords are not colored (i.e. as a string in another language), using a river for formatting provides a similar benefit of highlighting important keywords without resorting to CAPS. * UPPERCASE IS ASSOCIATED WITH SHOUTING WHEN SEEN IN TEXT, IS HARD TO READ, AND MAKES SQL FEEL MORE LIKE COBOL THAN A MODERN LANGUAGE. If the SQL script you are editing already uses SHOUTCASE keywords, match that style or change *all* keywords to lowercase. Favor bending the rules for the sake of consistency rather than mixing styles. ### Naming guidance @@ -171,27 +172,31 @@ style or change all keywords to lowercase. Favor consistency over mixed casing. * Naming a table as a plural makes the table easier to speak about. (e.g. favor `employees` over `employee`) * Do not use object prefixes or Hungarian notation (e.g. `sp_`, `prc_`, `vw_`, `tbl_`, `t_`, `fn_`, etc). * Tables with semantic prefixes are okay if they aid understanding the nature of a table (e.g. in a Data Warehouse where it is common to use prefixes like `Dim` and `Fact`). * Never give a table the same name as one of its columns and vice versa. * Use a joining word for many-to-many joining tables (cross references) rather than concatenating table names (e.g. `Xref`): __GOOD__: `drivers_xref_cars` __BAD__: `drivers_cars` * Tables should always have a primary key. A single column, auto-number (identity) surrogate key is preferable. * Natural keys or composite keys can be enforced with unique constraints in lieu of making them a primary key. * Composite keys make for verbose and slow foreign key joins. `int`/`bigint` primary keys are optimal as foreign keys when a table gets large. * Tables should always have `created_at` and `updated_at` metadata fields in them to make data movement between systems easier (ETL). Also, consider storing deleted records in archival tables, or having a `deleted_at` field for soft deletes. * Don't forget the needs of data analysts and ETL developers when designing your model. #### Columns @@ -206,16 +211,18 @@ style or change all keywords to lowercase. Favor consistency over mixed casing. the AdventureWorks database, which commonly has columns named `Name` against this guide's advice. Remember that an existing convention may be in place that is beyond your control._ ) * Do not use `Desc` as an abbreviation for `Description`. Spell it out, or use some other non-keyword. #### Aliases * Aliases should relate in some way to the object or expression they are aliasing. * As a rule of thumb the alias can be the first letter of each word in the object's name or a good abbreviation. * If there is already an alias with the same name then append a number. * When using a subquery, prefix aliases with an `_` to differentiate them from aliases in the outer query. * Always include the `as` keyword. It makes the query easier to read and is explicit. * For computed data (i.e. `sum()` or `avg()`) use the name you would give it were it a column defined in the schema. @@ -723,7 +730,7 @@ delete prd ... ``` __BAD__: ```sql -- A trailing semicolon is sinister. @@ -842,8 +849,6 @@ select per.FirstName, per.LastName order by per.LastName, per.FirstName ``` [kickstarter-sql-guide]: https://gist.github.com/fredbenenson/7bb92718e19138c20591#file-kickstarter_sql_style_guide-md [sqlstyleguide]: http://www.sqlstyle.guide "SQL style guide by Simon Holywell" 
- 
        mattmc3 renamed this gist Dec 18, 2018 . 1 changed file with 12 additions and 12 deletions.There are no files selected for viewingThis file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,12 +1,12 @@ --- layout: default title: Modern SQL Style Guide revision: 2018-12-18 version: 0.1.0-beta description: A guide to writing clean, clear, and consistent SQL. --- # Modern SQL Style Guide ```sql select * @@ -17,9 +17,9 @@ select * ## Purpose These guidelines are designed to make SQL statements easy to write, easy to read, easy to maintain, and beautiful to boot. This document is to be used as a guide for anyone who would like to write clean and clear SQL code. This guide is opinionated in some areas and relaxed in others. You can use this set of guidelines, fork them, or make your own - the key here is that you pick a @@ -29,7 +29,7 @@ is a guiding principle to achieve cohesion. It is easy to include this guide in Markdown format as a part of a project's code base or reference it here for anyone on the project to freely read. The original Modern SQL style guide [homepage is here][homepage] and is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License][license]. @@ -47,7 +47,7 @@ simple modifications. ## Principles * We take a disciplined and practical approach to writing code. * We treat SQL like any other source code, which should be checked into source control, peer reviewed, and properly maintained. * We believe consistency in style is important, and we value craftsmanship, but not to the exclusion of other practical concerns. @@ -113,8 +113,8 @@ delete cc ### General guidance * Favor using a ["river"][rivers] for vertical alignment so that a query can be quickly and easily be scanned by a new reader. * Comments should appear at the top of your query or script, and should explain the intent of the query, not the mechanics. * Try to comment things that aren't obvious about the query (e.g., why a @@ -145,7 +145,7 @@ for modern SQL development. Using lowercase keywords is preferred because: * UPPERCASE words are harder to type and [harder to read][shoutcase-typeography]. * SQL syntax is not case-sensitive, and thus lowercase keywords work correctly in all variants of SQL, not just SQL Server. * Modern editors color code SQL keywords, so there is not a need to distinguish keywords by casing. * UPPERCASE IS ASSOCIATED WITH SHOUTING WHEN SEEN IN TEXT, AND MAKES SQL FEEL @@ -230,7 +230,7 @@ style or change all keywords to lowercase. Favor consistency over mixed casing. * No empty lines in the middle of a single statement. * One final newline at the end of a file * Use an [.editorConfig file][editor-config] to enforce reasonable whitespace rules if your SQL editor supports it: ```ini # .editorConfig is awesome: https://EditorConfig.org @@ -843,7 +843,7 @@ select per.FirstName, per.LastName ``` [homepage]: https://gist.github.com/mattmc3/38a85e6a4ca1093816c08d4815fbebfb "Modern SQL Style Guide" [kickstarter-sql-guide]: https://gist.github.com/fredbenenson/7bb92718e19138c20591#file-kickstarter_sql_style_guide-md [sqlstyleguide]: http://www.sqlstyle.guide "SQL style guide by Simon Holywell" 
- 
        mattmc3 revised this gist Dec 18, 2018 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewingThis file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -379,7 +379,7 @@ If using a `select` modifier like `distinct` or `top`, put the first column on its own line. ```sql -- treat the first column differently when using distinct and top select distinct p.Color , c.Name as CategoryName 
- 
        mattmc3 revised this gist Dec 18, 2018 . 1 changed file with 9 additions and 8 deletions.There are no files selected for viewingThis file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -207,17 +207,18 @@ style or change all keywords to lowercase. Favor consistency over mixed casing. this guide's advice. Remember that an existing convention may be in place that is beyond your control._ ) #### Aliases * Aliases should relate in some way to the object or expression they are aliasing. * As a rule of thumb the alias name should be the first letter of each word in the object's name or a good abbreviation. * If there is already an alias with the same name then append a number. * When using a subquery, prefix aliases with an `_` to differentiate them from aliases in the outer query. * Always include the `as` keyword. It make the query easier to read and is explicit. * For computed data (i.e. `sum()` or `avg()`) use the name you would give it were it a column defined in the schema. ### Whitespace 
- 
        mattmc3 revised this gist Dec 18, 2018 . 1 changed file with 8 additions and 1 deletion.There are no files selected for viewingThis file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -2,12 +2,19 @@ layout: default title: Modern T-SQL Style Guide revision: 2018-12-18 version: 0.1.0-beta description: A guide to writing clean, clear, and consistent T-SQL. --- # Modern T-SQL Style Guide ```sql select * from modern.sql_style_guide guide where guide.attributes in ('clean', 'easy', 'sensible') and guide.look = 'beautiful' ``` ## Purpose These guidelines are designed to make T-SQL statements easy to write, easy to 
- 
        mattmc3 revised this gist Dec 18, 2018 . 1 changed file with 1 addition and 0 deletions.There are no files selected for viewingThis file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,6 +1,7 @@ --- layout: default title: Modern T-SQL Style Guide revision: 2018-12-18 version: 0.0.5-alpha description: A guide to writing clean, clear, and consistent T-SQL. --- 
- 
        mattmc3 revised this gist Dec 18, 2018 . 1 changed file with 22 additions and 11 deletions.There are no files selected for viewingThis file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,7 +1,7 @@ --- layout: default title: Modern T-SQL Style Guide version: 0.0.5-alpha description: A guide to writing clean, clear, and consistent T-SQL. --- @@ -21,8 +21,9 @@ is a guiding principle to achieve cohesion. It is easy to include this guide in Markdown format as a part of a project's code base or reference it here for anyone on the project to freely read. The original Modern T-SQL style guide [homepage is here][homepage] and is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License][license]. Based on various existing attempts at SQL standards including: [http://www.sqlstyle.guide][sqlstyleguide] and @@ -300,13 +301,22 @@ team, then a standard 4 space indent can be used in place of a river. Major keywords starting a clause should occupying their own line. Major keywords are: * Select statement * `select` * `into` * `from` * `where` * `group by` * `having` * `order by` * Insert statement additions * `insert into` * `values` * Update statement additions * `update` * `set` * Delete statement additions * `delete` All other keywords are minor and should appear after the indent and not occupy a line to themselves. Other than this section, this guide will stick to @@ -823,7 +833,8 @@ select per.FirstName, per.LastName order by per.LastName, per.FirstName ``` [homepage]: https://gist.github.com/mattmc3/38a85e6a4ca1093816c08d4815fbebfb "Modern T-SQL Style Guide" [kickstarter-sql-guide]: https://gist.github.com/fredbenenson/7bb92718e19138c20591#file-kickstarter_sql_style_guide-md [sqlstyleguide]: http://www.sqlstyle.guide "SQL style guide by Simon Holywell" 
- 
        mattmc3 revised this gist Dec 18, 2018 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewingThis file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -21,7 +21,7 @@ is a guiding principle to achieve cohesion. It is easy to include this guide in Markdown format as a part of a project's code base or reference it here for anyone on the project to freely read. Modern T-SQL style guide is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License][license]. Based on various existing attempts at SQL standards including: 
- 
        mattmc3 revised this gist Dec 18, 2018 . 1 changed file with 2 additions and 2 deletions.There are no files selected for viewingThis file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -133,8 +133,8 @@ Do not SHOUTCASE or "Sentence case" SQL keywords (e.g., prefer `select`, not `SELECT` or `Select`). SHOUTCASED SQL is an anachronism, and is not appropriate for modern SQL development. Using lowercase keywords is preferred because: * UPPERCASE words are harder to type and [harder to read][shoutcase-typeography]. * SQL syntax is not case-sensitive, and thus lowercase keywords work correctly in all variants of SQL, not just T-SQL. * Modern editors color code SQL keywords, so there is not a need to distinguish 
- 
        mattmc3 revised this gist Dec 18, 2018 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewingThis file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,7 +1,7 @@ --- layout: default title: Modern T-SQL Style Guide version: 0.0.4-alpha description: A guide to writing clean, clear, and consistent T-SQL. --- 
- 
        mattmc3 revised this gist Dec 18, 2018 . 1 changed file with 6 additions and 6 deletions.There are no files selected for viewingThis file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -129,15 +129,15 @@ delete cc ### Casing Do not SHOUTCASE or "Sentence case" SQL keywords (e.g., prefer `select`, not `SELECT` or `Select`). SHOUTCASED SQL is an anachronism, and is not appropriate for modern SQL development. Using lowercase keywords is preferred because: * UPPERCASE words are harder to type and [harder to read] [shoutcase-typeography]. * SQL syntax is not case-sensitive, and thus lowercase keywords work correctly in all variants of SQL, not just T-SQL. * Modern editors color code SQL keywords, so there is not a need to distinguish keywords by casing. * UPPERCASE IS ASSOCIATED WITH SHOUTING WHEN SEEN IN TEXT, AND MAKES SQL FEEL MORE LIKE COBOL THAN A MODERN LANGUAGE. @@ -196,7 +196,7 @@ style or change all keywords to lowercase. Favor consistency over mixed casing. tables with similarly named columns. (**NOTE:** _This guide uses the AdventureWorks database, which commonly has columns named `Name` against this guide's advice. Remember that an existing convention may be in place that is beyond your control._ ) #### Aliasing or correlations 
- 
        mattmc3 revised this gist Dec 18, 2018 . 1 changed file with 2 additions and 2 deletions.There are no files selected for viewingThis file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -28,8 +28,8 @@ Based on various existing attempts at SQL standards including: [http://www.sqlstyle.guide][sqlstyleguide] and [Kickstarter guide][kickstarter-sql-guide]. The example SQL statements used are based on tables in the [AdventureWorks][adventureworks] database. **NOTE**: This style guide is written for use with [Microsoft SQL Server][mssql], but much of it can be applied to any SQL database with some 
- 
        mattmc3 revised this gist Dec 18, 2018 . 1 changed file with 484 additions and 312 deletions.There are no files selected for viewingThis file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,34 +1,35 @@ --- layout: default title: Modern T-SQL Style Guide version: 0.0.3-alpha description: A guide to writing clean, clear, and consistent T-SQL. --- # Modern T-SQL Style Guide ## Purpose These guidelines are designed to make T-SQL statements easy to write, easy to read, easy to maintain, and beautiful to boot. This document is to be used as a guide for anyone who would like to write clean and clear T-SQL code. This guide is opinionated in some areas and relaxed in others. You can use this set of guidelines, fork them, or make your own - the key here is that you pick a style and stick to it. The odds of making everyone happy are low, so compromise is a guiding principle to achieve cohesion. It is easy to include this guide in Markdown format as a part of a project's code base or reference it here for anyone on the project to freely read. Modern T-SQL style guide created by [Matt McElheny][mmc] is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License][license]. Based on various existing attempts at SQL standards including: [http://www.sqlstyle.guide][sqlstyleguide] and [Kickstarter guide][kickstarter-sql-guide]. The example SQL statements used are based on tables in the [AdventureWorks] [adventureworks] database. **NOTE**: This style guide is written for use with [Microsoft SQL Server][mssql], but much of it can be applied to any SQL database with some @@ -43,6 +44,8 @@ simple modifications. not to the exclusion of other practical concerns. * We demonstrate intent explicitly in code, via clear structure and comments where needed. * We adhere to a consistent style for handwritten SQL so that our code can thrive in an environment with many authors, editors, and readers. ## Quick look @@ -51,84 +54,106 @@ well formatted SQL that matches the recommendations in this style guide: ```sql -- basic select example select p.Name as ProductName , p.ProductNumber , pm.Name as ProductModelName , p.Color , p.ListPrice from Production.Product as p join Production.ProductModel as pm on p.ProductModelID = pm.ProductModelID where p.Color in ('Blue', 'Red') and p.ListPrice < 800.00 and pm.Name like '%frame%' order by p.Name ``` ```sql -- basic insert example insert into Sales.Currency ( CurrencyCode ,Name ,ModifiedDate ) values ( 'XBT' ,'Bitcoin' ,getutcdate() ) ``` ```sql -- basic update example update p set p.ListPrice = p.ListPrice * 1.05 , p.ModifiedDate = getutcdate() from Production.Product p where p.SellEndDate is null and p.SellStartDate is not null ``` ```sql -- basic delete example delete cc from Sales.CreditCard cc where cc.ExpYear < '2003' and cc.ModifiedDate < dateadd(year, -1, getutcdate()) ``` ## Rules ### General guidance * Favor using a ["river"][rivers] for vertical alignment so that a query can be quickly and easily be scanned by a new reader. * Comments should appear at the top of your query or script, and should explain the intent of the query, not the mechanics. * Try to comment things that aren't obvious about the query (e.g., why a particular filter is necessary, why an optimization trick was needed, etc.) * Favor being descriptive over terseness: __GOOD__: `select emp.LoginID as EmployeeUserName` __BAD__: `select emp.LoginID as EmpUsrNm` * Follow any existing style in the script before applying this style guide. The SQL script should have one clear style, and these rules should not be applied to existing scripts unless the whole script is being changed to adhere to the same style. * Favor storing `datetime` and `datetime2` in UTC unless embedding timezone information (`datetimeoffset`) so that times are clear and convertible. Use [ISO-8601][iso-8601] compliant time and date information (`YYYY-MM-DD HH:MM:SS.SSSSS`) when referring to date/time data. ### Casing Do not SHOUTCASE or "Sentense case" SQL keywords (e.g., prefer `select`, not `SELECT`). SHOUTCASED SQL is an anachronism, and is not appropriate for modern SQL development. Using lowercase keywords is preferred because: * UPPERCASE words are harder to type and [harder to read] [shoutcase-typeography]. * SQL syntax is not case-sensitive, and thus lowecase keywords work correctly in all variants of SQL, not just T-SQL. * Moden editors color code SQL keywords, so there is not a need to distinguish keywords by casing. * UPPERCASE IS ASSOCIATED WITH SHOUTING WHEN SEEN IN TEXT, AND MAKES SQL FEEL MORE LIKE COBOL THAN A MODERN LANGUAGE. If the SQL script you are editing already uses SHOUTCASE keywords, match that style or change all keywords to lowercase. Favor consistency over mixed casing. ### Naming guidance * Names should be `underscore_separated` or `PascalCase` but do not mix styles. __GOOD__: `select count(*) as the_tally, sum(*) as the_total ...` __BAD__: `select count(*) as The_Tally, sum(*) as theTotal ...` #### Tables @@ -139,34 +164,48 @@ delete g * Do not use object prefixes or Hungarian notation (e.g. `sp_`, `prc_`, `vw_`, `tbl`, `t_`, `fn_`, etc). * Never give a table the same name as one of its columns and vice versa. * Use a joining word for many-to-many joining tables (cross references) rather than concatenating table names (e.g. `Xref`): __GOOD__: `DriversXrefCars` __BAD__: `DriversCars` * Tables should always have a primary key. Single column, auto-number (identity) surrogate keys are preferable. Natural keys or composite keys can be enforced with unique constraints in lieu of making them a primary key. Composite keys make for verbose foreign key joins, and `int`/`bigint` primary keys offer optimal performance and space for foreign keys when a table gets large. * Tables should always have `created_at` and `updated_at` metadata fields in them to make data movement between systems easier (ETL). Also, consider storing deleted records in archival tables, or having a `deleted_at` field for soft deletes. Don't forget the needs of data analysts when designing your model. #### Columns * Do not use [reserved words][sql-server-keywords] for column names if possible. * Prefer not simply using `id` as the name of the primary identifier for the table if possible. * Do not add a column with the same name as its table and vice versa. * Avoid common words like `Name`, `Description`, etc. Prefer a descriptive prefix for those words so that they don't require aliases when joined to other tables with similarly named columns. (**NOTE:** _This guide uses the AdventureWorks database, which commonly has columns named `Name` against this guide's advice. Remember that an existing convention may be in place that is beyond your control.__ ) #### Aliasing or correlations * Should relate in some way to the object or expression they are aliasing. * As a rule of thumb the correlation name should be the first letter of each word in the object's name or a good abbreviation. * If there is already a correlation with the same name then append a number. * When using a subquery, prefix aliases with an `_` to differentiate them from aliases in the outer query. * Always include the `as` keyword—makes it easier to read as it is explicit. * For computed data (`sum()` or `avg()`) use the name you would give it were it a column defined in the schema. @@ -175,13 +214,13 @@ delete g * No tabs. Use spaces for indents. * Configure your editor to 4 spaces per indent, but prefer your SQL to indent to the "river", and not to a set indent increment. * No trailing whitespace. * No more than two blank lines between statements. * No empty lines in the middle of a single statement. * One final newline at the end of a file * Use an [.editorConfig file][editor-config] to enforce reasonable whitespace rules if your T-SQL editor supports it: ```ini # .editorConfig is awesome: https://EditorConfig.org @@ -198,158 +237,214 @@ insert_final_newline = true ### River formatting Spaces may 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 reader's eye to scan over the code and separate the keywords from the implementation detail. Rivers are [bad in typography][rivers], but helpful here. [Celko's book][celko] describes using a river to vertically align your query. Right align keywords to the river if you chose to use one. The `on` clause in the `from` may have its own river to help align information vertically. Subqueries should create their own river as well. ```sql -- a river in the 7th column helps vertical readability select prdct.Name as ProductName , prdct.ListPrice , prdct.Color , cat.Name as CategoryName , subcat.Name as SubcategoryName from Production.Product as prdct left join Production.ProductSubcategory as subcat on prdct.ProductSubcategoryID = subcat.ProductSubcategoryID left join Production.ProductCategory as cat on subcat.ProductCategoryID = cat.ProductCategoryID where prdct.ListPrice <= 1000.00 and prdct.ProductID not in ( select _pd.ProductID from Production.ProductDocument _pd where _pd.ModifiedDate < dateadd(year, -1, getutcdate()) ) and prdct.Color in ('Black', 'Red', 'Silver') order by prdct.ListPrice desc, prdct.Name ``` ```sql -- alternately, a river in the a different column is fine if that is preferred -- due to longer keywords, but know that indenting can feel "off" if the -- `select` is not in the first column for the query select prdct.Name as ProductName , prdct.ListPrice , prdct.Color , cat.Name as CategoryName , subcat.Name as SubcategoryName from Production.Product as prdct left join Production.ProductSubcategory as subcat on prdct.ProductSubcategoryID = subcat.ProductSubcategoryID left join Production.ProductCategory as cat on subcat.ProductCategoryID = cat.ProductCategoryID where prdct.ListPrice <= 1000.00 and prdct.ProductID not in ( select _pd.ProductID from Production.ProductDocument _pd where _pd.ModifiedDate < dateadd(year, -1, getutcdate()) ) and prdct.Color in ('Black', 'Red', 'Silver') order by prdct.ListPrice desc, prdct.Name ``` ### Indent formatting Using a river can be tedious, so if this alignment is not preferred by your team, then a standard 4 space indent can be used in place of a river. Major keywords starting a clause should occupying their own line. Major keywords are: * `select` * `into` * `from` * `where` * `group by` * `having` * `order by` All other keywords are minor and should appear after the indent and not occupy a line to themselves. Other than this section, this guide will stick to showing "river" formatting examples. ```sql -- Editors tend to handle indenting style better than river alignment. River -- formatting has advantages over indent formatting, but this style is -- acceptable. select prdct.Name as ProductName ,prdct.ListPrice ,prdct.Color ,cat.Name as CategoryName ,subcat.Name as SubcategoryName from Production.Product as prdct left join Production.ProductSubcategory as subcat on prdct.ProductSubcategoryID = subcat.ProductSubcategoryID left join Production.ProductCategory as cat on subcat.ProductCategoryID = cat.ProductCategoryID where prdct.ListPrice <= 1000.00 and prdct.Color in ('Black', 'Red', 'Silver') order by prdct.ListPrice desc, prdct.Name ``` ### `select` clause Select the first column on the same line, and align all subsequent columns after the first get their own line. ```sql select prdct.Color , cat.Name as CategoryName , count(*) as ProductCount from ... ``` If three or fewer columns are selected, have short names, and don't need aliased, you may chose to have them occupy the same line for brevity. ```sql -- shortcut for small columns select p.Color, c.Name, p.ListPrice from ... ``` If using a `select` modifier like `distinct` or `top`, put the first column on its own line. ```sql -- treat the firt column differently when using distinct and top select distinct p.Color , c.Name as CategoryName from ... ``` Use commas as a prefix as opposed to a suffix. This is preferred because: * It makes it easy to add new columns to the end of the column list, which is more common than at the beginning * It prevents unintentional aliasing bugs (missing comma) * It makes commenting out columns at the end easier * When statements take multiple lines like windowing functions and `case` statements, the prefix comma makes it clear when a new column starts * It does not adversely affect readability The comma should border the "river" on the keyword side. __GOOD__: ```sql select Name , ListPrice , Color , CategoryName ... ``` __BAD__: ```sql -- whoops! forgot a trailing comma because it's hard to see, making an -- accidental alias of `ListPrice Color` select Name, ListPrice Color, CategoryName ... ``` Always use `as` to rename columns. `as` statements can be used for additional vertical alignment but don't have to be: __GOOD__: ```sql select prdct.Color as ProductColor , cat.Name as CategoryName , count(*) as ProductCount from ... ... ``` __BAD__: ```sql select prdct.Color ProductColor , cat.Name CategoryName , count(*) ProductCount from ... ... ``` Always rename aggregates, derived columns (e.g. `case` statements), and function-wrapped columns: ```sql select ProductName , sum(UnitPrice * OrderQty) as TotalCost , getutcdate() as NowUTC from ... ``` Always use table alias prefixes for all columns when querying from more than one table. Single character aliases are fine for a few tables, but are less likely to be clear as a query grows: ```sql select prdct.Color , subcat.Name as SubcategoryName , count(*) as ItemCount from Production.Product as prdct left join Production.ProductSubcategory as subcat on ... ``` Do not bracket-escape table or column names unless the names contain keyword @@ -358,40 +453,46 @@ collisions or would cause a syntax error without properly qualifying them. __GOOD__: ```sql -- owner and status are keywords select Title , [Owner] , [Status] from Production.Document ``` __BAD__: ```sql -- extra brackets are messy and unnecessary select [Title] , [Owner] , [Status] from [Production].[Document] ``` #### Windowing functions Long Window functions should be split across multiple lines: one for each clause, aligned with a river. Partition keys can share the same line, or be split. Ascending order is an intuitive default and thus using an explicit `asc` is not necessary whereas `desc` is. All window functions should be aliased. ```sql select p.ProductID , p.Name as ProductName , p.ProductNumber , p.ProductLine , row_number() over (partition by p.ProductLine , left(p.ProductNumber, 2) order by right(p.ProductNumber, 4) desc) as SequenceNum , p.Color from Production.Product p order by p.ProductLine , left(p.ProductNumber, 2) , SequenceNum ``` #### `case` statements `case` statements aren't always easy to format but try to align `when`, `then`, and `else` together inside `case` and `end`. @@ -400,36 +501,38 @@ and `else` together inside `case` and `end`. preferable. ```sql select dep.Name as DepartmentName , case when dep.Name in ('Engineering', 'Tool Design', 'Information Services') then 'Information Technology' else dep.GroupName end as NewGroupName from HumanResources.Department as dep order by NewGroupName, DepartmentName ``` ### `from` clause Only one table should be in the `from` part. Never use comma separated `from`-joins: __GOOD__: ```sql select cust.AccountNumber , sto.Name as StoreName from Sales.Customer as cust join Sales.Store as sto on cust.StoreID = sto.BusinessEntityID ... ``` __BAD__: ```sql select cust.AccountNumber , sto.Name as StoreName from Sales.Customer as cust, Sales.Store as sto where cust.StoreID = sto.BusinessEntityID ... ``` @@ -440,238 +543,307 @@ query, and the full table list is easier to scan without excessive staggering: __GOOD__: ```sql -- this is easier to format and read select * from HumanResources.Employee as emp join Person.Person as per on emp.BusinessEntityID = per.BusinessEntityID left join HumanResources.EmployeeDepartmentHistory as edh on emp.BusinessEntityID = edh.BusinessEntityID left join HumanResources.Department as dep on edh.DepartmentID = dep.DepartmentID ``` __BAD__: ```sql -- verbosity for the sake of verbosity is not helpful -- `join` by itself always means `inner join` -- `outer` is an unnecessary optional keyword select * from HumanResources.Employee as emp inner join Person.Person as per on emp.BusinessEntityID = per.BusinessEntityID left outer join HumanResources.EmployeeDepartmentHistory as edh on emp.BusinessEntityID = edh.BusinessEntityID left outer join HumanResources.Department as dep on edh.DepartmentID = dep.DepartmentID ``` The `on` keyword and condition can go on its own line, but is easier to scan if it lines up on the `join` line. This is an acceptable style alternative: ```sql -- this is an easier format to scan visually, but comes at the cost of longer -- lines of code. select * from HumanResources.Employee as emp join Person.Person as per on emp.BusinessEntityID = per.BusinessEntityID left join HumanResources.EmployeeDepartmentHistory as edh on emp.BusinessEntityID = edh.BusinessEntityID left join HumanResources.Department as dep on edh.DepartmentID = dep.DepartmentID ... ``` Additional filters in the `join` go on new indented lines. Line up using the `on` keyword: __GOOD__: ```sql select emp.JobTitle from HumanResources.Employee as emp left join HumanResources.EmployeeDepartmentHistory as edh on emp.BusinessEntityID = edh.BusinessEntityID left join HumanResources.Department as dep on edh.DepartmentID = dep.DepartmentID and dep.Name <> dep.GroupName -- multi-conditions start a new line where dep.DepartmentID is null ``` __BAD__: ```sql select emp.JobTitle from HumanResources.Employee as emp left join HumanResources.EmployeeDepartmentHistory as edh on emp.BusinessEntityID = edh.BusinessEntityID left join HumanResources.Department as dep on edh.DepartmentID = dep.DepartmentID and dep.Name <> dep.GroupName -- needs a new line where dep.DepartmentID is null ``` Begin with `inner join`s and then list `left join`s, order them semantically, and do not intermingle `left join`s with `inner join`s unless necessary. Order the `on` clause with joining aliases referencing tables top-to-bottom: __GOOD__: ```sql select * from Production.Product as prd join Production.ProductModel as prm on prd.ProductModelID = prm.ProductModelID left join Production.ProductSubcategory as psc on prd.ProductSubcategoryID = psc.ProductSubcategoryID left join Production.ProductDocument as doc on prd.ProductID = doc.ProductID ``` __BAD__: ```sql select * from Production.Product as prd left join Production.ProductSubcategory as psc on psc.ProductSubcategoryID = prd.ProductSubcategoryID -- backwards join Production.ProductModel as prm -- intermingled on prm.ProductModelID = prd.ProductModelID -- backwards left join Production.ProductDocument as doc on prd.ProductID = doc.ProductID ``` Avoid `right joins` as they are usually better written with a `left join` __GOOD__: ```sql select * from Production.Product as prd left join Production.ProductSubcategory as psc on ... ``` __BAD__: ```sql select * from Production.ProductSubcategory as psc right join Production.Product as prd on ... ``` ### `where` clause Multiple `where` clauses should go on different lines and align to the river: ```sql select * from Production.Product prd where prd.Weight > 2.5 and prd.ListPrice < 1500.00 and Color in ('Blue', 'Black', 'Red') and SellStartDate >= '2006-01-01' ... ``` When mixing `and` and `or` statements, do not rely on order of operations and instead always use parenthesis to make the intent clear: ```sql select * from Production.Product prd where (prd.Weight > 10.0 and Color in ('Red', 'Silver')) or Color is null ``` Always put a semicolon on its own line when using them. This prevents common errors like adding conditions to a `where` clause and neglecting to move the trailing semicolon: __GOOD__: ```sql -- The prefix semicolon is clear and easy to spot when adding to a `where` delete prd from Production.Product prd where prd.ListPrice = 0 and weight is null and size is null ; ... ``` __GOOD__: ```sql -- A trailing semicolon is sinister. -- We added some where conditions and missed it. -- This is a destructive bug. delete prd from Production.Product prd where prd.ListPrice = 0; -- dangerous and weight is null -- syntax error here, but the bad delete is valid and size is null ... ``` ### `group by` clause Maintain the same column order as the `select` clause in the `group by`: __GOOD__: ```sql select poh.EmployeeID , poh.VendorID , count(*) as OrderCount , avg(poh.SubTotal) as AvgSubTotal from Purchasing.PurchaseOrderHeader as poh group by poh.EmployeeID , poh.VendorID ``` __BAD__: ```sql -- messing with the 'group by' order makes it hard to scan for accuracy select poh.EmployeeID , poh.VendorID , count(*) as OrderCount , avg(poh.SubTotal) as AvgSubTotal from Purchasing.PurchaseOrderHeader as poh group by poh.VendorID -- out of order , poh.EmployeeID ``` ### `having` clause A `having` clause is just a `where` clause for aggregate functions. The same rules for `where` clauses apply to `having`. __Example__: ```sql select poh.EmployeeID , poh.VendorID , count(*) as OrderCount , avg(poh.SubTotal) as AvgSubTotal from Purchasing.PurchaseOrderHeader as poh group by poh.EmployeeID , poh.VendorID having count(*) > 1 and avg(poh.SubTotal) > 3000.00 ``` ### `order by` clause Do not use the superfluous `asc` in `order by` statements: __GOOD__: ```sql -- asc is implied and obvious select per.LastName , per.FirstName from Person.Person per order by per.LastName , per.FirstName ``` __BAD__: ```sql -- asc is clutter - it's never ambiguous when you wanted to sort ascending select per.LastName , per.FirstName from Person.Person per order by per.LastName asc -- useless asc , per.FirstName asc ``` Ordering by column number is okay, but not preferred: ```sql -- This is okay, but not great. select per.FirstName + ' ' + per.LastName as FullName , per.LastName + ', ' + per.FirstName as LastFirst from Person.Person per order by 2 ``` The `by` keyword can sit on the other side of a 7th column river, but align the order by columns: ```sql select per.FirstName , per.LastName from Person.Person per order by per.LastName , per.FirstName ``` If three or fewer columns are in the `order by` and have short names you may chose to have them occupy the same line for brevity. ```sql -- shortcut for small columns select per.FirstName, per.LastName from Person.Person per order by per.LastName, per.FirstName ``` [mmc]: https://github.com/mattmc3 [kickstarter-sql-guide]: https://gist.github.com/fredbenenson/7bb92718e19138c20591#file-kickstarter_sql_style_guide-md [sqlstyleguide]: http://www.sqlstyle.guide "SQL style guide by Simon Holywell" [adventureworks]: https://docs.microsoft.com/en-us/sql/samples/adventureworks-install-configure "AdventureWorks sample database" [shoutcase-typeography]: https://practicaltypography.com/all-caps.html [editor-config]: https://EditorConfig.org [mssql]: https://www.microsoft.com/en-us/sql-server/default.aspx "Microsoft SQL Server" [simon]: https://www.simonholywell.com/?utm_source=sqlstyle.guide&utm_medium=link&utm_campaign=md-document "SimonHolywell.com" [celko]: https://www.amazon.com/gp/product/0120887975/ref=as_li_ss_tl?ie=UTF8&linkCode=ll1&tag=treffynnon-20&linkId=9c88eac8cd420e979675c815771313d5 "Joe Celko's SQL Programming Style (The Morgan Kaufmann Series in Data Management Systems)" [iso-8601]: https://en.wikipedia.org/wiki/ISO_8601 "Wikipedia: ISO 8601" [rivers]: http://practicaltypography.com/one-space-between-sentences.html "Practical Typography: one space between sentences" [reserved-keywords]: #reserved-keyword-reference "Reserved keyword reference" [license]: http://creativecommons.org/licenses/by-sa/4.0/ "Creative Commons Attribution-ShareAlike 4.0 International License" [sql-server-keywords]: https://docs.microsoft.com/en-us/sql/t-sql/language-elements/reserved-keywords-transact-sql "SQL Server reserved keywords" 
- 
        mattmc3 revised this gist Dec 12, 2018 . 1 changed file with 2 additions and 0 deletions.There are no files selected for viewingThis file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -645,6 +645,8 @@ order by 2, 1 [mmc]: https://github.com/mattmc3 [kickstarter-sql-guide]: https://gist.github.com/fredbenenson/7bb92718e19138c20591#file-kickstarter_sql_style_guide-md [editor-config]: https://EditorConfig.org [mssql]: https://www.microsoft.com/en-us/sql-server/default.aspx "Microsoft SQL Server" [simon]: https://www.simonholywell.com/?utm_source=sqlstyle.guide&utm_medium=link&utm_campaign=md-document "SimonHolywell.com" [issue]: https://github.com/treffynnon/sqlstyle.guide/issues 
- 
        mattmc3 revised this gist Dec 12, 2018 . 1 changed file with 10 additions and 10 deletions.There are no files selected for viewingThis file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -214,9 +214,9 @@ select emp.id as employee_id , emp.last_name as employee_last_name , mgr.first_name as manager_first_name , mgr.last_name as manager_last_name from dbo.employees as emp join dbo.departments as dpt on emp.department_id = dpt.id left join dbo.employees as mgr on emp.manager_id = mgr.id where dpt.name in ('IT', 'DBA', 'Analytics') and (emp.title like '%DBA%' or emp.title like '%Database%') order by dpt.name @@ -235,9 +235,9 @@ select emp.id as employee_id , emp.last_name as employee_last_name , mgr.first_name as manager_first_name , mgr.last_name as manager_last_name from dbo.employees as emp join dbo.departments as dpt on emp.department_id = dpt.id left join dbo.employees as mgr on emp.manager_id = mgr.id where dpt.name in ('IT', 'DBA', 'Analytics') and (emp.title like '%DBA%' or emp.title like '%Database%') order by dpt.name @@ -249,7 +249,7 @@ select emp.id as employee_id -- Using a river can be tedious, so if this alignment is not preferred by your -- team, then standard 4 space indenting should be used in place of a river -- with the major keywords occupying their own line. (i.e. `select`, `from`, -- `where`, `group by`, `having`, `order by`): select emp.id as employee_id ,dpt.name as department @@ -259,9 +259,9 @@ select ,mgr.first_name as manager_first_name ,mgr.last_name as manager_last_name from dbo.employees as emp join dbo.departments as dpt on emp.department_id = dpt.id left join dbo.employees as mgr on emp.manager_id = mgr.id where dpt.name in ('IT', 'DBA', 'Analytics') and (emp.title like '%DBA%' or emp.title like '%Database%') 
- 
        mattmc3 revised this gist Dec 12, 2018 . 1 changed file with 337 additions and 26 deletions.There are no files selected for viewingThis file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,7 +1,7 @@ --- layout: default title: Modern T-SQL Style Guide version: 0.0.2-alpha description: A guide to writing clean, clear, and consistent T-SQL. --- @@ -23,8 +23,9 @@ code base or reference it here for anyone on the project to freely read. Modern T-SQL style guide created by [Matt McElheny][mmc] is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License][licence]. Based on various SQL standards including [http://www.sqlstyle.guide][sqlstyleguide] and [Kickstarter guide][kickstarter-sql-guide]. This document is to be used as a guide for anyone who would like to write clean and clear T-SQL code that is meant to be shared. @@ -95,19 +96,11 @@ delete g ### General guidance * Do not SHOUTCASE SQL keywords (e.g., prefer `select`, not `select`) * Favor using a "river" for vertical alignment so that a query can be quickly and easily be scanned by a new reader. * Comments should appear at the top of your query, and should explain the intent of the query, not the mechanics. * Try to comment things that aren't obvious about the query (e.g., why a particular filter is necessary, why an optimization trick was needed, etc.) * Favor being descriptive over terseness: @@ -118,12 +111,65 @@ delete g __BAD__: `select usr.user_name as custnm` * Follow any existing style in the script before applying this style guide. The SQL script should have one clear style, and these rules should not be applied to existing scripts unless the whole script is being changed to adhere to this style guide. * Favor storing `datetime` and `datetime2` in UTC unless embedding timezone information (`datetimeoffset`) so that times are clear and convertible. Use [ISO-8601][iso-8601] compliant time and date information (`YYYY-MM-DD HH:MM:SS.SSSSS`) when referring to date/time data. ### Naming guidance * Names should be `underscore_separated` or `PascalCase` but do not mix styles. __GOOD__: `select count(*) as the_tally, sum(*) as the_total` __BAD__: `select count(*) as TheTally, sum(*) as theTotal` #### Tables * Do not use [reserved words][sql-server-keywords] for table names if possible. * Prefer the shortest commonly understood words to name a table. * Naming a table as a plural makes the table easier to speak about. (e.g. favor `employees` over `employee`) * Do not use object prefixes or Hungarian notation (e.g. `sp_`, `prc_`, `vw_`, `tbl`, `t_`, `fn_`, etc). * Never give a table the same name as one of its columns and vice versa. * Use `_xref_` for many-to-many tables rather than concatenating table names: __GOOD__: `drivers_xref_cars` __BAD__: `drivers_cars` * Tables should always have a primary key. Surrogate, auto-number identity primary keys are preferable in most circumstances. Natural keys can be enforced with unique constraints in lieu of making them a primary key. #### Columns * Do not use [reserved words][sql-server-keywords] for column names if possible. * Prefer not simply using `id` as the primary identifier for the table if possible. * Do not add a column with the same name as its table and vice versa. * Avoid common words like `name`, `description`, etc. Prefer a descriptive prefix for those words so that they don't require aliases when joined to other tables with similarly named columns. #### Aliasing or correlations * Should relate in some way to the object or expression they are aliasing. * as a rule of thumb the correlation name should be the first letter of each word in the object's name. * If there is already a correlation with the same name then append a number. * Always include the `as` keyword—makes it easier to read as it is explicit. * For computed data (`sum()` or `avg()`) use the name you would give it were it a column defined in the schema. ### Whitespace @@ -227,7 +273,7 @@ order by ### `select` clause select the first column on the same line, and align all additional columns to the first column on their own line: ```sql @@ -306,17 +352,31 @@ select prj.name project_name ... ``` Do not bracket-escape table or column names unless the names contain keyword collisions or would cause a syntax error without properly qualifying them. __GOOD__: ```sql -- name and state are keywords select [name] , street1 , city , [state] , zip from dbo.people ``` __BAD__: ```sql -- the brackets are messy and unnecessary select [name] , [street1] , [city] , [state] , [zip] from [dbo].[people] ``` Long Window functions should be split across multiple lines: one for each @@ -331,6 +391,257 @@ row_number() over (partition by prj.category_id, prj.year ) as sequence_number ``` ### `case` statements `case` statements aren't always easy to format but try to align `when`, `then`, and `else` together inside `case` and `end`. `then` can stay on the `when` line if needed, but aligning with `else` is preferable. ```sql select prj.name as project_name , prj.created_at , case when prj.category in ('stuff', 'and', 'things') then 'Amazing!' else 'Average' end as opinion from ... ``` ### `from` and `join` clause Only one table should be in the `from`. Never use comma separated `from`-joins: __GOOD__: ```sql select projects.name as project_name , count(backings.id) as backings_count from dbo.projects as projects join dbo.backings as backings on backings.project_id = projects.id ... ``` __BAD__: ```sql select projects.name as project_name , count(backings.id) as backings_count from dbo.projects as projects, dbo.backings as backings where backings.project_id = projects.id ... ``` Favor not using the extraneous words `inner` or `outer` when joining tables. Alignment is easier without them, they don't add to the understanding of the query, and the full table list is easier to scan without excessive staggering: __GOOD__: ```sql select projects.name as project_name, , count(backings.id) as backings_count from dbo.projects as projects join dbo.backings as backings on ... join ... left join dbo.backer_rewards as backer_rewards on ... left join ... ``` __BAD__: ```sql select projects.name as project_name, , count(backings.id) as backings_count from dbo.projects as projects inner join dbo.backings as backings on ... inner join ... left outer join dbo.backer_rewards as backer_rewards on ... left outer join ... ``` The `on` keyword and condition goes on the `inner join` line: ```sql select projects.name as project_name , count(backings.id) as backings_count from dbo.projects as projects join dbo.backings as backings on projects.id = backings.project_id ... ``` Additional filters in the `join` go on new indented lines. Line up using the `on` keyword as a second river: ```sql select projects.name as project_name , count(backings.id) as backings_count from dbo.projects as projects join dbo.backings as backings on projects.id = backings.project_id and backings.project_country <> 'US' ... ``` Begin with `inner join`s and then list `left join`s, order them semantically, and do not intermingle `left join`s with `inner join`s unless necessary: __GOOD__: ```sql join dbo.backings as backings on ... join dbo.users as users on ... join dbo.locations as locations on ... left join dbo.backer_rewards as backer_rewards on ... left join ... ``` __BAD__: ```sql left join dbo.backer_rewards as backer_rewards on backings join dbo.users as users on ... left join ... join dbo.locations as locations on ... ``` Avoid `right joins` as they are usually better written with a `left join` __BAD__: ```sql from dbo.backer_rewards as backer_rewards right join dbo.users as users on ... ``` __GOOD__: ```sql from dbo.users as users left join dbo.backer_rewards as backer_rewards on ... ``` ### `where` clause Multiple `where` clauses should go on different lines and align to the river: ```sql select name , goal from dbo.projects as projects where country = 'US' and deadline >= '2015-01-01' ... ``` When mixing `and` and `or` statements, do not rely on order of operations and instead always use parenthesis to make the intent clear: ```sql select name , goal from dbo.projects as projects where (country = 'US' and deadline >= '2015-01-01') or include = 1 ... ``` Always put a semicolon on its own line when using them. This prevents common errors like adding conditions to a `where` clause and neglecting to move the trailing semicolon: __BAD__: ```sql -- messing with a where clause, it's easy to miss a trailing semicolon delete u from dbo.users u where created_at < dateadd(year, -1, getutcdate()); and user_name like '%test_user%' -- whoops, missed that trailing semicolon! ... ``` __GOOD__: ```sql -- semicolons that start a line are easy to spot delete u from dbo.users u where created_at < dateadd(year, -1, getutcdate()) and user_name like '%test_user%' ; -- < if we add a condition, we'll see this semicolon ... ``` ### `group by` clause Maintain the same column order as the `select` clause in the `group by`: __BAD__: ```sql -- messing with the 'group by' order makes it hard to scan for accuracy select p.project_name , p.country , sum(p.pledged) as total_pledged from dbo.projects as p group by p.country -- out of order , p.project_name ``` __GOOD__: ```sql select p.project_name , p.country , sum(p.pledged) as total_pledged from dbo.projects as p group by p.project_name , p.country ``` ### `order by` clause Do not use the superfluous `asc` in `order by` statements: __BAD__: ```sql -- asc is clutter - it's never ambiguous when you wanted to sort ascending select p.project_name , p.country from dbo.projects as p order by p.country asc , p.project_name asc ``` __GOOD__: ```sql -- asc is clutter - it's never ambiguous when you wanted to sort ascending select p.project_name , p.country from dbo.projects as p order by p.country , p.project_name ``` Ordering by column number is okay, but not preferred: ```sql -- This is okay, but not great. It's better when running ad-hoc queries or -- queries with calculated columns select p.project_name , case when p.category in ('Science', 'Technology', 'Engineering') then 'STEM' else 'Other' end as category_group , p.country from dbo.projects as p order by 2, 1 ``` [mmc]: https://github.com/mattmc3 [kickstarter-sql-guide]: https://gist.github.com/fredbenenson/7bb92718e19138c20591#file-kickstarter_sql_style_guide-md [editor-config]: https://EditorConfig.org 
- 
        mattmc3 created this gist Dec 12, 2018 .There are no files selected for viewingThis file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,364 @@ --- layout: default title: Modern T-SQL Style Guide version: 0.0.1-alpha description: A guide to writing clean, clear, and consistent T-SQL. --- # Modern T-SQL Style Guide ## Purpose You can use this set of guidelines, fork them, or make your own - the key here is that you pick a style and stick to it. The odds of making everyone happy are low, so compromise is a guiding principle to achieve cohesion. These guidelines are designed to make T-SQL statements easy to write, easy to read, and easy to maintain. This guide is opinionated in some areas and relaxed in others. It is easy to include this guide in Markdown format as a part of a project's code base or reference it here for anyone on the project to freely read. Modern T-SQL style guide created by [Matt McElheny][mmc] is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License][licence]. Based on a work at [http://www.sqlstyle.guide][sqlstyleguide] and [Kickstarter guide][kickstarter-sql-guide]. This document is to be used as a guide for anyone who would like to write clean and clear T-SQL code that is meant to be shared. **NOTE**: This style guide is written for use with [Microsoft SQL Server][mssql], but much of it can be applied to any SQL database with some simple modifications. ## Principles * We take a disciplined and practical approach to writing code. * We treat T-SQL like any other source code, which should be checked into source control, peer reviewed, and properly maintained. * We believe consistency in style is important, and we value craftsmanship, but not to the exclusion of other practical concerns. * We demonstrate intent explicitly in code, via clear structure and comments where needed. ## Quick look Before getting into all the rules, here is a quick look at some examples showing well formatted SQL that matches the recommendations in this style guide: ```sql -- basic select example select u.user_name as employee_user_name , u.created as created_date , d.[name] as department_name from dbo.users as u join dbo.departments as d on u.department_id = d.id where u.created >= dateadd(year, -1, getutcdate()) order by created_date desc ``` ```sql -- basic insert example insert into dbo.categories ( category_code ,category_name ,created_at ) values ( 'MOV' ,'Movies' ,getutcdate() ) ``` ```sql -- basic update example update e set e.salary = e.salary * 1.04 , updated_at = getutcdate() from dbo.employees as e where e.department_id in (1, 2, 3) ``` ```sql -- basic delete example delete g from dbo.gifts as g where g.is_purchased = 1 and g.updated_at <= dateadd(month, -6, getutcdate()) and g.return_date is null ``` ## Rules ### General guidance * Do not SHOUTCASE SQL keywords (e.g., prefer `select`, not `SELECT`) * Favor using a "river" for vertical alignment so that a query can be quickly and easily be scanned by a new reader. * Names should be `underscore_separated` or `PascalCase` but do not mix styles. __GOOD__: `select count(*) as the_tally, sum(*) as the_total` __BAD__: `SELECT Count(*) as TheTally, sum(*) as theTotal` * Comments should as the top of your query, and should explain the intent of the query, not the mechanics. * Try to comment things that aren't obvious about the query (e.g., why a particular filter is necessary, why an optimization trick was needed, etc.) * Favor being descriptive over terseness: __GOOD__: `select usr.user_name as customer_user_name` __BAD__: `select usr.user_name as custnm` * Do not use object prefixes or Hungarian notation (e.g. `sp_`, `prc_`, `vw_`, `tbl`, `t_`, `fn_`, etc). * Follow any existing style in the script before applying this style guide. The SQL script should have one clear style, and these rules should not be applied to existing scripts unless the whole script is being changed to adhere to this style guide. ### Whitespace * No tabs. Use spaces for indents. * Configure your editor to 4 spaces per indent, but prefer your SQL to indent to the "river" and not to a set indent increment. * No trailing whitespace. * No more than two lines between statements. * No empty lines in the middle of a single statement. * One final newline at the end of a file * Use an [.editorConfig file][editor-config] to enforce reasonable whitespace rules if your T-SQL editor supports it: ```ini # .editorConfig is awesome: https://EditorConfig.org # SQL files [*.{sql,tsql,ddl}] charset = utf-8 indent_style = space indent_size = 4 end_of_line = crlf trim_trailing_whitespace = true insert_final_newline = true ``` ### River formatting 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. [Celko's book][celko] describes using a river to vertically align your query. Right align keywords to the river. ```sql -- a river in the 7th column helps vertical readability select emp.id as employee_id , dpt.name as department , dpt.location , emp.first_name as employee_first_name , emp.last_name as employee_last_name , mgr.first_name as manager_first_name , mgr.last_name as manager_last_name from employees as emp join departments as dpt on emp.department_id = dpt.id left join employees as mgr on emp.manager_id = mgr.id where dpt.name in ('IT', 'DBA', 'Analytics') and (emp.title like '%DBA%' or emp.title like '%Database%') order by dpt.name , emp.last_name , emp.first_name ``` ```sql -- alternately, a river in the a different column is fine if that is preferred -- due to longer keywords, but know that indenting can feel off if the `select` -- is not in the first column for the query select emp.id as employee_id , dpt.name as department , dpt.location , emp.first_name as employee_first_name , emp.last_name as employee_last_name , mgr.first_name as manager_first_name , mgr.last_name as manager_last_name from employees as emp join departments as dpt on emp.department_id = dpt.id left join employees as mgr on emp.manager_id = mgr.id where dpt.name in ('IT', 'DBA', 'Analytics') and (emp.title like '%DBA%' or emp.title like '%Database%') order by dpt.name , emp.last_name , emp.first_name ``` ```sql -- Using a river can be tedious, so if this alignment is not preferred by your -- team, then standard 4 space indenting should be used in place of a river -- with the major keywords occupying their own line. (i.e. `select`, `from`, -- `where`, `group by`, `having`, `order by`): select emp.id as employee_id ,dpt.name as department ,dpt.location ,emp.first_name as employee_first_name ,emp.last_name as employee_last_name ,mgr.first_name as manager_first_name ,mgr.last_name as manager_last_name from employees as emp inner join departments as dpt on emp.department_id = dpt.id left join employees as mgr on emp.manager_id = mgr.id where dpt.name in ('IT', 'DBA', 'Analytics') and (emp.title like '%DBA%' or emp.title like '%Database%') order by dpt.name ,emp.last_name ,emp.first_name ``` ### `select` clause Select the first column on the same line, and align all additional columns to the first column on their own line: ```sql select projects.name , users.email , projects.country , count(backings.id) as backings_count from ... ``` Use commas as a prefix as opposed to a suffix. This is preferred because: * It makes it easy to add new columns to the end of the column list * It prevents unintentional aliasing bugs * It makes commenting out columns much easier * Column lists are more likely to change at the end than at the beginning The comma should border the "river". __GOOD__: ```sql select user_name as customer_user_name , created_at , is_deleted ... ``` __BAD__: ```sql -- whoops! forgot a trailing comma because it's hard to see, making an -- accidental alias select usr.user_name as customer_user_name, created_at is_deleted ... ``` Always rename aggregates, `case` derived columns, and function-wrapped columns: ```sql select product_name , sum(amount) as sum_amount from ... ``` Always use table aliases for all columns when querying from more than one table. Single character aliases are fine for a few tables, but are less likely to be clear as a query grows: ```sql select prj.name as project_name , count(bck.id) as backings_count from dbo.backings as bck join dbo.projects as prj on ... ``` Always use `as` to rename columns. `as` statements can be used for additional vertical alignment but don't have to be: __GOOD__: ```sql select projects.name as project_name , count(backings.id) as backings_count ... ``` __BAD__: ```sql select prj.name project_name , count(bck.id) backings_count ... ``` `case` statements should be aligned with a river after the `case` keyword, with `when`, `then`, and `end` on their own lines. ```sql select prj.name as project_name , prj.created_at , case when prj.category in ('stuff', 'and', 'things') then 'Amazing!' else 'Average' end as opinion from ... ``` Long Window functions should be split across multiple lines: one for each clause, aligned to the `partition` keyword. Partition keys can share the same line, or be split. Ascending order is an intuitive default and thus using an explicit `asc` is not necessary whereas `desc` is. All window functions should be aliased. ```sql row_number() over (partition by prj.category_id, prj.year order by pledged desc, last_name, first_name ) as sequence_number ``` [mmc]: https://github.com/mattmc3 [kickstarter-sql-guide]: https://gist.github.com/fredbenenson/7bb92718e19138c20591#file-kickstarter_sql_style_guide-md [editor-config]: https://EditorConfig.org [simon]: https://www.simonholywell.com/?utm_source=sqlstyle.guide&utm_medium=link&utm_campaign=md-document "SimonHolywell.com" [issue]: https://github.com/treffynnon/sqlstyle.guide/issues "SQL style guide issues on GitHub" [fork]: https://github.com/treffynnon/sqlstyle.guide/fork "Fork SQL style guide on GitHub" [pull]: https://github.com/treffynnon/sqlstyle.guide/pulls/ "SQL style guide pull requests on GitHub" [celko]: https://www.amazon.com/gp/product/0120887975/ref=as_li_ss_tl?ie=UTF8&linkCode=ll1&tag=treffynnon-20&linkId=9c88eac8cd420e979675c815771313d5 "Joe Celko's SQL Programming Style (The Morgan Kaufmann Series in Data Management Systems)" [dl-md]: https://raw.githubusercontent.com/treffynnon/sqlstyle.guide/gh-pages/_includes/sqlstyle.guide.md "Download the guide in Markdown format" [iso-8601]: https://en.wikipedia.org/wiki/ISO_8601 "Wikipedia: ISO 8601" [rivers]: http://practicaltypography.com/one-space-between-sentences.html "Practical Typography: one space between sentences" [reserved-keywords]: #reserved-keyword-reference "Reserved keyword reference" [eav]: https://en.wikipedia.org/wiki/Entity%E2%80%93attribute%E2%80%93value_model "Wikipedia: Entity–attribute–value model" [sqlstyleguide]: http://www.sqlstyle.guide "SQL style guide by Simon Holywell" [licence]: http://creativecommons.org/licenses/by-sa/4.0/ "Creative Commons Attribution-ShareAlike 4.0 International License" [sql-server-keywords]: https://docs.microsoft.com/en-us/sql/t-sql/language-elements/reserved-keywords-transact-sql "SQL Server reserved keywords" [mysql-keywords]: https://dev.mysql.com/doc/refman/8.0/en/keywords.html "MySQL keywords and reserved words"