Skip to content

Commit 3e8a32f

Browse files
committed
Add Dynamic T-SQL recommendation
1 parent baca2a7 commit 3e8a32f

File tree

1 file changed

+143
-15
lines changed

1 file changed

+143
-15
lines changed

SQL Server Name Convention and T-SQL Programming Style.md

Lines changed: 143 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,14 @@ Reasons for using a naming convention (as opposed to allowing programmers to cho
1414
- [SQL Server Object Name Convention](#sql-server-object-name-convention)
1515
- [SQL Server Data Types Recommendation](#data-types-recommendation)
1616
- [T-SQL Programming Style](#t-sql-programming-style)
17-
- [General programming style](#general-programming-style)
17+
- [General T-SQL programming style](#general-t-sql-programming-style)
1818
- [Stored procedures and functions programming style](#programming-style)
19+
- [Dynamic T-SQL Recommendation](#dynamic-t-sql-recommendation)
1920
- [Reference and useful links](#reference)
2021

2122

22-
<a id="sql-server-object-name-convention"></a>
2323
## SQL Server Object Name Convention
24+
<a id="sql-server-object-name-convention"></a>
2425

2526
| Object | Code | Notation | Length | Plural | Prefix | Suffix | Abbreviation | Char Mask | Example |
2627
|------------------------------------------|------| ---------- |-------:|--------|--------|--------|--------------|--------------|--------------------------------------|
@@ -33,7 +34,9 @@ Reasons for using a naming convention (as opposed to allowing programmers to cho
3334
| [Memory-optimized SCHEMA_ONLY Table] | | PascalCase | 128 | No | MT_ | _SO | Yes | [A-z][0-9] | `MT_MyTable_SO` |
3435
| [Temporal Table] | | PascalCase | 128 | No | No | _TT | Yes | [A-z][0-9] | `MyTable_TT` |
3536
| [Disk-Based Table] | U | PascalCase | 128 | No | No | No | Yes | [A-z][0-9] | `MyTable` |
36-
| Table Column | | PascalCase | 128 | No | No | No | Yes | [A-z][0-9] | `MyColumn` |
37+
| [Disk-Based Wide Table - SPARSE Column] | U | PascalCase | 128 | No | No | _SPR | Yes | [A-z][0-9] | `MyTable_SPR` |
38+
| [Table Column] | | PascalCase | 128 | No | No | No | Yes | [A-z][0-9] | `MyColumn` |
39+
| [Table Column SPARSE] | | PascalCase | 128 | No | No | _SPR | Yes | [A-z][0-9] | `MyColumn_SPR` |
3740
| Table Default Values | D | PascalCase | 128 | No | DF_ | No | Yes | [A-z][0-9] | `DF_MyTable_MyColumn` |
3841
| Table Check Column Constraint | C | PascalCase | 128 | No | CK_ | No | Yes | [A-z][0-9] | `CK_MyTable_MyColumn` |
3942
| Table Check Table Constraint | C | PascalCase | 128 | No | CTK_ | No | Yes | [A-z][0-9] | `CTK_MyTable_MyColumn_AnotherColumn` |
@@ -45,12 +48,13 @@ Reasons for using a naming convention (as opposed to allowing programmers to cho
4548
| [DDL Trigger] | TR | PascalCase | 128 | No | TR_ | _DDL | Yes | [A-z][0-9] | `TR_LogicalName_DDL` |
4649
| [DML Trigger] | TR | PascalCase | 128 | No | TR_ | _DML | Yes | [A-z][0-9] | `TR_MyTable_LogicalName_DML` |
4750
| [Logon Trigger] | TR | PascalCase | 128 | No | TR_ | _LOG | Yes | [A-z][0-9] | `TR_LogicalName_LOG` |
48-
| View | V | PascalCase | 128 | No | VI_ | No | No | [A-z][0-9] | `VI_LogicalName` |
49-
| Stored Procedure | P | PascalCase | 128 | No | usp_ | No | No | [A-z][0-9] | `usp_LogicalName` |
50-
| Scalar User-Defined Function | FN | PascalCase | 128 | No | udf_ | No | No | [A-z][0-9] | `udf_FunctionLogicalName` |
51-
| Table-Valued Function | FN | PascalCase | 128 | No | tvf_ | No | No | [A-z][0-9] | `tvf_FunctionLogicalName` |
52-
| Synonym | SN | camelCase | 128 | No | sy_ | No | No | [A-z][0-9] | `sy_logicalName` |
53-
| Sequence | SO | PascalCase | 128 | No | sq_ | No | No | [A-z][0-9] | `sq_TableName` |
51+
| [View] | V | PascalCase | 128 | No | VI_ | No | No | [A-z][0-9] | `VI_LogicalName` |
52+
| [Indexed View] | V | PascalCase | 128 | No | VIX_ | No | No | [A-z][0-9] | `VIx_LogicalName` |
53+
| [Stored Procedure] | P | PascalCase | 128 | No | usp_ | No | No | [A-z][0-9] | `usp_LogicalName` |
54+
| [Scalar User-Defined Function] | FN | PascalCase | 128 | No | udf_ | No | No | [A-z][0-9] | `udf_FunctionLogicalName` |
55+
| [Table-Valued Function] | FN | PascalCase | 128 | No | tvf_ | No | No | [A-z][0-9] | `tvf_FunctionLogicalName` |
56+
| [Synonym] | SN | camelCase | 128 | No | sy_ | No | No | [A-z][0-9] | `sy_logicalName` |
57+
| [Sequence] | SO | PascalCase | 128 | No | sq_ | No | No | [A-z][0-9] | `sq_TableName` |
5458
| CLR Assembly | | PascalCase | 128 | No | CA | No | Yes | [A-z][0-9] | `CALogicalName` |
5559
| CLR Stored Procedures | PC | PascalCase | 128 | No | pc_ | No | Yes | [A-z][0-9] | `pc_CAName_LogicalName` |
5660
| CLR Scalar User-Defined Function | | PascalCase | 128 | No | cudf_ | No | No | [A-z][0-9] | `cudf_CAName_LogicalName` |
@@ -68,17 +72,27 @@ Reasons for using a naming convention (as opposed to allowing programmers to cho
6872
[Memory-optimized SCHEMA_ONLY Table]:https://docs.microsoft.com/en-us/sql/relational-databases/in-memory-oltp/defining-durability-for-memory-optimized-objects
6973
[Temporal Table]:https://docs.microsoft.com/en-us/sql/relational-databases/tables/temporal-tables
7074
[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
75+
[Disk-Based Wide Table - SPARSE Column]:https://docs.microsoft.com/en-us/sql/relational-databases/tables/tables#wide-tables
76+
[Table Column]:https://docs.microsoft.com/en-us/sql/t-sql/statements/alter-table-transact-sql
77+
[Table Column SPARSE]:https://docs.microsoft.com/en-us/sql/relational-databases/tables/use-sparse-columns
7178

7279
[DDL Trigger]:https://docs.microsoft.com/en-us/sql/t-sql/statements/create-trigger-transact-sql
7380
[DML Trigger]:https://docs.microsoft.com/en-us/sql/relational-databases/triggers/dml-triggers
7481
[Logon Trigger]:https://docs.microsoft.com/en-us/sql/t-sql/statements/create-trigger-transact-sql
82+
[View]:https://docs.microsoft.com/en-us/sql/relational-databases/views/views
83+
[Indexed View]:https://docs.microsoft.com/en-us/sql/relational-databases/views/create-indexed-views
84+
[Stored Procedure]:https://docs.microsoft.com/en-us/sql/t-sql/statements/create-procedure-transact-sql
85+
[Scalar User-Defined Function]:https://docs.microsoft.com/en-us/sql/relational-databases/user-defined-functions/create-user-defined-functions-database-engine#Scalar
86+
[Table-Valued Function]:https://docs.microsoft.com/en-us/sql/relational-databases/user-defined-functions/create-user-defined-functions-database-engine#TVF
87+
[Synonym]:https://docs.microsoft.com/en-us/sql/relational-databases/synonyms/synonyms-database-engine
88+
[Sequence]:https://docs.microsoft.com/en-us/sql/relational-databases/sequence-numbers/sequence-numbers
7589

7690
**[⬆ back to top](#table-of-contents)**
7791

7892

79-
<a id="data-types-recommendation"></a>
8093
## SQL Server Data Types Recommendation
81-
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)
94+
<a id="data-types-recommendation"></a>
95+
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)
8296

8397
| General Type | Type | ANSI | Recommended | What use instead | Why use or not |
8498
|----------------------|---------------------|------|----------------|--------------------|-----------------------------------------------------------|
@@ -156,10 +170,12 @@ More details about SQL Server data types and mapping it with another databases y
156170

157171

158172
## T-SQL Programming Style
159-
SQL Server T-SQL Coding Conventions, Best Practices, and Programming Guidelines
173+
<a id="t-sql-programming-style"></a>
174+
SQL Server T-SQL Coding Conventions, Best Practices, and Programming Guidelines.
160175

161176

162177
### General programming style
178+
<a id="#general-t-sql-programming-style"></a>
163179

164180
- 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`.
165181
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/),
@@ -309,8 +325,8 @@ ORDER BY t2.Value2;
309325
**[⬆ back to top](#table-of-contents)**
310326

311327

312-
<a id="programming-style"></a>
313328
### Stored procedures and functions programming style
329+
<a id="programming-style"></a>
314330

315331
- All stored procedures and functions should use `ALTER` statement and start with the object presence check (see example below)
316332
- `ALTER` statement should be preceded by 2 line breaks
@@ -327,7 +343,7 @@ ORDER BY t2.Value2;
327343
- Use `RAISERROR` instead `PRINT` if you want to give feedback about the state of the currently executing SQL batch without lags.
328344
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).
329345
- All code should be self documenting
330-
- TSQL code, triggers, stored procedures, functions, should have a standard comment-documentation banner:
346+
- T-SQL code, triggers, stored procedures, functions, should have a standard comment-documentation banner:
331347
```tsql
332348
summary: >
333349
This procedure returns an object build script as a single-row, single column
@@ -393,8 +409,116 @@ GO
393409
**[⬆ back to top](#table-of-contents)**
394410

395411

396-
<a id="reference"></a>
412+
### Dynamic T-SQL Recommendation
413+
<a id="dynamic-t-sql-recommendation"></a>
414+
**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)**
415+
416+
Dynamic SQL is a programming technique that allows you to construct SQL statements dynamically at runtime.
417+
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.
418+
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.
419+
420+
More details [here](http://www.sqlservertutorial.net/sql-server-stored-procedures/sql-server-dynamic-sql/).
421+
422+
- Do not use [nvarchar(max)] for your object’s name parameter, use [sysname] instead (synonym for nvarchar(128)).
423+
```tsql
424+
/* Bad */
425+
DECLARE @tableName nvarchar(max) = N'MyTableName';
426+
427+
/* Good */
428+
DECLARE @tableName sysname = N'MyTableName';
429+
```
430+
- Do quote the names of your objects properly.
431+
```tsql
432+
/* Bad */
433+
DECLARE @tsql nvarchar(max);
434+
DECLARE @tableName sysname = N'My badly named table!';
435+
SET @tsql = N'SELECT object_id FROM ' + @tableName;
436+
437+
/* Good */
438+
DECLARE @tsql nvarchar(max);
439+
DECLARE @tableName sysname = N'My badly named table 111!';
440+
SET @tsql = N'SELECT object_id FROM ' + QUOTENAME(@tableName);
441+
```
442+
- Always use [`sp_executesql`] instead [`EXEC`] to prevent sql injection.
443+
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).
444+
Also [`sp_executesql`] can even be used to output values as well (see example below).
445+
```tsql
446+
/* Bad EXEC example*/
447+
DECLARE @tsql nvarchar(max);
448+
DECLARE @tableName sysname = N'master.sys.tables';
449+
DECLARE @id int = 2107154552;
450+
SET @tsql = N'SELECT "name" FROM ' + @tableName +
451+
N' WHERE object_id = ' + CONVERT(nvarchar(max), @id);
452+
EXEC (@tsql);
453+
454+
/* Good sp_executesql example*/
455+
DECLARE @tsql nvarchar(max);
456+
DECLARE @tableName sysname = N'master.sys.tables';
457+
DECLARE @id int = 2107154552;
458+
SET @tsql = N'SELECT name FROM ' + @tableName +
459+
N' WHERE object_id = ' + CONVERT(nvarchar(max), @id);
460+
EXEC sp_executesql @tsql, N'@ID int', @ID = @id;
461+
462+
/* Good sp_executesql example with OUTPUT */
463+
DECLARE @tsql nvarchar(max);
464+
DECLARE @tableName sysname = N'master.sys.tables';
465+
DECLARE @count bigint;
466+
SET @tsql = N'SELECT @countOUT = COUNT(*) FROM ' + @tableName + N';';
467+
EXEC sp_executesql @tsql, N'@countOUT bigint OUTPUT', @countOUT = @count OUTPUT;
468+
PRINT('@count = ' + CASE WHEN @count IS NULL THEN 'NULL' ELSE CAST(@count AS varchar(30)) END);
469+
```
470+
- Do not use dynamic T-SQL if your statement is not dynamic.
471+
```tsql
472+
/* Bad */
473+
DECLARE @tsql nvarchar(max);
474+
DECLARE @id int = 2107154552;
475+
SET @tsql = N'SELECT object_id, "name" FROM master.sys.tables WHERE object_id = ' + CONVERT(nvarchar(max), @id);
476+
EXEC sp_executesql @tsql;
477+
478+
/* Good */
479+
DECLARE @id int = 2107154552;
480+
SELECT object_id, "name" FROM master.sys.tables WHERE object_id = @id;
481+
```
482+
- Do not debug the code that creates the dynamic T-SQL first, debug the generated T-SQL statement instead.
483+
Use `@debug` variable to print (or a `SELECT` statement if your dynamic T-SQL is over 4000 characters) dynamic statement instead executing it.
484+
- Do take the time to format your dynamic T-SQL.
485+
```tsql
486+
/* Bad @tsql formating */
487+
DECLARE @tsql nvarchar(max);
488+
DECLARE @sep nvarchar(30) = ' UNION ALL ';
489+
DECLARE @debug bit = 1;
490+
491+
SELECT @tsql = COALESCE(@tsql, N'') +
492+
N'SELECT N' + QUOTENAME(name,'''') +
493+
N' AS DBName, (SELECT COUNT(*) FROM ' +
494+
QUOTENAME(name) + N'.sys.tables) AS TableCount' +
495+
@sep
496+
FROM sys.databases
497+
ORDER BY name;
498+
499+
SET @tsql = LEFT(@tsql, LEN(@tsql) - LEN(@sep));
500+
IF @debug = 1 SELECT @tsql AS "tsql" ELSE EXEC sp_executesql @tsql;
501+
502+
/* Good @tsql formating */
503+
DECLARE @tsql nvarchar(max);
504+
DECLARE @sep nvarchar(30) = ' UNION ALL ';
505+
DECLARE @debug bit = 1;
506+
DECLARE @crlf nvarchar(10) = NCHAR(13) + NCHAR(10);
507+
508+
SELECT @tsql = COALESCE(@tsql, N'') + @crlf +
509+
N'SELECT N' + QUOTENAME(name,'''') + N' AS DBName' + @crlf +
510+
N' , (SELECT COUNT(*) FROM ' + QUOTENAME(name) + N'.sys.tables) AS TableCount' + @crlf +
511+
@sep
512+
FROM sys.databases
513+
ORDER BY name;
514+
515+
SET @tsql = LEFT(@tsql, LEN(@tsql) - LEN(@sep)) + N';';
516+
IF @debug = 1 SELECT @tsql AS "tsql" ELSE EXEC sp_executesql @tsql;
517+
```
518+
519+
397520
## Official Reference and useful links
521+
<a id="reference"></a>
398522
- [Transact-SQL Formatting Standards](https://www.simple-talk.com/sql/t-sql-programming/transact-sql-formatting-standards-%28coding-styles%29/) (by Robert Sheldon)
399523
- [Subjectivity: Naming Standards](http://blogs.sqlsentry.com/aaronbertrand/subjectivity-naming-standards/) (by Aaron Bertrand)
400524
- [General Database Conventions](http://kejser.org/database-naming-conventions/general-database-conventions/) (by Thomas Kejser)
@@ -422,5 +546,9 @@ GO
422546
- [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/)
423547
- [SQL naming conventions](https://www.red-gate.com/simple-talk/blogs/sql-naming-conventions/) (by Phi Factor)
424548
- [SQL Server Compact Object Limitations](http://technet.microsoft.com/en-us/library/ms172451%28v=sql.110%29.aspx)
549+
- [Dos and Don'ts of Dynamic SQL](https://www.sqlservercentral.com/articles/dos-and-donts-of-dynamic-sql) (by Thom Andrews)
425550

426551
**[⬆ back to top](#table-of-contents)**
552+
553+
[`sp_executesql`]:https://docs.microsoft.com/en-us/sql/relational-databases/system-stored-procedures/sp-executesql-transact-sql
554+
[`EXEC`]:https://docs.microsoft.com/en-us/sql/t-sql/language-elements/execute-transact-sql

0 commit comments

Comments
 (0)