{"id":71526,"date":"2017-06-27T16:52:52","date_gmt":"2017-06-27T16:52:52","guid":{"rendered":"https:\/\/www.simple-talk.com\/?p=71526"},"modified":"2021-09-29T16:21:12","modified_gmt":"2021-09-29T16:21:12","slug":"basics-good-t-sql-coding-style-part-3-querying-manipulating-data","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/t-sql-programming-sql-server\/basics-good-t-sql-coding-style-part-3-querying-manipulating-data\/","title":{"rendered":"The Basics of Good T-SQL Coding Style &#8211; Part 3: Querying and Manipulating Data"},"content":{"rendered":"<h4>The series so far:<\/h4>\n<ol>\n<li><a href=\"https:\/\/www.simple-talk.com\/sql\/t-sql-programming\/basics-good-t-sql-coding-style\/\">The Basics of Good T-SQL Coding Style<\/a><\/li>\n<li><a href=\"https:\/\/www.simple-talk.com\/sql\/t-sql-programming\/basics-good-t-sql-coding-style-part-2-defining-database-objects\/\">The Basics of Good T-SQL Coding Style \u2013 Part 2: Defining Database Objects<\/a><\/li>\n<li><a href=\"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 \u2013 Part 3: Querying and Manipulating Data<\/a><\/li>\n<li><a href=\"https:\/\/www.red-gate.com\/simple-talk\/sql\/t-sql-programming\/basics-good-t-sql-coding-style-part-4-performance\/\">The Basics of Good T-SQL Coding Style \u2013 Part 4: Performance<\/a><\/li>\n<\/ol>\n\n<p>If you\u2019ve been around SQL Server for a while, you know that writing T-SQL code is nothing to treat lightly. You must take into account a wide range of considerations to ensure that the code is both accurate and consistent and that it does not break applications or compromise security.<\/p>\n<p>To help with this process, many teams create a set of coding standards that outline acceptable styles and usage, while providing a set of guidelines for addressing specific issues. Such standards not only help to streamline the development process, but also to ease the burden of updating, reviewing, and troubleshooting code, processes that in their own right can represent significant effort.<\/p>\n<p>Unfortunately, it is no small task to develop a comprehensive set of T-SQL standards. The more you dig into the various issues, the more you uncover, resulting in an effort that is often more unwieldy and time-consuming than you had bargained for.<\/p>\n<p>This series seeks to help tame the coding beast by covering many of the issues you should take into account when developing your own standards. The first article focuses on general coding practices that can apply to any type of T-SQL code, and the second article centers on the code used to define database objects. In this article, we cover coding issues related to querying and modifying SQL Server data.<\/p>\n<p>Throughout the article, I provide examples that demonstrate some of the pitfalls developers can run into when working with T-SQL code. The examples are based on the following schema and tables, which I created on a local instance of SQL Server 2016:<\/p>\n<pre class=\"theme:ssms2012-simple-talk lang:tsql decode:true\">CREATE SCHEMA Inventory;\r\n  GO\r\n  CREATE TABLE Inventory.ProductCategories(\r\n  \u00a0 ProductCategoryID INT NOT NULL PRIMARY KEY,\r\n  \u00a0 ProductCategoryName NVARCHAR(50) NOT NULL);\r\n  CREATE TABLE Inventory.Products(\r\n  \u00a0 ProductID INT NOT NULL PRIMARY KEY,\r\n  \u00a0 ProductName NVARCHAR(100) NOT NULL,\r\n  \u00a0 ProductCategory INT NULL\r\n  \u00a0 \u00a0 REFERENCES Inventory.ProductCategories(ProductCategoryID));<\/pre>\n<p>You can create the tables in any database that\u2019s convenient for you. The operations we perform are fairly basic and require few resources (although it\u2019s always best to stay away from production servers). With that in mind, let\u2019s get started.<\/p>\n<h2>Referencing database objects<\/h2>\n<p>We\u2019ll begin by populating the <strong>Products<\/strong> and <strong>ProductCategories<\/strong> tables, using the following two <strong>INSERT<\/strong> statements:<\/p>\n<pre class=\"theme:ssms2012-simple-talk lang:tsql decode:true\">INSERT INTO Inventory.ProductCategories\r\n  VALUES(1, 'Category 1'), (2, 'Category 2'), (3, 'Category 3');\r\n  INSERT INTO Inventory.Products\r\n  VALUES(101, 'Product 101', 1), (102, 'Product 102', 2), (103, 'Product 103', 3);<\/pre>\n<p>The <strong>INSERT<\/strong> statements already point to our first issue. When using these statements, you should specify the column names, even if you\u2019re inserting data into all columns. Not only does this make it easier to verify that the data is targeting the correct columns, but it also ensures that statements are more resilient to changes to the table definition. For instance, if a column were added to the <strong>Products<\/strong> table, the second <strong>INSERT<\/strong> statement would fail.<\/p>\n<p>We need to be just as specific when querying data, making sure all object references are complete. As an example, consider the following <strong>SELECT<\/strong> statement:<\/p>\n<pre class=\"theme:ssms2012-simple-talk lang:tsql decode:true\">SELECT * FROM Products;<\/pre>\n<p>Notice that the statement provides no schema when referencing the table name. Unless the table resides in the default schema, the database engine will return an error stating that <strong>Products<\/strong> is an invalid object. You should include the schema whether or not the table (or other object) resides in the default schema, and you should do so in any statement that that references the object, whether a <strong>SELECT<\/strong>, <strong>DELETE<\/strong>, <strong>UPDATE<\/strong>, or other type of statement.<\/p>\n<p>In addition, if you\u2019re referencing an object in a remote database, you must provide the fully qualified name, which includes the server and database, along with the schema and object. For example, the following fully qualified name references the <strong>Products<\/strong> table in the <strong>Inventory<\/strong> schema, which is in the <strong>StoreDB<\/strong> database running on the <strong>Server01<\/strong> SQL Server instance:<\/p>\n<pre class=\"theme:ssms2012-simple-talk lang:tsql decode:true\">Server01.StoreDB.Inventory.Products<\/pre>\n<p>Another issue with the above <strong>SELECT<\/strong> statement is the use of the asterisk wildcard in the select list. Because the wildcard represents all columns, it is tempting to use it when you want to return every column in a table or view. However, table and view definitions can change and you can suddenly find yourself with broken applications. Except for the occasional ad hoc query, you should avoid using a wildcard in this way and specify each column in the select list.<\/p>\n<p>Now let\u2019s look at a <strong>SELECT<\/strong> statement that raises several other coding issues:<\/p>\n<pre class=\"theme:ssms2012-simple-talk lang:tsql decode:true\">SELECT ProductID, ProductName, ProductCategoryName\r\n  FROM Inventory.Products INNER JOIN Inventory.ProductCategories\r\n  \u00a0 ON ProductCategory = ProductCategoryID\r\n  ORDER BY 1 DESC;<\/pre>\n<p>This time we\u2019re joining the <strong>Products<\/strong> and <strong>ProductCategories<\/strong> tables, returning values from three columns in those tables, and sorting the results by the <strong>ProductID<\/strong> column, which is represented by a column ID of <strong>1<\/strong>.<\/p>\n<p>When querying multiple tables in this way, you should assign an alias to each table to make it easier to reference that table in other parts of the code. You should then use those aliases to qualify any column references, something we fail to do here. We can get away with not qualifying the column names because no duplicate names exist between the two tables, but that is not always the case. And even if there are no duplicate columns, you should still qualify the names so it is clear where the data is coming from. This makes the code more readable and easier to troubleshoot.<\/p>\n<p>Another issue with the <strong>SELECT<\/strong> statement is the use of a column number in the <strong>ORDER BY<\/strong> clause, rather than the column name. This can cause confusion and lead to errors. For example, someone might change the order of the columns in the select list without accounting for the numbers used in the <strong>ORDER BY <\/strong>clause.<\/p>\n<h2>Accessing SQL Server data<\/h2>\n<p>When developing or reviewing T-SQL code that manipulates data, you can encounter a variety of issues related to stylistic consistency, code accuracy, and performance. In the next article, we\u2019ll cover performance-related issues. Our focus here is on style and accuracy, but before we get started, we\u2019ll add more sample data to the <strong>Products<\/strong> table:<\/p>\n<pre class=\"theme:ssms2012-simple-talk lang:tsql decode:true\">INSERT INTO Inventory.Products(ProductID, ProductName, ProductCategory)\r\n  VALUES(104, 'Product 104', 1), (105, 'Product 105', 2), (106, 'Product 106', NULL),\r\n  \u00a0 (107, 'Product 107', 1), (108, 'Product 108', NULL), (109, 'Product 109', 3);<\/pre>\n<p>After adding the data, let\u2019s create a <strong>SELECT<\/strong> statement that joins the <strong>Products<\/strong> and <strong>ProductCategories<\/strong> tables:<\/p>\n<pre class=\"theme:ssms2012-simple-talk lang:tsql decode:true\">SELECT TOP(6) p.ProductID, p.ProductName,\u00a0\r\n  \u00a0 CASE pc.ProductCategoryName\r\n  \u00a0 \u00a0 WHEN 'Category 1' THEN 'Cat1'\r\n  \u00a0 \u00a0 WHEN 'Category 2' THEN 'Cat2'\r\n  \u00a0 \u00a0 WHEN 'Category 3' THEN 'Cat3'\r\n  \u00a0 END AS ProductCategory,\r\n  \u00a0 p.ProductName + '_' + p.ProductID AS ProductAltID\u00a0 \u00a0\r\n  FROM Inventory.Products p WITH(NOLOCK)\r\n  \u00a0 INNER JOIN Inventory.ProductCategories pc WITH(NOLOCK)\r\n  \u00a0 ON p.ProductCategory = pc.ProductCategoryID;<\/pre>\n<p>Unfortunately, if we try to run this statement, we\u2019ll receive the following conversion error:<\/p>\n<pre class=\"theme:ssms2012-simple-talk lang:tsql decode:true\">Conversion failed when converting the varchar value 'Product 101_' to data type int.<\/pre>\n<p>The problem is that we\u2019re trying to concatenate the underscore and <strong>ProductName<\/strong> column, defined with the <strong>NVARCHAR<\/strong> data type, with the <strong>ProductID<\/strong> column, defined with an <strong>INT<\/strong> data type. Because we start with the string values, you might assume that the database engine will implicitly convert the numeric value to a string. However, the <strong>INT<\/strong> data type takes precedence of the <strong>NVARCHAR<\/strong> data type, so the database engine instead tries to convert the string to a numeric value, which results in an error.<\/p>\n<p>Data type precedence is only one of many issues you can run into when converting data, which is one reason it\u2019s a good idea to explicitly convert data. You can find more information about data-conversion issues in my article <a href=\"https:\/\/www.simple-talk.com\/sql\/t-sql-programming\/how-to-get-sql-server-data-conversion-horribly-wrong\/\">How to Get SQL Server Data-Conversion Horribly Wrong<\/a>.<\/p>\n<p>The above <strong>SELECT<\/strong> statement also raises a number of other issues. For example, it includes the <strong>TOP<\/strong> operator in the select list, but does not include an <strong>ORDER<\/strong> <strong>BY<\/strong> clause, making the results more unpredictable.<\/p>\n<p>The statement also contains a <strong>CASE<\/strong> expression that does not include an <strong>ELSE<\/strong> block. Although the <strong>ELSE<\/strong> block is not required, it is often a good idea to include one to handle unexpected values. At the very least, you should keep this issue in mind when reviewing your code.<\/p>\n<p>Finally, the <strong>SELECT<\/strong> statement uses the <strong>NOLOCK<\/strong> table hint when referencing each table. The table hint is equivalent to the <strong>READ<\/strong> <strong>UNCOMMITTED<\/strong> isolation level, which allows a statement to read rows that have been modified by other transactions but not yet committed. Developers will sometimes use <strong>NOLOCK<\/strong> or <strong>READ<\/strong> <strong>UNCOMMITTED<\/strong> to improve query performance, but this can result in dirty reads, so you need to be cautious when using either one.<\/p>\n<p>That said, if you are okay with risking dirty reads, <strong>READ<\/strong> <strong>UNCOMMITTED<\/strong> is generally considered the better option of the two because it provides more precise control over your isolation levels.<\/p>\n<p>You also want to be careful about your use of subqueries. Although most issues with subqueries are related to performance (which we\u2019ll cover in the next article), subqueries can also present other issues. For example, the following <strong>SELECT<\/strong> statement uses a subquery in the <strong>WHERE<\/strong> clause, along with the <strong>NOT<\/strong> <strong>IN<\/strong> operator:<\/p>\n<pre class=\"theme:ssms2012-simple-talk lang:tsql decode:true\">SELECT ProductID, ProductName, ProductCategory\r\n  FROM Inventory.Products\r\n  WHERE ProductCategory NOT IN (SELECT ProductCategory\u00a0\r\n  \u00a0 FROM Inventory.Products);<\/pre>\n<p>You need to be cautious when using the <strong>IN<\/strong> or <strong>NOT<\/strong> <strong>IN<\/strong> operator if the subquery\u2019s source data contains <strong>NULL<\/strong> values. In this case, the subquery itself returns both numeric and <strong>NULL<\/strong> values, but the outer <strong>SELECT<\/strong> statement returns an empty data set. To get the data you need, you should consider using a <strong>NOT<\/strong> <strong>EXISTS<\/strong> operator instead of <strong>NOT<\/strong> <strong>IN<\/strong> or recast the statement as a left outer join.<\/p>\n<p>You should also ensure your subquery is written correctly and returns the right data. For example, the subquery in the following <strong>SELECT<\/strong> statement can return more than one value and includes an <strong>ORDER<\/strong> <strong>BY<\/strong> clause:<\/p>\n<pre class=\"theme:ssms2012-simple-talk lang:tsql decode:true\">SELECT ProductID, ProductName, ProductCategory\r\n  FROM Inventory.Products\r\n  WHERE ProductCategory = (SELECT ProductCategory\u00a0\r\n  \u00a0 FROM Inventory.Products ORDER BY ProductCategory);<\/pre>\n<p>If a subquery is supposed to return a scalar value, you better be sure that\u2019s what it will always do. In addition, be sure not to include an <strong>ORDER<\/strong> <strong>BY<\/strong> clause in a subquery unless it also includes the <strong>TOP<\/strong> operator in the select list.<\/p>\n<p>There are, of course, numerous other concerns to be aware of when working with subqueries. For more information, see my article <a href=\"https:\/\/www.simple-talk.com\/sql\/sql-training\/subqueries-in-sql-server\/\">Subqueries in SQL Server<\/a>.<\/p>\n<h2>Filtering data<\/h2>\n<p>In the previous two <strong>SELECT<\/strong> statements, we used the subqueries in the <strong>WHERE<\/strong> clause as part of filtering the data. Although these examples focused on the use of subqueries, there are a number of other issues you can run into when filtering data.<\/p>\n<p>For example, you have to be careful when using logical operators to define multiple conditions in your <strong>WHERE<\/strong> clause, as in the following <strong>SELECT<\/strong> statement:<\/p>\n<pre class=\"theme:ssms2012-simple-talk lang:tsql decode:true\">SELECT ProductID, ProductName, ProductCategory\r\n  FROM Inventory.Products\r\n  WHERE ProductID &lt; 103 OR ProductID &gt; 107 OR ProductID = 106\u00a0\r\n  \u00a0 AND ProductCategory IS NOT NULL;<\/pre>\n<p>The <strong>SELECT<\/strong> statement uses both the <strong>OR<\/strong> and the <strong>AND<\/strong> logical operators, giving us the following results:<\/p>\n<table class=\"table--tight\">\n<thead>\n<tr>\n<td>ProductID<\/td>\n<td>ProductName<\/td>\n<td>ProductCategory<\/td>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>101<\/td>\n<td>Product 101<\/td>\n<td>1<\/td>\n<\/tr>\n<tr>\n<td>102<\/td>\n<td>Product 102<\/td>\n<td>2<\/td>\n<\/tr>\n<tr>\n<td>108<\/td>\n<td>Product 108<\/td>\n<td>NULL<\/td>\n<\/tr>\n<tr>\n<td>109<\/td>\n<td>Product 109<\/td>\n<td>3<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>In this case, we\u2019ve simply specified the various operators and expressions without trying to control the logic, essentially taking any results the database engine wants to feed us. In this case, the database engine returns products that meet either of the first two conditions <em>or<\/em> products that have a <strong>ProductID<\/strong> value of <strong>106<\/strong> <em>and<\/em> a <strong>ProductCategory<\/strong> value that is not <strong>NULL<\/strong>.<\/p>\n<p>When we mix logical operators, we must carefully control how the conditions are applied. What we\u2019re really after here is to return the products that meet any of the first three conditions <em>and<\/em> eliminate any rows with a <strong>ProductCategory<\/strong> value of <strong>NULL<\/strong>. To do so, we can enclose the first three expressions in parentheses:<\/p>\n<pre class=\"theme:ssms2012-simple-talk lang:tsql decode:true\">SELECT ProductID, ProductName, ProductCategory\r\n  FROM Inventory.Products\r\n  WHERE (ProductID &lt; 103 OR ProductID &gt; 107 OR ProductID = 106)\r\n  \u00a0 AND ProductCategory IS NOT NULL;<\/pre>\n<p>Now we get the results we want, without rows that contain a <strong>ProductCategory<\/strong> value of <strong>NULL<\/strong>:<\/p>\n<table class=\"table--tight\">\n<thead>\n<tr>\n<td>ProductID<\/td>\n<td>ProductName<\/td>\n<td>ProductCategory<\/td>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>101<\/td>\n<td>Product 101<\/td>\n<td>1<\/td>\n<\/tr>\n<tr>\n<td>102<\/td>\n<td>Product 102<\/td>\n<td>2<\/td>\n<\/tr>\n<tr>\n<td>109<\/td>\n<td>Product 109<\/td>\n<td>3<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>While we\u2019re on the topic of <strong>WHERE<\/strong> clauses, don\u2019t forget to include them in your <strong>DELETE<\/strong> and <strong>UPDATE<\/strong> statements, unless you don\u2019t mind incurring the wrath of everyone around you. For example, a statement such as the following will delete every row in the <strong>Products<\/strong> table:<\/p>\n<pre class=\"theme:ssms2012-simple-talk lang:tsql decode:true\">DELETE Inventory.Products;<\/pre>\n<p>Also watch for statements that inadvertently create Cartesian products because they include no <strong>WHERE<\/strong> clause. For example, the following join is based on pre-ANSI SQL-92 standards, in which the join condition is normally defined in the <strong>WHERE<\/strong> clause:<\/p>\n<pre class=\"theme:ssms2012-simple-talk lang:tsql decode:true\">SELECT p.ProductID, p.ProductName, pc.ProductCategoryName\r\n  FROM Inventory.Products p, Inventory.ProductCategories pc;<\/pre>\n<p>Because there is no <strong>WHERE<\/strong> clause to limit the results, the statement will return 27 rows, with each row in the first table matched to every row in the second table. This number might not be a big deal here, but what if your tables contain millions of records?<\/p>\n<p>The same thing happens when we do a cross join without specifying a <strong>WHERE<\/strong> clause:<\/p>\n<pre class=\"theme:ssms2012-simple-talk lang:tsql decode:true\">SELECT p.ProductID, p.ProductName, pc.ProductCategoryName\r\n  FROM Inventory.Products p CROSS JOIN Inventory.ProductCategories pc;<\/pre>\n<p>Again, we end up with 27 rows, but if you were querying massive tables in a production environment, you could bring your system to a standstill.<\/p>\n<h2>Working with table structures<\/h2>\n<p>When developing your coding standards, you should also address the use of temporary tables, table variables, and common table expressions (CTEs), providing guidelines that explain which type to use when. For example, in the following code, the first statement creates a temporary table, and the second statement uses that table to join the <strong>Products<\/strong> table to the temporary table:<\/p>\n<pre class=\"theme:ssms2012-simple-talk lang:tsql decode:true\">SELECT p.ProductCategory AS CategoryID,\r\n  \u00a0 CASE\r\n  \u00a0 \u00a0 WHEN pc.ProductCategoryName IS NULL THEN 'No category'\r\n  \u00a0 \u00a0 ELSE pc.ProductCategoryName\u00a0\r\n    END AS CategoryName,\u00a0\r\n  \u00a0 COUNT(p.ProductID) AS ProductCount\r\n  INTO #ProductCounts\r\n  FROM Inventory.Products p\r\n  \u00a0 LEFT JOIN Inventory.ProductCategories pc\r\n  \u00a0 ON p.ProductCategory = pc.ProductCategoryID\r\n  GROUP BY p.ProductCategory, pc.ProductCategoryName;\r\n  SELECT pr.ProductID, pr.ProductName, ct.CategoryName\r\n  FROM Inventory.Products pr\r\n  \u00a0 LEFT JOIN #ProductCounts ct\r\n  \u00a0 ON pr.ProductCategory = ct.CategoryID\r\n  WHERE ct.ProductCount &gt; 2;<\/pre>\n<p>When we run these statements, we get the following results:<\/p>\n<table class=\"table--tight\">\n<thead>\n<tr>\n<td>ProductID<\/td>\n<td>ProductName<\/td>\n<td>CategoryName<\/td>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>101<\/td>\n<td>Product 101<\/td>\n<td>Category 1<\/td>\n<\/tr>\n<tr>\n<td>104<\/td>\n<td>Product 104<\/td>\n<td>Category 1<\/td>\n<\/tr>\n<tr>\n<td>107<\/td>\n<td>Product 107<\/td>\n<td>Category 1<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Whenever you implement a temporary table structure in this way, you should evaluate whether the best solution is to use a temporary table, table variable, or CTE, any of which will work in this case. However, you need to clearly understand the differences between them so you can decide which is best in certain circumstances.<\/p>\n<p>For example, temporary tables and table variables are both written to <strong>tempdb<\/strong>, so there\u2019s additional overhead that comes with them. CTEs are not written to <strong>tempdb<\/strong>. In addition, you can create temporary tables at a global or local scope, and the table persists until it is explicitly dropped or the session is terminated. A table variable exists only within the scope of the current batch, stored procedure, or user-defined function. A CTE exists only within the scope of the statement it precedes. You can also create indexes on temporary tables, but not on table variables or CTEs.<\/p>\n<p>There are, in fact, a number of differences between the three. To a certain degree, you can think of a temporary table more like a regular table, a CTE closer to a view, and a table variable more like other variables. But these are generalities. Know that there is a lot more to them than just that.<\/p>\n<h2>Working with transactions<\/h2>\n<p>A transaction is a sequence of T-SQL statements that are executed as a single logical unit. You can explicitly define a transaction by enclosing the T-SQL statements within a <strong>BEGIN<\/strong> <strong>TRANSACTION\u2026COMMIT<\/strong> <strong>TRANSACTION<\/strong> code block, as shown in the following stored procedure definition:<\/p>\n<pre class=\"theme:ssms2012-simple-talk lang:tsql decode:true\">CREATE PROCEDURE Inventory.AddProduct\r\n  \u00a0 @ProductID INT,\r\n  \u00a0 @ProductName NVARCHAR(100) = '',\r\n  \u00a0 @ProductCategory INT = NULL\r\n  AS\r\n  \u00a0 SET NOCOUNT ON\r\n  \u00a0 BEGIN TRANSACTION\u00a0\r\n  \u00a0\u00a0\r\n  \u00a0 \u00a0 INSERT INTO Inventory.Products(ProductID, ProductName, ProductCategory)\r\n  \u00a0 \u00a0 VALUES(@ProductID, @ProductName, @ProductCategory)\r\n  \u00a0COMMIT TRANSACTION;\u00a0\r\n  GO<\/pre>\n<p>Although the database engine will create and run the stored procedure with no problem, the definition itself is missing two important components: rollback logic and error handling.<\/p>\n<p>When defining a transaction, you should take advantage of the T-SQL elements that let you effectively control that transaction, including the ability to roll back all or part of the transaction. You can follow a <strong>BEGIN<\/strong> <strong>TRANSACTION<\/strong> statement with one or more <strong>ROLLBACK<\/strong> <strong>TRANSACTION<\/strong> statements that let you return to the beginning of the transaction or to specific savepoints within the transaction. You can also nest transactions to better control execution logic.<\/p>\n<p>You should also integrate error handling into this logic so you can take specific actions and log relevant information should an error occur. For this, you can use a <strong>TRY\u2026CATCH<\/strong> block that controls the statement flow in the event of an error. For an overview of error handing in SQL Server, you might want to check out my article <a href=\"https:\/\/www.simple-talk.com\/sql\/database-administration\/handling-errors-in-sql-server-2012\/\">Handling Errors in SQL Server 2012<\/a>. Most of the basics have remained unchanged through SQL Server 2016.<\/p>\n<h2>Running the EXECUTE statement<\/h2>\n<p>Now that we\u2019ve introduced stored procedures, we should also touch upon the <strong>EXECUTE<\/strong> statement. In the following example, the <strong>EXECUTE<\/strong> statement calls the procedure from the previous example, passing in two parameter values:<\/p>\n<pre class=\"theme:ssms2012-simple-talk lang:tsql decode:true\">EXECUTE Inventory.AddProduct 110, 'Product 110';<\/pre>\n<p>The fact that we\u2019re passing in only two parameter values is important to note because the stored procedure is defined with three. The procedure will still run, but it will insert a <strong>NULL<\/strong> value into the <strong>ProductCategory<\/strong> column. Even if this is what we want, we should still explicitly include the value when calling the procedure so there is no doubt about the intent.<\/p>\n<p>Related to this issue is the importance of including the parameter name when providing the value. This helps to ensure that the intended value is being mapped to the correct parameter, making the code easier to review and to avoid unnecessary errors. It will also help to ensure you\u2019re not including extra values when calling the procedure or passing in a value that is not consistent with a parameter\u2019s data type (although it doesn\u2019t actual prevent either possibility).<\/p>\n<p>While we\u2019re on the topic of the <strong>EXECUTE<\/strong> statement, keep in mind that you should not use it to call dynamic SQL, whether within a stored procedure or directly, as in the following example:<\/p>\n<pre class=\"theme:ssms2012-simple-talk lang:tsql decode:true\">DECLARE @sql VARCHAR(1000);\r\n  DECLARE @id INT;\r\n  SET @id = 101;\r\n  SET @sql = 'SELECT ProductName FROM Inventory.Products\r\n  \u00a0 WHERE ProductID = ' + CAST(@id AS VARCHAR(10)) + ';';\r\n  EXECUTE (@sql);<\/pre>\n<p>Although the database engine lets us execute dynamic SQL in this way, doing so can make your system susceptible to SQL injection attacks because user input can run directly against the database. For this reason, many database folks recommend that you avoid dynamic SQL altogether.<\/p>\n<p>However, if you can\u2019t get around implementing dynamic SQL, you should use the <strong>sp_executesql<\/strong> system stored procedure to execute the code, rather than an <strong>EXECUTE<\/strong> statement. The <strong>sp_executesql<\/strong> stored procedure also supports strongly typed variables and tends to be more efficient.<\/p>\n<h2>Getting the code right<\/h2>\n<p>Clearly, you must take into account a number of factors when developing T-SQL standards that address issues related to manipulating SQL Server data. As with any aspect of T-SQL coding, you want to create definitive guidelines that will help team members develop readable, consistent, and accurate code that does not break applications or introduce security risks.<\/p>\n<p>As with the previous articles in this series, what we\u2019ve covered here is meant only to provide a starting point to help you to understand the types of issues to address when creating your own standards. You should include whatever specifics you believe relevant to your organization\u2019s development efforts, keeping in mind that your standards are a work-in-progress, evolving as the team changes and as SQL Server and T-SQL are modified.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>SQL was designed to be a third-generation language, expressed in syntax close to real language, because it was designed to be easy for untrained people to use.  Even so, there are ways of expressing SQL Queries and data manipulation in ways  that make it easier for the database engine to turn into efficient action. and easier for your colleagues to understand.  Robert Sheldon homes in on data querying and manipulation and makes suggestions for team standards in SQL Coding.&hellip;<\/p>\n","protected":false},"author":221841,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[143531],"tags":[45110],"coauthors":[6779],"class_list":["post-71526","post","type-post","status-publish","format-standard","hentry","category-t-sql-programming-sql-server","tag-t-sql-formatting"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/71526","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/221841"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=71526"}],"version-history":[{"count":9,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/71526\/revisions"}],"predecessor-version":[{"id":72022,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/71526\/revisions\/72022"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=71526"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=71526"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=71526"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=71526"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}