Massive update SQL SQL Server Name Convention and T-SQL Programming Style

This commit is contained in:
Konstantin Taranov
2020-01-31 14:29:47 +03:00
parent e403eed719
commit c9fcd48fd6
@@ -2,108 +2,153 @@
> There are only two hard things in Computer Science: cache invalidation and naming things > There are only two hard things in Computer Science: cache invalidation and naming things
> -- <cite>[Phil Karlton](https://www.karlton.org/2017/12/naming-things-hard/)</cite> > -- <cite>[Phil Karlton](https://www.karlton.org/2017/12/naming-things-hard/)</cite>
[Naming convention][99] is a set of rules for choosing the character sequence to be used for identifiers which denote variables, types, functions, and other entities in source code and documentation. [Naming convention](https://en.wikipedia.org/wiki/Naming_convention_(programming)) is a set of rules for choosing the character sequence to be used for identifiers which denote variables, types, functions, and other entities in source code and documentation.
Reasons for using a naming convention (as opposed to allowing programmers to choose any character sequence) include the following: Reasons for using a naming convention (as opposed to allowing programmers to choose any character sequence) include the following:
- To reduce the effort needed to read and understand source code; - To reduce the effort needed to read and understand source code.
- To enable code reviews to focus on more important issues than arguing over syntax and naming standards. - To enable code reviews to focus on more important issues than arguing over syntax and naming standards.
- To enable code quality review tools to focus their reporting mainly on significant issues other than syntax and style preferences. - To enable code quality review tools to focus their reporting mainly on significant issues other than syntax and style preferences.
[99]:https://en.wikipedia.org/wiki/Naming_convention_(programming)
## Table of Contents ## Table of Contents
- [SQL Server Object Name Convention](#sql-server-object-name-convention) - [SQL Server Object Name Convention](#sql-server-object-name-convention)
- [SQL Server Data Types Recommendation](#data-types-recommendation) - [SQL Server Data Types Recommendation](#data-types-recommendation)
- [T-SQL Programming Style](#t-sql-programming-style) - [T-SQL Programming T-SQL Style](#t-sql-programming-style)
- [General programming style](#general-programming-style) - [General T-SQL programming style](#general-t-sql-programming-style)
- [Stored procedures and functions programming style](#programming-style) - [Stored procedures and functions programming style](#programming-style)
- [Reference and useful links](#reference) - [Dynamic T-SQL Recommendation](#dynamic-t-sql-recommendation)
- [Reference and useful links](#reference)
<a id="sql-server-object-name-convention"></a>
## SQL Server Object Name Convention ## SQL Server Object Name Convention
<a id="sql-server-object-name-convention"></a>
| Object | Code | Notation | Length | Plural | Prefix | Suffix | Abbreviation | Char Mask | Example | | Object | Code | Notation | Length | Plural | Prefix | Suffix | Abbreviation | Char Mask | Example |
|----------------------------------|------| ---------- |-------:|--------|--------|--------|--------------|--------------|--------------------------------------| |------------------------------------------|------| ---------- |-------:|--------|---------|--------|--------------|--------------|--------------------------------------|
| Database | | UPPERCASE | 30 | No | No | No | Yes | [A-z] | `MYDATABASE` | | [Database] | | UPPERCASE | 30 | No | No | No | Yes | [A-z] | `MYDATABASE` |
| Database Trigger | | PascalCase | 50 | No | DTR_ | No | Yes | [A-z] | `DTR_CheckLogin` | | [Schema] | | lowercase | 30 | No | No | No | Yes | [a-z][0-9] | `myschema` |
| Schema | | lowercase | 30 | No | No | No | Yes | [a-z][0-9] | `myschema` | | [Global Temporary Table] | | PascalCase | 117 | No | No | No | Yes | ##[A-z][0-9] | `##MyTable` |
| File Table | | PascalCase | 128 | No | FT_ | No | Yes | [A-z][0-9] | `FT_MyTable` | | [Local Temporary Table] | | PascalCase | 116 | No | No | No | Yes | #[A-z][0-9] | `#MyTable` |
| Global Temporary Table | | PascalCase | 117 | No | No | No | Yes | ##[A-z][0-9] | `##MyTable` | | [File Table] | | PascalCase | 128 | No | `FT_` | No | Yes | [A-z][0-9] | `FT_MyTable` |
| Local Temporary Table | | PascalCase | 116 | No | No | No | Yes | #[A-z][0-9] | `#MyTable` | | [Memory-optimized SCHEMA_AND_DATA Table] | | PascalCase | 128 | No | `MT_` | `_SD` | Yes | [A-z][0-9] | `MT_MyTable_SD` |
| Table | U | PascalCase | 128 | No | No | No | Yes | [A-z][0-9] | `MyTable` | | [Memory-optimized SCHEMA_ONLY Table] | | PascalCase | 128 | No | `MT_` | `_SO` | Yes | [A-z][0-9] | `MT_MyTable_SO` |
| Table Column | | PascalCase | 128 | No | No | No | Yes | [A-z][0-9] | `MyColumn` | | [Temporal Table] | | PascalCase | 128 | No | No | `_TT` | Yes | [A-z][0-9] | `MyTable_TT` |
| Table Default Values | D | PascalCase | 128 | No | DF_ | No | Yes | [A-z][0-9] | `DF_MyTable_MyColumn` | | [Disk-Based Table] | U | PascalCase | 128 | No | No | No | Yes | [A-z][0-9] | `MyTable` |
| Table Check Column Constraint | C | PascalCase | 128 | No | CK_ | No | Yes | [A-z][0-9] | `CK_MyTable_MyColumn` | | [Disk-Based Wide Table - SPARSE Column] | U | PascalCase | 128 | No | No | `_SPR` | Yes | [A-z][0-9] | `MyTable_SPR` |
| Table Check Table Constraint | C | PascalCase | 128 | No | CTK_ | No | Yes | [A-z][0-9] | `CTK_MyTable_MyColumn_AnotherColumn` | | [Table Column] | | PascalCase | 128 | No | No | No | Yes | [A-z][0-9] | `MyColumn` |
| Table Primary Key | PK | PascalCase | 128 | No | PK_ | No | Yes | [A-z][0-9] | `PK_MyTableID` | | [Table Column SPARSE] | | PascalCase | 128 | No | No | `_SPR` | Yes | [A-z][0-9] | `MyColumn_SPR` |
| Table Alternative Key | UQ | PascalCase | 128 | No | AK_ | No | Yes | [A-z][0-9] | `AK_MyTable_MyColumn_AnotherColumn` | | [Columns Check Constraint] | C | PascalCase | 128 | No | `CTK_` | No | Yes | [A-z][0-9] | `CTK_MyTable_MyColumn_AnotherColumn` |
| Table Foreign Key | F | PascalCase | 128 | No | FK_ | No | Yes | [A-z][0-9] | `FK_MyTable_ForeignTableID` | | [Column Check Constraint] | C | PascalCase | 128 | No | `CK_` | No | Yes | [A-z][0-9] | `CK_MyTable_MyColumn` |
| Table Clustered Index | | PascalCase | 128 | No | IXC_ | No | Yes | [A-z][0-9] | `IXC_MyTable_MyColumn_AnotherColumn` | | [Column Default Values] | D | PascalCase | 128 | No | `DF_` | No | Yes | [A-z][0-9] | `DF_MyTable_MyColumn` |
| Table Non Clustered Index | | PascalCase | 128 | No | IX_ | No | Yes | [A-z][0-9] | `IX_MyTable_MyColumn_AnotherColumn` | | [Table Primary Key] | PK | PascalCase | 128 | No | `PK_` | No | Yes | [A-z][0-9] | `PK_MyTableID` |
| Table Trigger | TR | PascalCase | 128 | No | TR_ | No | Yes | [A-z][0-9] | `TR_MyTable_LogicalName` | | [Table Unique (Alternative) Key] | UQ | PascalCase | 128 | No | `AK_` | No | Yes | [A-z][0-9] | `AK_MyTable_MyColumn_AnotherColumn` |
| View | V | PascalCase | 128 | No | VI_ | No | No | [A-z][0-9] | `VI_LogicalName` | | [Table Foreign Key] | F | PascalCase | 128 | No | `FK_` | No | Yes | [A-z][0-9] | `FK_MyTable_ForeignTableID` |
| Stored Procedure | P | PascalCase | 128 | No | usp_ | No | No | [A-z][0-9] | `usp_LogicalName` | | [Table Clustered Index] | | PascalCase | 128 | No | `IXC` | No | Yes | [A-z][0-9] | `IXC_MyTable_MyColumn_AnotherColumn` |
| Scalar User-Defined Function | FN | PascalCase | 128 | No | udf_ | No | No | [A-z][0-9] | `udf_FunctionLogicalName` | | [Table Non Clustered Index] | | PascalCase | 128 | No | `IX_` | No | Yes | [A-z][0-9] | `IX_MyTable_MyColumn_AnotherColumn` |
| Table-Valued Function | FN | PascalCase | 128 | No | tvf_ | No | No | [A-z][0-9] | `tvf_FunctionLogicalName` | | [DDL Trigger] | TR | PascalCase | 128 | No | `TR_` | `_DDL` | Yes | [A-z][0-9] | `TR_LogicalName_DDL` |
| Synonym | SN | camelCase | 128 | No | sy_ | No | No | [A-z][0-9] | `sy_logicalName` | | [DML Trigger] | TR | PascalCase | 128 | No | `TR_` | `_DML` | Yes | [A-z][0-9] | `TR_MyTable_LogicalName_DML` |
| Sequence | SO | PascalCase | 128 | No | sq_ | No | No | [A-z][0-9] | `sq_TableName` | | [Logon Trigger] | TR | PascalCase | 128 | No | `TR_` | `_LOG` | Yes | [A-z][0-9] | `TR_LogicalName_LOG` |
| CLR Assembly | | PascalCase | 128 | No | CA | No | Yes | [A-z][0-9] | `CALogicalName` | | [View] | V | PascalCase | 128 | No | `VI_` | No | No | [A-z][0-9] | `VI_LogicalName` |
| CLR Stored Procedures | PC | PascalCase | 128 | No | pc_ | No | Yes | [A-z][0-9] | `pc_CAName_LogicalName` | | [Indexed View] | V | PascalCase | 128 | No | `VIX_` | No | No | [A-z][0-9] | `VIx_LogicalName` |
| CLR Scalar User-Defined Function | | PascalCase | 128 | No | cudf_ | No | No | [A-z][0-9] | `cudf_CAName_LogicalName` | | [Stored Procedure] | P | PascalCase | 128 | No | `usp_` | No | No | [A-z][0-9] | `usp_LogicalName` |
| CLR Table-Valued Function | | PascalCase | 128 | No | ctvf_ | No | No | [A-z][0-9] | `ctvf_CAName_LogicalName` | | [Scalar User-Defined Function] | FN | PascalCase | 128 | No | `udf_` | No | No | [A-z][0-9] | `udf_FunctionLogicalName` |
| CLR User-Defined Aggregates | | PascalCase | 128 | No | ca_ | No | No | [A-z][0-9] | `ca_CAName_LogicalName` | | [Table-Valued Function] | FN | PascalCase | 128 | No | `tvf_` | No | No | [A-z][0-9] | `tvf_FunctionLogicalName` |
| CLR User-Defined Types | | PascalCase | 128 | No | ct_ | No | No | [A-z][0-9] | `ct_CAName_LogicalName` | | [Synonym] | SN | camelCase | 128 | No | `sy_` | No | No | [A-z][0-9] | `sy_logicalName` |
| CLR Triggers | | PascalCase | 128 | No | ctr_ | No | No | [A-z][0-9] | `ctr_CAName_LogicalName` | | [Sequence] | SO | PascalCase | 128 | No | `sq_` | No | No | [A-z][0-9] | `sq_TableName` |
| [CLR Assembly] | | PascalCase | 128 | No | `CA ` | No | Yes | [A-z][0-9] | `CALogicalName` |
| [CLR Stored Procedures] | PC | PascalCase | 128 | No | `pc_` | No | Yes | [A-z][0-9] | `pc_CAName_LogicalName` |
| [CLR Scalar User-Defined Function] | | PascalCase | 128 | No | `cudf_` | No | No | [A-z][0-9] | `cudf_CAName_LogicalName` |
| [CLR Table-Valued Function] | | PascalCase | 128 | No | `ctvf_` | No | No | [A-z][0-9] | `ctvf_CAName_LogicalName` |
| [CLR User-Defined Aggregates] | | PascalCase | 128 | No | `ca_` | No | No | [A-z][0-9] | `ca_CAName_LogicalName` |
| [CLR User-Defined Types] | | PascalCase | 128 | No | `ct_` | No | No | [A-z][0-9] | `ct_CAName_LogicalName` |
| [CLR Triggers] | | PascalCase | 128 | No | `ctr_` | No | No | [A-z][0-9] | `ctr_CAName_LogicalName` |
[Database]:https://docs.microsoft.com/en-us/sql/t-sql/statements/create-database-transact-sql
[Schema]:https://docs.microsoft.com/en-us/sql/relational-databases/security/authentication-access/create-a-database-schema
[Global Temporary Table]:https://docs.microsoft.com/en-us/sql/relational-databases/tables/tables
[Local Temporary Table]:https://docs.microsoft.com/en-us/sql/relational-databases/tables/tables
[File Table]:https://docs.microsoft.com/en-us/sql/relational-databases/blob/filetables-sql-server
[Memory-optimized SCHEMA_AND_DATA Table]:https://docs.microsoft.com/en-us/sql/relational-databases/in-memory-oltp/introduction-to-memory-optimized-tables
[Memory-optimized SCHEMA_ONLY Table]:https://docs.microsoft.com/en-us/sql/relational-databases/in-memory-oltp/defining-durability-for-memory-optimized-objects
[Temporal Table]:https://docs.microsoft.com/en-us/sql/relational-databases/tables/temporal-tables
[Disk-Based Table]:https://docs.microsoft.com/en-us/sql/relational-databases/in-memory-oltp/comparing-disk-based-table-storage-to-memory-optimized-table-storage
[Disk-Based Wide Table - SPARSE Column]:https://docs.microsoft.com/en-us/sql/relational-databases/tables/tables#wide-tables
[Table Column]:https://docs.microsoft.com/en-us/sql/t-sql/statements/alter-table-transact-sql
[Table Column SPARSE]:https://docs.microsoft.com/en-us/sql/relational-databases/tables/use-sparse-columns
[Columns Check Constraint]:https://docs.microsoft.com/en-us/sql/relational-databases/tables/create-check-constraints
[Column Check Constraint]:https://docs.microsoft.com/en-us/sql/relational-databases/tables/create-check-constraints
[Column Default Values]:https://docs.microsoft.com/en-us/sql/relational-databases/tables/specify-default-values-for-columns
[Table Primary Key]:https://docs.microsoft.com/en-us/sql/relational-databases/tables/create-primary-keys
[Table Unique (Alternative) Key]:https://docs.microsoft.com/en-us/sql/relational-databases/tables/create-unique-constraints
[Table Foreign Key]:https://docs.microsoft.com/en-us/sql/relational-databases/tables/create-foreign-key-relationships
[Table Clustered Index]:https://docs.microsoft.com/en-us/sql/relational-databases/indexes/clustered-and-nonclustered-indexes-described
[Table Non Clustered Index]:https://docs.microsoft.com/en-us/sql/relational-databases/indexes/clustered-and-nonclustered-indexes-described
[DDL Trigger]:https://docs.microsoft.com/en-us/sql/t-sql/statements/create-trigger-transact-sql
[DML Trigger]:https://docs.microsoft.com/en-us/sql/relational-databases/triggers/dml-triggers
[Logon Trigger]:https://docs.microsoft.com/en-us/sql/t-sql/statements/create-trigger-transact-sql
[View]:https://docs.microsoft.com/en-us/sql/relational-databases/views/views
[Indexed View]:https://docs.microsoft.com/en-us/sql/relational-databases/views/create-indexed-views
[Stored Procedure]:https://docs.microsoft.com/en-us/sql/t-sql/statements/create-procedure-transact-sql
[Scalar User-Defined Function]:https://docs.microsoft.com/en-us/sql/relational-databases/user-defined-functions/create-user-defined-functions-database-engine#Scalar
[Table-Valued Function]:https://docs.microsoft.com/en-us/sql/relational-databases/user-defined-functions/create-user-defined-functions-database-engine#TVF
[Synonym]:https://docs.microsoft.com/en-us/sql/relational-databases/synonyms/synonyms-database-engine
[Sequence]:https://docs.microsoft.com/en-us/sql/relational-databases/sequence-numbers/sequence-numbers
[CLR Assembly]:https://docs.microsoft.com/en-us/sql/relational-databases/clr-integration/assemblies/creating-an-assembly
[CLR Stored Procedures]:https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/sql/clr-stored-procedures
[CLR Scalar User-Defined Function]:https://docs.microsoft.com/en-us/sql/relational-databases/clr-integration-database-objects-user-defined-functions/clr-user-defined-functions
[CLR Table-Valued Function]:https://docs.microsoft.com/en-us/sql/relational-databases/clr-integration-database-objects-user-defined-functions/clr-table-valued-functions
[CLR User-Defined Aggregates]:https://docs.microsoft.com/en-us/sql/relational-databases/clr-integration-database-objects-user-defined-functions/clr-user-defined-aggregates
[CLR User-Defined Types]:https://docs.microsoft.com/en-us/sql/relational-databases/clr-integration-database-objects-user-defined-types/clr-user-defined-types
[CLR Triggers]:https://docs.microsoft.com/en-us/sql/relational-databases/triggers/create-clr-triggers
**[⬆ back to top](#table-of-contents)** **[⬆ back to top](#table-of-contents)**
<a id="data-types-recommendation"></a>
## SQL Server Data Types Recommendation ## SQL Server Data Types Recommendation
More details about SQL Server data types and mapping it with another databases you can find [here](https://github.com/ktaranov/sqlserver-kit/blob/master/SQL%20Server%20Data%20Types.md) <a id="data-types-recommendation"></a>
More details about SQL Server data types and mapping it with another databases and program languages you can find [here](https://github.com/ktaranov/sqlserver-kit/blob/master/SQL%20Server%20Data%20Types.md)
| General Type | Type | Recommended | What use instead | Why use or not | | General Type | Type | ANSI | Recommended | What use instead | Why use or not |
|----------------------|---------------------|----------------|--------------------|-----------------------------------------------------------| |----------------------|---------------------|------|----------------|--------------------|------------------------------------------------------------------------------------|
| Exact Numerics | [bit] | *Maybe* | [tinyint][1] | | | Exact Numerics | [bit] | No | *Maybe* | [tinyint][1] | `bit` convert any number (except 0) to 1, 0 converted to 0 |
| Exact Numerics | [tinyint][1] | *Maybe* | [int][1] | | | Exact Numerics | [tinyint][1] | No | *Maybe* | [int][1] | for saving 3 bytes compare to `int` data type or for replacing `bit` data type |
| Exact Numerics | [smallint][1] | *Maybe* | [int][1] | | | Exact Numerics | [smallint][1] | Yes | *Maybe* | [int][1] | for saving 2 bytes compare to `int` data type |
| Exact Numerics | [int][1] | Yes | - | | | Exact Numerics | [int][1] | Yes | Yes | - | |
| Exact Numerics | [bigint][1] | Yes | [int][1] | | | Exact Numerics | [bigint][1] | No | Yes | [int][1] | if you work more than |
| Exact Numerics | [decimal][2] | Yes | - | | | Exact Numerics | [decimal][2] | Yes | Yes | - | |
| Exact Numerics | [smallmoney][3] | No | [decimal][2] | [possibility to loss precision due to rounding errors][9] | | Exact Numerics | [smallmoney][3] | No | *Maybe* | [decimal][2] | [possibility to loss precision due to rounding errors][9] |
| Exact Numerics | [money][3] | No | [decimal][2] | [possibility to loss precision due to rounding errors][9] | | Exact Numerics | [money][3] | No | *Maybe* | [decimal][2] | [possibility to loss precision due to rounding errors][9] |
| Approximate Numerics | [real][4] | Yes | - | | | Approximate Numerics | [real][4] | Yes | Yes | - | |
| Approximate Numerics | [float][4] | Yes | - | | | Approximate Numerics | [float][4](1-24) | Yes | No | [real][4] | SQL Server automatically converts `float(1-24)` to `real` data type |
| Date and Time | [date] | Yes | - | | | Approximate Numerics | [float][4](24-53) | Yes | Yes | - | |
| Date and Time | [smalldatetime] | *Maybe* | [date] | | | Date and Time | [date] | Yes | Yes | - | |
| Date and Time | [time] | Yes | - | | | Date and Time | [smalldatetime] | No | *Maybe* | [date] | |
| Date and Time | [datetime2] | Yes | - | | | Date and Time | [time] | Yes | Yes | - | |
| Date and Time | [datetime] | No | [datetime2] | | | Date and Time | [datetime2] | No | Yes | - | |
| Date and time | [datetimeoffset] | Yes | - | | | Date and Time | [datetime] | Yes | *Maybe* | [datetime2] | [On the Advantages of DateTime2(n) over DateTime] |
| Character Strings | [char][5] | *Maybe* | | | | Date and time | [datetimeoffset] | No | Yes | - | |
| Character Strings | [varchar][5] | Yes | [varchar][5] | | | Character Strings | [char][5] | Yes | *Maybe* | [varchar][5] | Save 1 byte from `varchar`, but be ready for trailing spaces |
| Character Strings | [varchar(max)][5] | Yes | - | | | Character Strings | [varchar][5] | Yes | Yes | - | |
| Character Strings | [nchar][6] | *Maybe* | [nvarchar][6] | | | Character Strings | [varchar(max)][5] | Yes | Yes | - | |
| Character Strings | [nvarchar][6] | Yes | - | | | Character Strings | [nchar][6] | Yes | *Maybe* | [nvarchar][6] | |
| Character Strings | [nvarchar(max)][6] | Yes | - | | | Character Strings | [nvarchar][6] | Yes | Yes | - | |
| Character Strings | [ntext][7] | **Deprecated** | [nvarchar(max)][6] | | | Character Strings | [nvarchar(max)][6] | Yes | Yes | - | |
| Character Strings | [text][7] | **Deprecated** | [nvarchar(max)][6] | | | Character Strings | [ntext][7] | No | **Deprecated** | [nvarchar(max)][6] | [NVARCHAR(MAX) VS NTEXT in SQL Server] |
| Binary Strings | [image][7] | **Deprecated** | [nvarchar(max)][6] | | | Character Strings | [text][7] | No | **Deprecated** | [varchar(max)][6] | [Differences Between Sql Server TEXT and VARCHAR(MAX) Data Type] |
| Binary Strings | [binary][8] | **Deprecated** | [nvarchar(max)][6] | | | Binary Strings | [image][7] | No | **Deprecated** | [varbinary(max)][8]| [VARBINARY(MAX) Tames the BLOB] |
| Binary Strings | [varbinary][8] | Yes | - | | | Binary Strings | [binary][8] | Yes | **Deprecated** | [varbinary][8] | [Conversions between any data type and the binary data types are not guaranteed][8]|
| Binary Strings | [varbinary(max)][8] | Yes | - | | | Binary Strings | [varbinary][8] | Yes | Yes | - | |
| Other Data Types | [cursor] | *Maybe* | - | | | Binary Strings | [varbinary(max)][8] | Yes | Yes | - | |
| Other Data Types | [sql_variant] | No | [varchar][5]? | | | Other Data Types | [cursor] | No | Yes | - | |
| Other Data Types | [hierarchyid] | *Maybe* | - | | | Other Data Types | [sql_variant] | No | Yes | - | |
| Other Data Types | [rowversion] | *Maybe* | - | | | Other Data Types | [hierarchyid] | No | Yes | - | |
| Other Data Types | [timestamp] | **Deprecated** | [rowversion] | it is just synonym to [rowversion] data type | | Other Data Types | [rowversion] | No | *Maybe* | - | |
| Other Data Types | [uniqueidentifier] | Yes | - | | | Other Data Types | [timestamp] | No | **Deprecated** | [rowversion] | it is just synonym to [rowversion] data type and must be [removed][] |
| Other Data Types | [xml] | Yes | - | | | Other Data Types | [uniqueidentifier] | No | Yes | - | |
| Other Data Types | [table] | *Maybe* | - | | | Other Data Types | [xml] | Yes | Yes | - | |
| Spatial Data Types | [geometry] | Yes | - | | | Other Data Types | [table] | No | *Maybe* | - | |
| Spatial Data Types | [geography] | Yes | - | | | Spatial Data Types | [geometry] | No | Yes | - | |
| Spatial Data Types | [geography] | No | Yes | - | |
[1]:https://docs.microsoft.com/sql/t-sql/data-types/int-bigint-smallint-and-tinyint-transact-sql [1]:https://docs.microsoft.com/sql/t-sql/data-types/int-bigint-smallint-and-tinyint-transact-sql
[2]:https://docs.microsoft.com/sql/t-sql/data-types/decimal-and-numeric-transact-sql [2]:https://docs.microsoft.com/sql/t-sql/data-types/decimal-and-numeric-transact-sql
@@ -133,81 +178,118 @@ More details about SQL Server data types and mapping it with another databases y
[table]:https://docs.microsoft.com/sql/t-sql/data-types/table-transact-sql [table]:https://docs.microsoft.com/sql/t-sql/data-types/table-transact-sql
[geometry]:https://docs.microsoft.com/sql/t-sql/spatial-geometry/spatial-types-geometry-transact-sql [geometry]:https://docs.microsoft.com/sql/t-sql/spatial-geometry/spatial-types-geometry-transact-sql
[geography]:https://docs.microsoft.com/sql/t-sql/spatial-geography/spatial-types-geography [geography]:https://docs.microsoft.com/sql/t-sql/spatial-geography/spatial-types-geography
[On the Advantages of DateTime2(n) over DateTime]:http://www.sqltact.com/2012/12/on-advantages-of-datetime2n-over.html
[Differences Between Sql Server TEXT and VARCHAR(MAX) Data Type]:https://sqlhints.com/2016/05/11/differences-between-sql-server-text-and-varcharmax-data-type/
[NVARCHAR(MAX) VS NTEXT in SQL Server]:https://www.sqlservercurry.com/2010/07/nvarcharmax-vs-ntext-in-sql-server.html
[VARBINARY(MAX) Tames the BLOB]:https://www.itprotoday.com/microsoft-visual-studio/varbinarymax-tames-blob
[removed]:https://feedback.azure.com/forums/908035-sql-server/suggestions/32889865-deprecate-timestamp-the-keyword-not-rowversion-i
**[⬆ back to top](#table-of-contents)** **[⬆ back to top](#table-of-contents)**
## T-SQL Programming Style ## T-SQL Programming Style
SQL Server T-SQL Coding Conventions, Best Practices, and Programming Guidelines <a id="t-sql-programming-style"></a>
SQL Server T-SQL Coding Conventions, Best Practices, and Programming Guidelines.
### General programming style ### General programming T-SQL style
<a id="#general-t-sql-programming-style"></a>
- For database objects names in code please use only schema plus object name, do not hardcore server and database name in your code: `dbo.MyTable` is good and NOT `PRODSERVER.PRODDB.dbo.MyTable`. - For database objects names in code use only schema plus object name, do not hardcode server and database names in your code: `dbo.MyTable` is good and bad `PRODSERVER.PRODDB.dbo.MyTable`.
More details [here](https://www.red-gate.com/simple-talk/opinion/editorials/why-you-shouldnt-hardcode-the-current-database-name-in-your-views-functions-and-stored-procedures/). More details [here](https://www.red-gate.com/simple-talk/opinion/editorials/why-you-shouldnt-hardcode-the-current-database-name-in-your-views-functions-and-stored-procedures/),
- Delimiters: spaces (not tabs) [here](https://www.sqlserverscience.com/basics/on-default-schemas-and-search-paths/) and [here](https://www.red-gate.com/hub/product-learning/sql-prompt/finding-code-smells-using-sql-prompt-procedures-lack-schema-qualification).
- Avoid using asterisk in select statements `SELECT *`, use explicit column names. More details [here](https://www.red-gate.com/hub/product-learning/sql-prompt/finding-code-smells-using-sql-prompt-asterisk-select-list) - Delimiters: **spaces** (not tabs)
- No square brackets `[]` and [reserved words](https://github.com/ktaranov/sqlserver-kit/blob/master/Scripts/Check_Reserved_Words_For_Object_Names.sql) in object names and alias, use only Latin symbols **`[A-z]`** and numeric **`[0-9]`** - Avoid using asterisk in select statements `SELECT *`, use explicit column names.
- Prefer [ANSI syntax](http://standards.iso.org/ittf/PubliclyAvailableStandards/c053681_ISO_IEC_9075-1_2011.zip) and functions More details [here](https://www.red-gate.com/hub/product-learning/sql-prompt/finding-code-smells-using-sql-prompt-asterisk-select-list).
- All finished expressions should have semicolon `;` at the end. This is ANSI standard and Microsoft announced with the SQL Server 2008 release that semicolon statement terminators will become mandatory in a future version so statement terminators other than semicolons (whitespace) are currently deprecated. This deprecation announcement means that you should always use semicolon terminators in new development. - No square brackets `[]` and [reserved words](https://github.com/ktaranov/sqlserver-kit/blob/master/Scripts/Check_Reserved_Words_For_Object_Names.sql) in object names and alias, use only Latin symbols **`[A-z]`** and numeric **`[0-9]`**.
- Prefer [ANSI syntax](http://standards.iso.org/ittf/PubliclyAvailableStandards/c053681_ISO_IEC_9075-1_2011.zip) and functions ([`CAST`][10] instead [`CONVERT`][10], [`COALESE`](https://docs.microsoft.com/en-us/sql/t-sql/language-elements/coalesce-transact-sql) instead [`ISNULL`](https://docs.microsoft.com/en-us/sql/t-sql/functions/isnull-transact-sql), etc.).
- All finished expressions should have semicolon `;` at the end.
This is ANSI standard and Microsoft announced with the SQL Server 2008 release that semicolon statement terminators will become mandatory in a future version so statement terminators other than semicolons (whitespace) are currently deprecated.
This deprecation announcement means that you should always use semicolon terminators in new development.
More details [here](http://www.dbdelta.com/always-use-semicolon-statement-terminators/). More details [here](http://www.dbdelta.com/always-use-semicolon-statement-terminators/).
- All script files should end with `GO` and line break - All script files should end with `GO` and line break.
- Avoid non-standard column aliases, use ,if required, double-quotes and always `AS` keyword: `SELECT p.LastName AS "Last Name" FROM dbo.Person AS p;`. - Keywords should be in **UPPERCASE**: `SELECT`, `FROM`, `GROUP BY` etc.
- Data types declaration should be in **lowercase**: `varchar(30)`, `int`, `real`, `nvarchar(max)` etc.
More details [here](https://www.sentryone.com/blog/aaronbertrand/backtobasics-lower-case-data-types).
- All system database and tables must be in **lowercase** for properly working for Case Sensitive instance: `master, sys.tables …`.
- Avoid non-standard column aliases, use, if required, double-quotes for special characters and always `AS` keyword before alias:
```sql
SELECT
p.LastName AS "Last Name"
FROM dbo.Person AS p;
```
More details [here](https://www.red-gate.com/hub/product-learning/sql-prompt/sql-prompt-code-analysis-avoid-non-standard-column-aliases). More details [here](https://www.red-gate.com/hub/product-learning/sql-prompt/sql-prompt-code-analysis-avoid-non-standard-column-aliases).
All possible ways using aliases in SQL Server: All possible ways using aliases in SQL Server:
```sql ```tsql
/* Recommended due to ANSI */ /* Recommended due to ANSI */
SELECT SCHEMA_NAME(schema_id)+'.'+[name] AS "Tables" FROM sys.tables; SELECT SCHEMA_NAME(schema_id) + '.' + "name" AS "Tables" FROM sys.tables;
/* Not recommended but possible */ /* Not recommended but possible */
SELECT Tables = SCHEMA_NAME(schema_id)+'.'+[name] FROM sys.tables; SELECT SCHEMA_NAME(schema_id) + '.' + [name] AS "Tables" FROM sys.tables;
SELECT "Tables" = SCHEMA_NAME(schema_id)+'.'+[name] FROM sys.tables; SELECT Tables = SCHEMA_NAME(schema_id) + '.' + [name] FROM sys.tables;
SELECT [Tables] = SCHEMA_NAME(schema_id)+'.'+[name] FROM sys.tables; SELECT "Tables" = SCHEMA_NAME(schema_id) + '.' + [name] FROM sys.tables;
SELECT 'Tables' = SCHEMA_NAME(schema_id)+'.'+[name] FROM sys.tables; SELECT [Tables] = SCHEMA_NAME(schema_id) + '.' + [name] FROM sys.tables;
SELECT SCHEMA_NAME(schema_id)+'.'+[name] [Tables] FROM sys.tables; SELECT 'Tables' = SCHEMA_NAME(schema_id) + '.' + [name] FROM sys.tables;
SELECT SCHEMA_NAME(schema_id)+'.'+[name] 'Tables' FROM sys.tables; SELECT SCHEMA_NAME(schema_id) + '.' + [name] [Tables] FROM sys.tables;
SELECT SCHEMA_NAME(schema_id)+'.'+[name] "Tables" FROM sys.tables; SELECT SCHEMA_NAME(schema_id) + '.' + [name] 'Tables' FROM sys.tables;
SELECT SCHEMA_NAME(schema_id)+'.'+[name] Tables FROM sys.tables; SELECT SCHEMA_NAME(schema_id) + '.' + [name] "Tables" FROM sys.tables;
SELECT SCHEMA_NAME(schema_id)+'.'+[name] AS [Tables] FROM sys.tables; SELECT SCHEMA_NAME(schema_id) + '.' + [name] Tables FROM sys.tables;
SELECT SCHEMA_NAME(schema_id)+'.'+[name] AS 'Tables' FROM sys.tables; SELECT SCHEMA_NAME(schema_id) + '.' + [name] AS [Tables] FROM sys.tables;
SELECT SCHEMA_NAME(schema_id)+'.'+[name] AS Tables FROM sys.tables; SELECT SCHEMA_NAME(schema_id) + '.' + [name] AS 'Tables' FROM sys.tables;
SELECT SCHEMA_NAME(schema_id) + '.' + [name] AS Tables FROM sys.tables;
``` ```
- The first argument in `SELECT` expression should be on the same line with it: `SELECT LastName …` - The first argument in `SELECT` expression should be on the next line:
- Arguments are divided by line breaks, commas should be placed before an argument:
```sql ```sql
SELECT FirstName SELECT
FirstName
```
- Arguments are divided by line breaks, commas should be placed before an argument:
```sql
SELECT
FirstName
, LastName , LastName
``` ```
- For SQL Server >= 2012 use `FETCH-OFFSET` instead `TOP`. - For SQL Server >= 2012 use [`FETCH-OFFSET`] instead [`TOP`].
But if you use `TOP` avoid use `TOP` in a `SELECT` statement without an `ORDER BY`. More details [here](https://docs.microsoft.com/en-us/sql/t-sql/queries/select-order-by-clause-transact-sql#using-offset-and-fetch-to-limit-the-rows-returned).
But if you use [`TOP`] avoid use [`TOP`] in a `SELECT` statement without an `ORDER BY`.
More details [here](https://www.red-gate.com/hub/product-learning/sql-prompt/finding-code-smells-using-sql-prompt-top-without-order-select-statement). More details [here](https://www.red-gate.com/hub/product-learning/sql-prompt/finding-code-smells-using-sql-prompt-top-without-order-select-statement).
- Use `TOP` function with brackets because `TOP` has supports use of an expression, such as `(@Rows*2)`, or a subquery: `SELECT TOP(100) LastName …`. - If you using [`TOP`] (instead recommended [`FETCH-OFFSET`]) function with round brackets because [`TOP`] has supports use of an expression, such as `(@Rows*2)`, or a sub query: `SELECT TOP(100) LastName …`.
More details [here](https://www.red-gate.com/hub/product-learning/sql-prompt/sql-prompt-code-analysis-avoiding-old-style-top-clause). Also `TOP` without brackets does not work with `UPDATE` and `DELETE` statements. More details [here](https://www.red-gate.com/hub/product-learning/sql-prompt/sql-prompt-code-analysis-avoiding-old-style-top-clause).
Also [`TOP`] without brackets does not work with `UPDATE` and `DELETE` statements.
```sql ```tsql
/* Not working without brackets () */ /* Not working without brackets () */
DECLARE @n int = 1; DECLARE @n int = 1;
SELECT TOP@n name FROM sys.objects; SELECT TOP@n name FROM sys.objects;
``` ```
- For demo queries use `TOP(100)` or lower value because SQL Server uses one sorting method for `TOP` 1-100 rows, and a different one for 101+ rows. - For demo queries use `TOP(100)` or lower value because SQL Server uses one sorting method for `TOP` 1-100 rows, and a different one for 101+ rows.
More details [here](https://www.brentozar.com/archive/2017/09/much-can-one-row-change-query-plan-part-2/). More details [here](https://www.brentozar.com/archive/2017/09/much-can-one-row-change-query-plan-part-2/).
- Keywords and data types declaration should be in **UPPERCASE** - Avoid specifying integers in the `ORDER BY` clause as positional representations of the columns in the select list.
- All objects must used with schema names but without database and server name: `FROM dbo.Table`. For stored procedure more details [here](https://www.red-gate.com/hub/product-learning/sql-prompt/finding-code-smells-using-sql-prompt-procedures-lack-schema-qualification). The statement with integers is not as easily understood by others compared with specifying the actual column name.
- All system database and tables must be in lower case for properly working for Case Sensitive instance: `master, sys.tables …` In addition, changes to the select list, such as changing the column order or adding new columns, requires modifying the `ORDER BY` clause in order to avoid unexpected results.
More details [here](https://docs.microsoft.com/en-us/sql/t-sql/queries/select-order-by-clause-transact-sql#best-practices).
```sql
/* bad */
SELECT ProductID, Name FROM Production.Production ORDER BY 2;
/* good */
SELECT ProductID, Name FROM Production.Production ORDER BY Name;
```
- Avoid using [`ISNUMERIC`](https://docs.microsoft.com/en-us/sql/t-sql/functions/isnumeric-transact-sql) function. Use for SQL Server >= 2012 [`TRY_CONVERT`](https://docs.microsoft.com/en-us/sql/t-sql/functions/try-convert-transact-sql) function and for SQL Server < 2012 `LIKE` expression: - Avoid using [`ISNUMERIC`](https://docs.microsoft.com/en-us/sql/t-sql/functions/isnumeric-transact-sql) function. Use for SQL Server >= 2012 [`TRY_CONVERT`](https://docs.microsoft.com/en-us/sql/t-sql/functions/try-convert-transact-sql) function and for SQL Server < 2012 `LIKE` expression:
```sql ```tsql
CASE WHEN Stuff(LTrim(TapAngle),1,1,'') NOT LIKE '%[^-+.ED0123456789]%' --is it a float? CASE WHEN STUFF(LTRIM(TapAngle),1,1,'') NOT LIKE '%[^-+.ED0123456789]%' /* is it a float? */
AND Left(LTrim(TapAngle),1) LIKE '[-.+0123456789]' AND LEFT(LTRIM(TapAngle),1) LIKE '[-.+0123456789]'
AND TapAngle LIKE '%[0123456789][ED][-+0123456789]%' AND TapAngle LIKE '%[0123456789][ED][-+0123456789]%'
AND Right(TapAngle ,1) LIKE N'[0123456789]' AND RIGHT(TapAngle ,1) LIKE N'[0123456789]'
THEN 'float' THEN 'float'
WHEN Stuff(LTrim(TapAngle),1,1,'') NOT LIKE '%[^.0123456789]%' --is it numeric WHEN STUFF(LTRIM(TapAngle),1,1,'') NOT LIKE '%[^.0123456789]%' /* is it numeric? */
AND Left(LTrim(TapAngle),1) LIKE '[-.+0123456789]' AND LEFT(LTRIM(TapAngle),1) LIKE '[-.+0123456789]'
AND TapAngle LIKE '%.%' AND TapAngle NOT LIKE '%.%.%' AND TapAngle LIKE '%.%' AND TapAngle NOT LIKE '%.%.%'
AND TapAngle LIKE '%[0123456789]%' AND TapAngle LIKE '%[0123456789]%'
THEN 'float' THEN 'float'
ELSE NULL ELSE NULL
END END
``` ```
@@ -219,10 +301,14 @@ SQL Server T-SQL Coding Conventions, Best Practices, and Programming Guidelines
- Avoid ambiguous formats for date-only literals, use `CAST('yyyymmdd' AS DATE)` format. - Avoid ambiguous formats for date-only literals, use `CAST('yyyymmdd' AS DATE)` format.
- Avoid treating dates like strings and avoid calculations on the left-hand side of the `WHERE` clause. - Avoid treating dates like strings and avoid calculations on the left-hand side of the `WHERE` clause.
More details [here](https://sqlblog.org/2009/10/16/bad-habits-to-kick-mis-handling-date-range-queries). More details [here](https://sqlblog.org/2009/10/16/bad-habits-to-kick-mis-handling-date-range-queries).
- Avoid using [hints](https://docs.microsoft.com/en-us/sql/t-sql/queries/hints-transact-sql) except `OPTION(RECOMPILE)` if needed. - Avoid using [hints](https://docs.microsoft.com/en-us/sql/t-sql/queries/hints-transact-sql) except `RECOMPILE` if needed and `NOEXPAND` (see next tip).
More details [here](https://www.red-gate.com/hub/product-learning/sql-prompt/sql-prompt-code-analysis-a-hint-is-used-pe004-7). More details [here](https://www.red-gate.com/hub/product-learning/sql-prompt/sql-prompt-code-analysis-a-hint-is-used-pe004-7).
- Use [`NOEXPAND`](https://docs.microsoft.com/en-us/sql/t-sql/queries/hints-transact-sql-table#using-noexpand) hint for [indexed views](https://docs.microsoft.com/sql/relational-databases/views/create-indexed-views) on non enterprise editions and Prior to SQL Server 2016 (13.x) SP1 to let the query optimizer know that we have indexes.
More details [here](https://bornsql.ca/blog/using-indexed-views-dont-forget-this-important-tip/).
- Avoid use of `SELECT…INTO` for production code, use instead `CREATE TABLE` + `INSERT INTO …` approach. More details [here](https://www.red-gate.com/hub/product-learning/sql-prompt/use-selectinto-statement). - Avoid use of `SELECT…INTO` for production code, use instead `CREATE TABLE` + `INSERT INTO …` approach. More details [here](https://www.red-gate.com/hub/product-learning/sql-prompt/use-selectinto-statement).
- Use only ISO standard JOINS syntaxes. The old style Microsoft/Sybase JOIN style for SQL, which uses the `=*` and `*=` syntax, has been deprecated and is no longer used. Queries that use this syntax will fail when the database engine level is 10 (SQL Server 2008) or later (compatibility level 100). The ANSI-89 table citation list (`FROM tableA, tableB`) is still ISO standard for `INNER JOINs` only. Neither of these styles are worth using. It is always better to specify the type of join you require` INNER`, `LEFT OUTER`, `RIGHT OUTER`, `FULL OUTER` and `CROSS`, which has been standard since ANSI SQL-92 was published. While you can choose any supported `JOIN `style, without affecting the query plan used by SQL Server, using the ANSI-standard syntax will make your code easier to understand, more consistent, and portable to other relational database systems. - Use only ISO standard JOINS syntaxes. The *old style* Microsoft/Sybase `JOIN` style for SQL, which uses the `=*` and `*=` syntax, has been deprecated and is no longer used.
Queries that use this syntax will fail when the database engine level is 10 (SQL Server 2008) or later (compatibility level 100). The ANSI-89 table citation list (`FROM tableA, tableB`) is still ISO standard for `INNER JOINs` only. Neither of these styles are worth using.
It is always better to specify the type of join you require` INNER`, `LEFT OUTER`, `RIGHT OUTER`, `FULL OUTER` and `CROSS`, which has been standard since ANSI SQL-92 was published. While you can choose any supported `JOIN `style, without affecting the query plan used by SQL Server, using the ANSI-standard syntax will make your code easier to understand, more consistent, and portable to other relational database systems.
More details [here](https://www.red-gate.com/hub/product-learning/sql-prompt/finding-code-smells-using-sql-prompt-old-style-join-syntax-st001). More details [here](https://www.red-gate.com/hub/product-learning/sql-prompt/finding-code-smells-using-sql-prompt-old-style-join-syntax-st001).
- Do not use a scalar user-defined function (UDF) in a `JOIN` condition, `WHERE` search condition, or in a `SELECT` list, unless the function is [schema-bound](https://docs.microsoft.com/en-us/sql/t-sql/statements/create-function-transact-sql#best-practices). - Do not use a scalar user-defined function (UDF) in a `JOIN` condition, `WHERE` search condition, or in a `SELECT` list, unless the function is [schema-bound](https://docs.microsoft.com/en-us/sql/t-sql/statements/create-function-transact-sql#best-practices).
More details [here](https://www.red-gate.com/hub/product-learning/sql-prompt/misuse-scalar-user-defined-function-constant-pe017). More details [here](https://www.red-gate.com/hub/product-learning/sql-prompt/misuse-scalar-user-defined-function-constant-pe017).
@@ -230,53 +316,86 @@ SQL Server T-SQL Coding Conventions, Best Practices, and Programming Guidelines
More details [here](https://www.brentozar.com/archive/2018/08/a-common-query-error/). More details [here](https://www.brentozar.com/archive/2018/08/a-common-query-error/).
- For concatenate unicode strings: - For concatenate unicode strings:
- always using the upper-case `N`; - always using the upper-case `N`;
- always store into a variable of type `NVARCHAR(MAX)`; - always store into a variable of type `nvarchar(max)`;
- avoid truncation of string literals, simply ensure that one piece is converted to `NVARCHAR(MAX)`. - avoid truncation of string literals, simply ensure that one piece is converted to `nvarchar(max)`.
Example: Example:
`SET @NVCmaxVariable = CONVERT(NVARCHAR(MAX), N'anything') + N'something else' + N'another';` ```tsql
DECLARE @nvcmaxVariable nvarchar(max);
SET @nvcmaxVariable = CAST(N'ಠ russian anomaly ЯЁЪ ಠ ' AS nvarchar(max)) + N'something else' + N'another';
SELECT @nvcmaxVariable;
```
More details [here](https://themondaymorningdba.wordpress.com/2018/09/13/them-concatenatin-blues/). More details [here](https://themondaymorningdba.wordpress.com/2018/09/13/them-concatenatin-blues/).
- Always specify a length to any text-based data type such as `VARCHAR`, `NVARCHAR`, `CHAR`, `NCHAR`: - Always specify a length to any text-based data type such as `varchar`, `nvarchar`, `char`, `nchar`:
```sql ```tsql
/* Correct */ /* bad */
DECLARE @myGoodVarchareVariable VARCHAR(50); DECLARE @myBadVarcharVariable varchar;
DECLARE @myGoodNVarchareVariable NVARCHAR(90); DECLARE @myBadNVarcharVariable nvarchar;
DECLARE @myGoodCharVariable CHAR(7); DECLARE @myBadCharVariable char;
DECLARE @myGoodNCharVariable NCHAR(10); DECLARE @myBadNCharVariable nchar;
/* Not correct */ /* good */
DECLARE @myBadVarcharVariable VARCHAR; DECLARE @myGoodVarchareVariable varchar(50);
DECLARE @myBadNVarcharVariable NVARCHAR; DECLARE @myGoodNVarchareVariable nvarchar(90);
DECLARE @myBadCharVariable CHAR; DECLARE @myGoodCharVariable char(7);
DECLARE @myBadNCharVariable NCHAR; DECLARE @myGoodNCharVariable nchar(10);
``` ```
More details [here](https://www.red-gate.com/hub/product-learning/sql-prompt/using-a-variable-length-datatype-without-explicit-length-the-whys-and-wherefores). More details [here](https://www.red-gate.com/hub/product-learning/sql-prompt/using-a-variable-length-datatype-without-explicit-length-the-whys-and-wherefores).
- Use only [`ORIGINAL_LOGIN()`](https://docs.microsoft.com/en-us/sql/t-sql/functions/original-login-transact-sql) function because is the only function that consistently returns the actual login name that we started with regardless of impersonation.
More details [here](https://sqlstudies.com/2015/06/24/which-user-function-do-i-use/).
- Always use `IF` statement with `BEGIN-END` block to prevent errors with multi line statements:
```tsql
DECLARE @x int = 0;
DECLARE @y int = 1;
/* bad */
IF @y > @x
SET @x = @x + 1;
SET @y = @y - 1;
ELSE
PRINT(1);
/* Msg 156, Level 15, State 1, Line 8
Incorrect syntax near the keyword 'ELSE'. */
/* good */
IF @y > @x
BEGIN
SET @x = @x + 1;
SET @y = @y - 1;
END;
ELSE
BEGIN
PRINT(1);
END;
```
- `FROM, WHERE, INTO, JOIN, GROUP BY, ORDER BY` expressions should be aligned so, that all their arguments are placed under each other (see Example below) - `FROM, WHERE, INTO, JOIN, GROUP BY, ORDER BY` expressions should be aligned so, that all their arguments are placed under each other (see Example below)
Example: TSQL Example with formating:
```sql ```tsql
WITH CTE_MyCTE AS ( WITH CTE_MyCTE AS (
SELECT t1.Value1 AS Val1 SELECT
, t1.Value2 AS Val2 t1.Value1 AS Val1
, t2.Value3 AS Val3 , t1.Value2 AS Val2
, t2.Value3 AS Val3
INNER JOIN dbo.Table3 AS t2 INNER JOIN dbo.Table3 AS t2
ON t1.Value1 = t2.Value1 ON t1.Value1 = t2.Value1
WHERE t1.Value1 > 1 WHERE t1.Value1 > 1
AND t2.Value2 >= 101 AND t2.Value2 >= 101
) )
SELECT t1.Value1 AS Val1 SELECT
, t1.Value2 AS Val2 t1.Value1 AS Val1
, t2.Value3 AS Val3 , t1.Value2 AS Val2
INTO #Table3 , t2.Value3 AS Val3
FROM CTE_MyCTE AS t1 INTO #Table3
ORDER BY t2.Value2; FROM CTE_MyCTE AS t1
ORDER BY t2.Value2;
``` ```
**[⬆ back to top](#table-of-contents)** **[⬆ back to top](#table-of-contents)**
<a id="programming-style"></a>
### Stored procedures and functions programming style ### Stored procedures and functions programming style
<a id="programming-style"></a>
- All stored procedures and functions should use `ALTER` statement and start with the object presence check (see example below) - All stored procedures and functions should use `ALTER` statement and start with the object presence check (see example below)
- `ALTER` statement should be preceded by 2 line breaks - `ALTER` statement should be preceded by 2 line breaks
@@ -287,50 +406,55 @@ SELECT t1.Value1 AS Val1
- Create `sp_` procedures only in `master` database - SQL Server will always scan through the system catalog first - Create `sp_` procedures only in `master` database - SQL Server will always scan through the system catalog first
- Always use `BEGIN TRY` and `BEGIN CATCH` for error handling - Always use `BEGIN TRY` and `BEGIN CATCH` for error handling
- Always use multi-line comment `/* */` instead in-line comment `--` - Always use multi-line comment `/* */` instead in-line comment `--`
- Use `SET NOCOUNT ON;` for stops the message that shows the count of the number of rows affected by a Transact-SQL statement. - Use `SET NOCOUNT ON;` for stops the message that shows the count of the number of rows affected by a Transact-SQL statement and decreasing network traffic.
More details [here](https://www.red-gate.com/hub/product-learning/sql-prompt/finding-code-smells-using-sql-prompt-set-nocount-problem-pe008-pe009). More details [here](https://www.red-gate.com/hub/product-learning/sql-prompt/finding-code-smells-using-sql-prompt-set-nocount-problem-pe008-pe009).
- Do not use `SET NOCOUNT OFF;` (because it is default behavior) - Do not use `SET NOCOUNT OFF;` because it is default behavior
- Use `RAISERROR` instead `PRINT` if you want to give feedback about the state of the currently executing SQL batch without lags. - Use `RAISERROR` instead `PRINT` if you want to give feedback about the state of the currently executing SQL batch without lags.
More details [here](http://sqlity.net/en/984/print-vs-raiserror/) and [here](http://sqlservercode.blogspot.com/2019/01/print-disruptor-of-batch-deletes-in-sql.html). More details [here](http://sqlity.net/en/984/print-vs-raiserror/) and [here](http://sqlservercode.blogspot.com/2019/01/print-disruptor-of-batch-deletes-in-sql.html).
- All code should be self documenting - All code should be self documenting
- TSQL code, triggers, stored procedures, functions, should have a standard comment-documentation banner: - T-SQL code, triggers, stored procedures, functions, scripts, should have a standard comment-documentation banner:
```tsql ```
summary: > <documentation>
This procedure returns an object build script as a single-row, single column <summary>Get all databases meta data using dynamic T-SQL</summary>
result. <returns>1 data set: temp table #DatabaseInfo.</returns>
Revisions: <issues>No</issues>
- Author: Bill Gates <author>Konstantin Taranov</author>
Version: 1.1 <created>2018-03-01</created>
Modification: dealt properly with heaps <modified>2019-11-14 by Konstantin Taranov</modified>
date: 2017-07-15 <version>1.2</version>
- version: 1.2 <sourceLink>https://github.com/ktaranov/sqlserver-kit/blob/master/Scripts/Databases_Report.sql</sourceLink>
modification: Removed several bugs and got column-level constraints working </documentation>
date: 2017-06-30
example:
- code: udf_MyFunction 'testValsue';
returns: >
single row, single column result Build_Script.
``` ```
**[⬆ back to top](#table-of-contents)** **[⬆ back to top](#table-of-contents)**
Stored Procedure Example: Stored Procedure Example:
```sql ```tsql
IF OBJECT_ID('dbo.usp_StoredProcedure', 'P') IS NULL IF OBJECT_ID('dbo.usp_StoredProcedure', 'P') IS NULL
EXECUTE('CREATE PROCEDURE dbo.usp_StoredProcedure as SELECT 1'); EXECUTE('CREATE PROCEDURE dbo.usp_StoredProcedure as SELECT 1');
GO GO
ALTER PROCEDURE dbo.usp_StoredProcedure ( ALTER PROCEDURE dbo.usp_StoredProcedure(
@parameterValue1 SMALLINT @parameterValue1 smallint
, @parameterValue2 NVARCHAR(300) , @parameterValue2 nvarchar(300)
, @debug BIT = 0 , @debug bit = 0
) )
/* /*
EXECUTE dbo.usp_StoredProcedure <documentation>
<summary>Simple example of tsql procedure</summary>
<returns>nothing</returns>
<issues>No</issues>
<author>Konstantin Taranov</author>
<created>2019-01-01</created>
<modified>2019-11-25 by Konstantin Taranov</modified>
<version>1.2</version>
<sourceLink>-</sourceLink>
<example1>EXECUTE dbo.usp_StoredProcedure
@parameterValue1 = 0 @parameterValue1 = 0
, @parameterValue2 = N'BULK' , @parameterValue2 = N'BULK'</example1>
</documentation>
*/ */
AS AS
SET NOCOUNT ON; SET NOCOUNT ON;
@@ -338,20 +462,19 @@ SET NOCOUNT ON;
BEGIN TRY BEGIN TRY
IF (@parameterValue1 < 0 OR @parameterValue2 NOT IN ('SIMPLE', 'BULK', 'FULL')) IF (@parameterValue1 < 0 OR @parameterValue2 NOT IN ('SIMPLE', 'BULK', 'FULL'))
RAISERROR('Not valid data parameter!', 16, 1); RAISERROR('Not valid data parameter!', 16, 1);
PRINT @parameterValue2; IF (@debug) PRINT @parameterValue2;
END TRY END TRY
BEGIN CATCH BEGIN CATCH
-- Print error information. /* Print error information. */
PRINT 'Error: ' + CONVERT(varchar(50), ERROR_NUMBER()) + PRINT 'Error: ' + CAST(ERROR_NUMBER()) AS varchar(50)) +
', Severity: ' + CONVERT(varchar(5), ERROR_SEVERITY()) + ', Severity: ' + CAST(ERROR_SEVERITY(), varchar(5)) +
', State: ' + CONVERT(varchar(5), ERROR_STATE()) + ', State: ' + CAST(ERROR_STATE(), varchar(5) ) +
', Procedure: ' + ISNULL(ERROR_PROCEDURE(), '-') + ', Procedure: ' + COALESCE(ERROR_PROCEDURE(), '-') +
', Line: ' + CONVERT(varchar(5), ERROR_LINE()) + ', Line: ' + CAST(ERROR_LINE(), varchar(5)) +
', User name: ' + CONVERT(sysname, CURRENT_USER); ', User name: ' + CAST(ORIGINAL_LOGIN(), sysname);
PRINT ERROR_MESSAGE(); PRINT ERROR_MESSAGE();
END CATCH; END CATCH;
GO GO
``` ```
@@ -359,8 +482,115 @@ GO
**[⬆ back to top](#table-of-contents)** **[⬆ back to top](#table-of-contents)**
<a id="reference"></a> ### Dynamic T-SQL Recommendation
<a id="dynamic-t-sql-recommendation"></a>
**Highly recommended to read awesome detailed article about dynamic T-SQL by Erland Sommarskog: [The Curse and Blessings of Dynamic SQL](http://sommarskog.se/dynamic_sql.html)**
Dynamic SQL is a programming technique that allows you to construct SQL statements dynamically at runtime.
It allows you to create more general purpose and flexible SQL statement because the full text of the SQL statements may be unknown at compilation.
For example, you can use the dynamic SQL to create a stored procedure that queries data against a table whose name is not known until runtime.
More details [here](http://www.sqlservertutorial.net/sql-server-stored-procedures/sql-server-dynamic-sql/).
- Do not use [nvarchar(max)][6] for your objects name parameter, use [sysname](https://docs.microsoft.com/en-us/previous-versions/sql/sql-server-2008-r2/ms191240(v=sql.105)?redirectedfrom=MSDN) instead (synonym for nvarchar(128) except that, by default, sysname is NOT NULL).
```tsql
/* Bad */
DECLARE @tableName nvarchar(max) = N'MyTableName';
/* Good */
DECLARE @tableName sysname = N'MyTableName';
```
- Do quote the names of your objects properly.
```tsql
/* Bad */
DECLARE @tsql nvarchar(max);
DECLARE @tableName sysname = N'My badly named table!';
SET @tsql = N'SELECT object_id FROM ' + @tableName;
/* Good */
DECLARE @tsql nvarchar(max);
DECLARE @tableName sysname = N'My badly named table 111!';
SET @tsql = N'SELECT object_id FROM ' + QUOTENAME(@tableName);
```
- Always use [`sp_executesql`] instead [`EXEC`] to prevent sql injection.
Also [`sp_executesql`] can parameterizing your dynamic statement that means plans can be reused as well (when the value of the dynamic object is the same).
Also [`sp_executesql`] can even be used to output values as well (see example below).
```tsql
/* Bad EXEC example with sql injection*/
DECLARE @tsql nvarchar(max);
DECLARE @tableName sysname = N'master.sys.tables; SELECT * FROM master.sys.server_principals;';
SET @tsql = N'SELECT "name" FROM ' + @tableName + N';';
EXEC (@tsql);
/* Good sp_executesql example*/
DECLARE @tsql nvarchar(max);
DECLARE @tableName sysname = N'master.sys.tables';
DECLARE @id int = 2107154552;
SET @tsql = N'SELECT name FROM ' + @tableName +
N' WHERE object_id = ' + CONVERT(nvarchar(max), @id);
EXEC sp_executesql @tsql, N'@ID int', @ID = @id;
/* Good sp_executesql example with OUTPUT */
DECLARE @tsql nvarchar(max);
DECLARE @tableName sysname = N'master.sys.tables';
DECLARE @count bigint;
SET @tsql = N'SELECT @countOUT = COUNT(*) FROM ' + @tableName + N';';
EXEC sp_executesql @tsql, N'@countOUT bigint OUTPUT', @countOUT = @count OUTPUT;
PRINT('@count = ' + CASE WHEN @count IS NULL THEN 'NULL' ELSE CAST(@count AS varchar(30)) END);
```
- Do not use dynamic T-SQL if your statement is not dynamic.
```tsql
/* Bad */
DECLARE @tsql nvarchar(max);
DECLARE @id int = 2107154552;
SET @tsql = N'SELECT object_id, "name" FROM master.sys.tables WHERE object_id = ' + CAST(@id AS nvarchar(max));
EXEC sp_executesql @tsql;
/* Good */
DECLARE @id int = 2107154552;
SELECT object_id, "name" FROM master.sys.tables WHERE object_id = @id;
```
- Do not debug the code that creates the dynamic T-SQL first, debug the generated T-SQL statement instead.
Use `@debug` variable to print (or a `SELECT` statement if your dynamic T-SQL is over 4000 characters) dynamic statement instead executing it.
See example below.
- Do take the time to format your dynamic T-SQL.
```tsql
/* Bad @tsql formating */
DECLARE @tsql nvarchar(max);
DECLARE @sep nvarchar(30) = ' UNION ALL ';
DECLARE @debug bit = 1;
SELECT @tsql = COALESCE(@tsql, N'') +
N'SELECT N' + QUOTENAME(name,'''') +
N' AS DBName, (SELECT COUNT(*) FROM ' +
QUOTENAME(name) + N'.sys.tables) AS TableCount' +
@sep
FROM sys.databases
ORDER BY name;
SET @tsql = LEFT(@tsql, LEN(@tsql) - LEN(@sep));
IF @debug = 1 SELECT @tsql AS "tsql" ELSE EXEC sp_executesql @tsql;
/* Good @tsql formating */
DECLARE @tsql nvarchar(max);
DECLARE @sep nvarchar(30) = ' UNION ALL ';
DECLARE @debug bit = 1;
DECLARE @crlf nvarchar(10) = NCHAR(13) + NCHAR(10);
SELECT @tsql = COALESCE(@tsql, N'') + @crlf +
N'SELECT N' + QUOTENAME(name,'''') + N' AS DBName' + @crlf +
N' , (SELECT COUNT(*) FROM ' + QUOTENAME(name) + N'.sys.tables) AS TableCount' + @crlf +
@sep
FROM sys.databases
ORDER BY name;
SET @tsql = LEFT(@tsql, LEN(@tsql) - LEN(@sep)) + N';';
IF @debug = 1 SELECT @tsql AS "tsql" ELSE EXEC sp_executesql @tsql;
```
## Official Reference and useful links ## Official Reference and useful links
<a id="reference"></a>
- [Transact-SQL Formatting Standards](https://www.simple-talk.com/sql/t-sql-programming/transact-sql-formatting-standards-%28coding-styles%29/) (by Robert Sheldon) - [Transact-SQL Formatting Standards](https://www.simple-talk.com/sql/t-sql-programming/transact-sql-formatting-standards-%28coding-styles%29/) (by Robert Sheldon)
- [Subjectivity: Naming Standards](http://blogs.sqlsentry.com/aaronbertrand/subjectivity-naming-standards/) (by Aaron Bertrand) - [Subjectivity: Naming Standards](http://blogs.sqlsentry.com/aaronbertrand/subjectivity-naming-standards/) (by Aaron Bertrand)
- [General Database Conventions](http://kejser.org/database-naming-conventions/general-database-conventions/) (by Thomas Kejser) - [General Database Conventions](http://kejser.org/database-naming-conventions/general-database-conventions/) (by Thomas Kejser)
@@ -368,18 +598,17 @@ GO
- [SQL Style Guide](http://www.sqlstyle.guide/) (by Simon Holywell) - [SQL Style Guide](http://www.sqlstyle.guide/) (by Simon Holywell)
- [SQL Code Layout and Beautification](https://www.simple-talk.com/sql/t-sql-programming/sql-code-layout-and-beautification/) (by William Brewer) - [SQL Code Layout and Beautification](https://www.simple-talk.com/sql/t-sql-programming/sql-code-layout-and-beautification/) (by William Brewer)
- [TSQL Coding Style](http://www.databasejournal.com/features/mssql/tsql-coding-style.html) (by Gregory Larsen) - [TSQL Coding Style](http://www.databasejournal.com/features/mssql/tsql-coding-style.html) (by Gregory Larsen)
- [Database object Limitations](http://technet.microsoft.com/en-us/library/ms172451%28v=sql.110%29.aspx) - [User-Defined Functions](https://docs.microsoft.com/en-us/sql/relational-databases/user-defined-functions/user-defined-functions)
- [User-Defined Functions MSDN](http://msdn.microsoft.com/en-us/library/ms191007.aspx) - [Synonyms (Database Engine)](https://docs.microsoft.com/en-us/sql/relational-databases/synonyms/synonyms-database-engine)
- [Synonim TECHNET](http://technet.microsoft.com/en-us/library/ms187552(v=sql.110).aspx) - [Primary and Foreign Key Constraints](https://docs.microsoft.com/en-us/sql/relational-databases/tables/primary-and-foreign-key-constraints)
- [Primary and Foreign Key Constraints MSDN](http://msdn.microsoft.com/en-us/library/ms179610.aspx) - [sys.objects](https://docs.microsoft.com/en-us/sql/relational-databases/system-catalog-views/sys-objects-transact-sql)
- [sys.objects MSDN](http://msdn.microsoft.com/en-us/library/ms190324.aspx) - [SQL Server Constraints](https://docs.microsoft.com/en-us/sql/t-sql/statements/alter-table-table-constraint-transact-sql)
- [Constraints TECHNET](http://technet.microsoft.com/en-us/library/ms189862%28v=sql.105%29.aspx)
- [CHECK Constraint TECHNET](http://technet.microsoft.com/en-us/library/ms188258%28v=sql.105%29.aspx) - [CHECK Constraint TECHNET](http://technet.microsoft.com/en-us/library/ms188258%28v=sql.105%29.aspx)
- [SQL Server CLR Integration MSDN](http://msdn.microsoft.com/en-us/library/ms254498%28v=vs.110%29.aspx) - [SQL Server CLR Integration](https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/sql/introduction-to-sql-server-clr-integration)
- [CLR Databse Objects MSDN](http://msdn.microsoft.com/en-us/library/ms345099%28SQL.100%29.aspx) - [Deploying CLR Database Objects](https://docs.microsoft.com/en-us/sql/relational-databases/clr-integration/deploying-clr-database-objects)
- [CLR Stored Procedures](http://msdn.microsoft.com/en-us/library/ms131094%28v=sql.100%29.aspx) - [CLR Stored Procedures](https://docs.microsoft.com/en-us/sql/database-engine/dev-guide/clr-stored-procedures)
- [User-defined Functions](http://msdn.microsoft.com/en-us/library/ms191320.aspx) - [User-defined Functions](https://docs.microsoft.com/en-us/sql/relational-databases/user-defined-functions/create-user-defined-functions-database-engine)
- [MSDN SET NOCOUNT ON](https://docs.microsoft.com/en-us/sql/t-sql/statements/set-nocount-transact-sql) - [SET NOCOUNT ON (Transact-SQL)](https://docs.microsoft.com/en-us/sql/t-sql/statements/set-nocount-transact-sql)
- [T-SQL Coding Guidelines Presentation](http://www.slideshare.net/chris1adkin/t-sql-coding-guidelines) (by Chris Adkin) - [T-SQL Coding Guidelines Presentation](http://www.slideshare.net/chris1adkin/t-sql-coding-guidelines) (by Chris Adkin)
- [Sql Coding Style](http://c2.com/cgi/wiki?SqlCodingStyle) - [Sql Coding Style](http://c2.com/cgi/wiki?SqlCodingStyle)
- [SQL Server Code Review Checklist for Developers](https://www.sqlshack.com/sql-server-code-review-checklist-for-developers/) (by Samir Behara) - [SQL Server Code Review Checklist for Developers](https://www.sqlshack.com/sql-server-code-review-checklist-for-developers/) (by Samir Behara)
@@ -387,5 +616,14 @@ GO
- [In The Cloud: The Importance of Being Organized](http://sqlblog.com/blogs/john_paul_cook/archive/2017/05/16/in-the-cloud-the-importance-of-being-organized.aspx) - [In The Cloud: The Importance of Being Organized](http://sqlblog.com/blogs/john_paul_cook/archive/2017/05/16/in-the-cloud-the-importance-of-being-organized.aspx)
- [Naming Conventions in Azure](http://www.sqlchick.com/entries/2017/6/24/naming-conventions-in-azure) - [Naming Conventions in Azure](http://www.sqlchick.com/entries/2017/6/24/naming-conventions-in-azure)
- [The Basics of Good T-SQL Coding Style Part 3: Querying and Manipulating Data](https://www.simple-talk.com/sql/t-sql-programming/basics-good-t-sql-coding-style-part-3-querying-manipulating-data/) - [The Basics of Good T-SQL Coding Style Part 3: Querying and Manipulating Data](https://www.simple-talk.com/sql/t-sql-programming/basics-good-t-sql-coding-style-part-3-querying-manipulating-data/)
- [SQL naming conventions](https://www.red-gate.com/simple-talk/blogs/sql-naming-conventions/) (by Phi Factor)
- [SQL Server Compact Object Limitations](http://technet.microsoft.com/en-us/library/ms172451%28v=sql.110%29.aspx)
- [Dos and Don'ts of Dynamic SQL](https://www.sqlservercentral.com/articles/dos-and-donts-of-dynamic-sql) (by Thom Andrews)
**[⬆ back to top](#table-of-contents)** **[⬆ back to top](#table-of-contents)**
[`TOP`]:https://docs.microsoft.com/it-it/sql/t-sql/queries/top-transact-sql
[`FETCH-OFFSET`]:https://docs.microsoft.com/en-us/sql/t-sql/queries/select-order-by-clause-transact-sql
[`sp_executesql`]:https://docs.microsoft.com/en-us/sql/relational-databases/system-stored-procedures/sp-executesql-transact-sql
[`EXEC`]:https://docs.microsoft.com/en-us/sql/t-sql/language-elements/execute-transact-sql
[10]:https://docs.microsoft.com/en-us/sql/t-sql/functions/cast-and-convert-transact-sql