{"id":107814,"date":"2025-11-12T12:52:41","date_gmt":"2025-11-12T12:52:41","guid":{"rendered":"https:\/\/www.red-gate.com\/simple-talk\/?p=107814"},"modified":"2025-12-17T11:32:56","modified_gmt":"2025-12-17T11:32:56","slug":"5-strategies-to-refactor-sql-code","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/databases\/5-strategies-to-refactor-sql-code\/","title":{"rendered":"5 Strategies to Refactor SQL Code"},"content":{"rendered":"\n<p>Code refactoring is a common process when developing in <a href=\"https:\/\/www.lenovo.com\/gb\/en\/glossary\/procedural-language\/?srsltid=AfmBOorD0-myV6-7Od0aETyKVOESV_iNmONhrH2Br9N8h5Q4LR9_ryiP\" target=\"_blank\" rel=\"noreferrer noopener\">procedural languages<\/a> &#8211; and essential to developing high-quality code &#8211; yet somehow often gets overlooked in <a href=\"https:\/\/aws.amazon.com\/what-is\/sql\/\" target=\"_blank\" rel=\"noreferrer noopener\">SQL<\/a>. In this article, we&#8217;ll explain what refactoring is, how it helps, and give concrete examples on how it can make your code more readable, reliable, and maintainable.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-what-is-refactoring\"><strong>What is Refactoring?<\/strong><\/h2>\n\n\n\n<p>Over time the term &#8220;refactoring&#8221; has expanded and is sometimes used to mean code quality improvement in general, but here we are using it with its original meaning: condensing and eliminating <a href=\"https:\/\/codingcraftsman.wordpress.com\/2018\/11\/09\/the-redundant-code-types\/\" target=\"_blank\" rel=\"noreferrer noopener\">redundant segments of code<\/a>. Like <a href=\"https:\/\/www.calculator.net\/factor-calculator.html\" target=\"_blank\" rel=\"noreferrer noopener\">factoring a number<\/a> in math, we break the code into smaller blocks, identify any repeated elements, then replace them with a single reference. The graphic below illustrates the basic concept of what refactoring tries to accomplish:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"623\" height=\"202\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/11\/image.png\" alt=\"A graphic illustrating the basic concept of what refactoring tries to accomplish.\" class=\"wp-image-107815\" srcset=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/11\/image.png 623w, https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/11\/image-300x97.png 300w\" sizes=\"auto, (max-width: 623px) 100vw, 623px\" \/><\/figure>\n\n\n\n<p>Refactoring can be considered a case of the <a href=\"https:\/\/www.geeksforgeeks.org\/software-engineering\/dont-repeat-yourselfdry-in-software-development\/\" target=\"_blank\" rel=\"noreferrer noopener\">DRY (Don&#8217;t Repeat Yourself)<\/a> principle of software development. Redundant code is harder to read, more prone to bugs, and significantly more expensive to maintain.<\/p>\n\n\n\n<p>Usually, refactoring doesn&#8217;t help performance; it just improves the code quality.&nbsp; But on shorter queries where <a href=\"https:\/\/netenrich.com\/fundamentals\/parsing\" target=\"_blank\" rel=\"noreferrer noopener\">parsing<\/a> is a substantial portion of the total execution time, refactoring can improve performance too.<\/p>\n\n\n\n<p>We&#8217;ll start with some tips on refactoring single queries, then move on to strategies for refactoring across your entire code base. We&#8217;ll demonstrate using short examples, but real-world queries can benefit even more. One of the most extreme cases I encountered, refactoring reduced a 1,200 line query down to just 90 lines \u2013 without, of course, altering the results at all.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-1-use-a-derived-table\">1. Use a Derived Table<\/h2>\n\n\n\n<p>Even novice SQL developers are usually adept at using <a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/learn\/subqueries-in-sql-server\/\" target=\"_blank\" rel=\"noreferrer noopener\">subqueries<\/a> in a <code>WHERE<\/code> clause or <a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/t-sql-programming-sql-server\/using-a-subquery-in-a-select-statement\/#:~:text=A%20subquery%20and%20correlated%20subquery,known%20as%20the%20outer%20query.\" target=\"_blank\" rel=\"noreferrer noopener\">as a column in a <code>SELECT<\/code><\/a>. But a subquery that returns a <em><a href=\"https:\/\/learn.microsoft.com\/en-us\/ssms\/visual-db-tools\/table-valued-object-properties-visual-database-tools\" target=\"_blank\" rel=\"noreferrer noopener\">table-valued object<\/a><\/em> can also be the target of the <code>FROM<\/code> clause, meaning we can use the result just as we would a base table. Such <a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/t-sql-programming-sql-server\/sql-and-scope\/#:~:text=any%20old%20table.-,Derived%20Tables,-A%20derived%20table\" target=\"_blank\" rel=\"noreferrer noopener\">derived tables<\/a> are ideal for creating intermediate values for further processing. The basic syntax looks like this:<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">SELECT (use derived columns here) FROM\n  (\n  SELECT (create derived columns here) FROM (base table)\n  ) AS (table alias)\n<\/pre><\/div>\n\n\n\n<p>Let&#8217;s turn these words into an actual example. Imagine a company that awards a pay modifier of 1% for every month an employee has worked beyond their first twelve months. Our task is a query that calculates for each employee the value of this modifier, along with modified values for their base and bonus pay. <em>Note:<\/em> <em>we&#8217;re using <a href=\"https:\/\/www.microsoft.com\/en-gb\/sql-server\" target=\"_blank\" rel=\"noreferrer noopener\">MS SQL Server<\/a> date functions. The syntax for <a href=\"https:\/\/www.postgresql.org\/\" target=\"_blank\" rel=\"noreferrer noopener\">PostgreSQL<\/a> or <a href=\"https:\/\/www.mysql.com\/\" target=\"_blank\" rel=\"noreferrer noopener\">MySQL<\/a> is slightly different.<\/em><\/p>\n\n\n\n<p>Our employee table contains the following columns:<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">CREATE TABLE employees (\n  emp_id \tINT NOT NULL,\n  hire_date \tDATE NOT NULL,\n  base_pay \tNUMERIC(9,2) NOT NULL,\n  bonus_pay \tNUMERIC(9,2) NOT NULL,\n  \u2026 );\n<\/pre><\/div>\n\n\n\n<p>The query to calculate the pay values is straightforward, though very redundant &#8211; we have to repeat the date arithmetic three times:<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">SELECT\n  emp_id,\n  (GREATEST(DATEDIFF(month, hire_date, GETDATE())-12,0) * .01 + 1),\n  (GREATEST(DATEDIFF(month, hire_date, GETDATE())-12,0) * .01 + 1) * base_pay,\n  (GREATEST(DATEDIFF(month, hire_date, GETDATE())-12,0) * .01 + 1) * bonus_pay\nFROM employees;\n<\/pre><\/div>\n\n\n\n<p>But if we move that date arithmetic into a subquery,&nbsp;the redundancy vanishes:<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">SELECT emp_id,\n  pay_mod,\n  pay_mod * base_pay,\n  pay_mod * bonus_pay\nFROM \n (SELECT GREATEST(DATEDIFF(month, hire_date, GETDATE())-12,0) * .01 + 1 AS pay_mod, *\nFROM employees) AS sub\n<\/pre><\/div>\n\n\n\n<p><em>Note: a version of this with PostgreSQL-format date arithmetic is in Appendix I.<\/em><\/p>\n\n\n\n<p>Remember that a derived table is a <em>table \u2013 <\/em>we can join other tables to it directly, just as if it was a base table. So we can use this technique to simplify much larger, more complex queries than this example.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">2. Lateral Join<\/h2>\n\n\n\n<p>A <a href=\"https:\/\/mayallo.com\/lateral-join\/\" target=\"_blank\" rel=\"noreferrer noopener\">lateral join<\/a> is similar to a <a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/t-sql-programming-sql-server\/using-a-subquery-in-a-select-statement\/\" target=\"_blank\" rel=\"noreferrer noopener\">correlated subquery<\/a>: both are evaluated once for every row in the main query. But where a subquery can only return a single value, a lateral join can return an entire row of values, or even multiple rows. This makes it an obvious choice for refactoring queries that contain multiple identical or similar subqueries.&nbsp;Again, let&#8217;s illustrate this with a concrete example.<\/p>\n\n\n\n<p>Using our employees table from above, let&#8217;s write a query to pair each employee with the name and date of the first company &#8220;team building&#8221; event occurring after they were hired. We have a new table of team events:<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">CREATE TABLE team_events (\n  event_name VARCHAR(128) NOT NULL,\n  event_date DATE NOT NULL );\n<\/pre><\/div>\n\n\n\n<p>Matching the right team event to each employee is easy enough: find event dates greater than the employee hire date, order by <code>event_date<\/code>, then use <code>TOP 1<\/code> (or <code>LIMIT 1<\/code>, for MySQL\/PostgreSQL) to match a single row. The problem is we can&#8217;t use a normal join because of that pesky one-row limit. So you might be tempted to use subqueries:<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">SELECT\n  e.emp_id, \n  ( SELECT event_date FROM team_events te\n\tWHERE te.event_date &gt; e.hire_date ORDER BY event_date LIMIT 1)\n  ( SELECT event_name FROM team_events te\n\tWHERE te.event_date &gt; e.hire_date ORDER BY event_date LIMIT 1)\nFROM employees e\n<\/pre><\/div>\n\n\n\n<p>If you had to select three or more columns in such a manner, you can see this quickly becomes a mess. But a lateral join handily removes the redundancy:<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">SELECT e.emp_id, te.event_date, te.event_name\nFROM employees e\nLEFT JOIN LATERAL \n(\n  SELECT * FROM team_events te\n  WHERE te.event_date &gt; e.hire_date ORDER BY event_date LIMIT 1\n) te ON true\n<\/pre><\/div>\n\n\n\n<p><em>(Note: SQL Server uses the \u2018APPLY\u2019 keyword for lateral joins; see Appendix I)<\/em><\/p>\n\n\n\n<p>A Left Join is used to ensure all employees are listed, even if no matching event exists. Notice that our join condition is just the &#8220;true&#8221; flag. This is because we encapsulate the join condition within the lateral subquery.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">3. Common Table Expression (CTE)<\/h2>\n\n\n\n<p>The word &#8220;common&#8221; in <a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/t-sql-programming-sql-server\/sql-server-cte-basics\/\" target=\"_blank\" rel=\"noreferrer noopener\">&#8216;Common Table Expression&#8217;<\/a> is a hint on how useful these can be for removing redundancy in queries. Our first two tips are limited on where the refactored code can be used. But CTEs are much more flexible: define it at the start of your query and use it anywhere you like, as often as you like. That makes CTEs the &#8216;Swiss army knife&#8217; of SQL code refactoring.<\/p>\n\n\n\n<p>To illustrate, let&#8217;s expand our last example of team building events. These events hold a competition that matches each employee to everyone else present, and we want a query to reflect this. To generate this &#8220;round robin&#8221; result, we use the query from above to match employees to events, then, <em>using the same query<\/em>, self-join the result table to itself.&nbsp;The resulting query is confusing and hard to read \u2013 the redundant blocks are highlighted to identify them:<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">SELECT e1.emp_id, e2.emp_id, te1.event_name, te1.event_date\nFROM employees e1\nLEFT JOIN LATERAL\n(\nSELECT * FROM team_events te\nWHERE te.event_date &gt; e1.hire_date ORDER BY event_date LIMIT 1\n) te1 ON true\nINNER JOIN employees e2 ON e1.emp_id &lt; e2.emp_id\nLEFT JOIN LATERAL\n(\nSELECT * FROM team_events te\nWHERE te.event_date &gt; e2.hire_date ORDER BY event_date LIMIT 1\n) te2 ON true\nWHERE te1.event_name = te2.event_name;<\/pre><\/div>\n\n\n\n<p>Using a CTE, we can condense the two redundant blocks into one, making the code shorter and much easier to understand:<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">WITH emp_event AS (\nSELECT *\nFROM employees e\nLEFT JOIN LATERAL\n(\nSELECT * FROM team_events te\nWHERE te.event_date &gt; e.hire_date ORDER BY event_date LIMIT 1\n) te ON true)\n\nSELECT ee1.emp_id, ee2.emp_id, ee1.event_name, ee1.event_date\nFROM emp_event ee1\nINNER JOIN emp_event ee2 ON ee1.event_name = ee2.event_name AND ee1.emp_id &lt; ee2.emp_id;<\/pre><\/div>\n\n\n\n<p>Here we name our CTE \u201cemp_event\u201d to indicate its joining employees to events. While we use the CTE twice here, a CTE can be used three, four, or more times in a single query. Even if you use it just once, a CTE can still be useful. It doesn&#8217;t shorten the query, but separating out a block of code and naming it to indicate purpose can help organize a large query.<\/p>\n\n\n\n<section id=\"my-first-block-block_93e3d0b4daf7acc4a20f5af1a89ec719\" class=\"my-first-block alignwide\">\n    <div class=\"bg-brand-600 text-base-white py-5xl px-4xl rounded-sm bg-gradient-to-r from-brand-600 to-brand-500 red\">\n        <div class=\"gap-4xl items-start md:items-center flex flex-col md:flex-row justify-between\">\n            <div class=\"flex-1 col-span-10 lg:col-span-7\">\n                <h3 class=\"mt-0 font-display mb-2 text-display-sm\">10 tools for every stage of SQL Server development<\/h3>\n                <div class=\"child:last-of-type:mb-0\">\n                                            SQL Toolbelt Essentials includes 10 ingeniously simple tools that cover your entire database development lifecycle.                                    <\/div>\n            <\/div>\n                            <a href=\"https:\/\/www.red-gate.com\/products\/sql-toolbelt-essentials\/\" class=\"btn btn--secondary btn--lg\">Learn more &amp; try for free<\/a>\n                    <\/div>\n    <\/div>\n<\/section>\n\n\n<h2 class=\"wp-block-heading\">4. Defining a View<\/h2>\n\n\n\n<p>All our examples so far have involved a single query. Let&#8217;s look more globally: refactoring across an entire database. Often a code pattern \u2013 especially those involving table joins \u2013 appears throughout a system over and over again. One of the easiest ways to eliminate this redundancy is by <a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/mastering-sql-views\/\" target=\"_blank\" rel=\"noreferrer noopener\">defining a view<\/a>.<\/p>\n\n\n\n<p>For our example, imagine a database tracking <a href=\"https:\/\/www.red-gate.com\/blog\/audit-and-compliance\/database-monitoring-and-security-go-hand-in-hand\" target=\"_blank\" rel=\"noreferrer noopener\">security alerts<\/a>. The alert description is based on its type, and comes from one lookup table, while the alert priority is based on its source, and comes from another lookup table. So, whenever we access an alert, we must join the same three tables in the same manner:<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">SELECT a.*, at.description, ap.priority\n  FROM Alerts a\n  LEFT OUTER JOIN AlertTypes at ON a.type = at.type\n  LEFT OUTER JOIN AlertPriorities ap ON a.source = ap.source<\/pre><\/div>\n\n\n\n<p>The details of this query aren&#8217;t terribly important. The point is that it&#8217;s a block of code that&#8217;s likely to be repeated throughout an application, whenever an alert is accessed. On production systems, it&#8217;s common to see such join patterns \u2013 by themselves or as blocks in larger queries \u2013 repeated dozens or even hundreds of times.<\/p>\n\n\n\n<p>Defining a view allows us to remove the redundancy, centralizing the business logic within the view, and allowing us to treat these three tables as if they were a single unit. This is as simple as prefacing the code in question with a <code>CREATE VIEW<\/code> statement:<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">CREATE VIEW VwAlert AS\n  SELECT a.*, at.description, ap.priority\n  FROM Alerts a\n  LEFT OUTER JOIN AlertTypes at ON a.alert_type = at.alert_type\n  LEFT OUTER JOIN AlertPriority ap ON a.alert_source = ap.alert_source<\/pre><\/div>\n\n\n\n<p>We can of course query this view directly. But we can also treat it as a table, including it as part of a larger query, for example:<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">SELECT * FROM devices d\nINNER JOIN VwAlerts va d.device_id = va.device_id\n<\/pre><\/div>\n\n\n\n<p>or:<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">SELECT * FROM USERS u\nINNER JOIN VwAlerts va ON u.user_id = va.user_id\n<\/pre><\/div>\n\n\n\n<p>If our business logic involving alert definition changes, we update one view, rather than performing edits scattered across multiple queries.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">5. Generated Columns<\/h2>\n\n\n\n<p><a href=\"https:\/\/www.postgresql.org\/docs\/current\/ddl-generated-columns.html\" target=\"_blank\" rel=\"noreferrer noopener\">Generated columns<\/a> (known as <a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/performance-sql-server\/computed-column-performance-in-sql-server\/\" target=\"_blank\" rel=\"noreferrer noopener\">computed columns<\/a> in SQL Server) allow us to define a repetitive calculation inside a table definition, to be done automatically whenever any query or application accesses the table. This not only eliminates redundancy, but it centralizes business logic at the DDL (data definition layer) &#8211; preventing any query or application from overriding or altering it.<\/p>\n\n\n\n<p>In our employee table, let&#8217;s add a column with a code to indicate employee type: &#8216;P&#8217; for permanent, &#8216;S&#8217; for temporary seasonal help, and &#8216;I&#8217; for interns. To determine how many days per year of paid time off they&#8217;re eligible for, the following rule is used:<\/p>\n\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Permanent employees receive 10 days.<\/li>\n\n\n\n<li>Interns receive 0 days.<\/li>\n\n\n\n<li>Seasonal help hired in December receive 1 day, otherwise they receive 3.<\/li>\n<\/ul>\n<\/div>\n\n\n<p>Calculating this value can be done with a single <code>CASE<\/code> statement, but a large system might use that value in dozens of forms, screens and queries across multiple applications. Repeating the code to calculate it is wasteful and prone to error and, if the code ever changes, updating can be a nightmare. A generated (computed) column is a better approach.<\/p>\n\n\n\n<p>In SQL Server, the code is straightforward (only our two new columns are included here):<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">CREATE TABLE employees (\n   \u2026\n   emp_code CHAR(1) NOT NULL DEFAULT 'P',\n   pto_days AS (CASE\n     WHEN emp_code='P' THEN 10\n     WHEN emp_code='I' THEN 0\n     WHEN emp_code = 'S' AND MONTH(hire_date)=12 THEN 1\n     WHEN emp_code = 'S' AND MONTH(hire_date)&lt;&gt;12 THEN 3\n     ELSE NULL END)\n  );<\/pre><\/div>\n\n\n\n<p>In MySQL, the code is similar, except it requires us to explicitly state the column&#8217;s data type:<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">CREATE TABLE employees (\n   \u2026\n   emp_code CHAR(1) NOT NULL DEFAULT 'P',\n   pto_days INTEGER GENERATED ALWAYS AS (CASE\n     WHEN emp_code='P' THEN 10\n     WHEN emp_code='I' THEN 0\n     WHEN emp_code = 'S' AND MONTH(hire_date)=12 THEN 1\n     WHEN emp_code = 'S' AND MONTH(hire_date)&lt;&gt;12 THEN 3\n     ELSE NULL END) VIRTUAL\n);<\/pre><\/div>\n\n\n\n<p>In both cases, we&#8217;ve defined the column as <strong><em>virtual<\/em><\/strong>: it\u2019s calculated on the fly when we query its value. Both SQL Server and MySQL have a second option, which physically stores the calculated value in the table itself.&nbsp; One benefit of storing the generated column is that it allows you to index on it. When a query needs to order by or quickly search for the results of a calculation, this is a very handy feature. <\/p>\n\n\n\n<p><em>Note: At the time of writing, PostgreSQL didn&#8217;t support virtual generated columns &#8211; the only option was store the value<\/em> <em>&#8211; but as of PostgreSQL 18 (released in September), <a href=\"https:\/\/www.postgresql.org\/docs\/18\/sql-createtable.html#SQL-CREATETABLE-PARMS-GENERATED-STORED\" target=\"_blank\" rel=\"noreferrer noopener\">this functionality was added<\/a>.<\/em><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-final-thoughts-amp-next-steps\">Final Thoughts &amp; Next Steps<\/h2>\n\n\n\n<p>Like any other programming language, SQL benefits from the compact, readable, and well-structured code that refactoring creates. These five techniques aren&#8217;t the only ways to get there, but they&#8217;re all useful and should be part of your daily toolkit. <\/p>\n\n\n\n<p><em>Please scroll to the bottom of the article for the full sample code.<\/em><\/p>\n\n\n\n<p><strong>Update 12\/12\/2025: <\/strong>SQL Server Central Editor Steve Jones has been thinking about refactoring SQL code and <a href=\"https:\/\/www.sqlservercentral.com\/editorials\/refactoring-sql-code\">shared his thoughts<\/a> in this article. What do you think? Whether you comment here on Simple Talk or over on SSC, we&#8217;d love to hear your views.<\/p>\n\n\n\n<section id=\"faq\" class=\"faq-block my-5xl\">\n    <h2>FAQs: Refactoring SQL Code<\/h2>\n\n                        <h3 class=\"mt-4xl\">1. What is SQL code refactoring?<\/h3>\n            <div class=\"faq-answer\">\n                <p>Refactoring SQL code means improving the structure, readability and maintainability of SQL queries and database objects without altering their external behavior.<\/p>\n            <\/div>\n                    <h3 class=\"mt-4xl\">2. Why refactor SQL code?<\/h3>\n            <div class=\"faq-answer\">\n                <p>Over time, SQL scripts can accumulate technical debt &#8211; making them harder to read, brittle to schema changes and expensive to maintain. Refactoring improves clarity and lowers risk.<\/p>\n            <\/div>\n                    <h3 class=\"mt-4xl\">3. When should you refactor SQL code?<\/h3>\n            <div class=\"faq-answer\">\n                <p>Consider refactoring when you see messy formatting, <code>SELECT *<\/code> wildcards, dynamic SQL constructs, duplicated logic, or frequent schema changes breaking queries.<\/p>\n            <\/div>\n                    <h3 class=\"mt-4xl\">4. What are key strategies for refactoring SQL code?<\/h3>\n            <div class=\"faq-answer\">\n                <ul>\n<li>Apply consistent formatting and naming conventions.<\/li>\n<li>Replace wildcards (<code>SELECT *<\/code>) with explicit column lists.<\/li>\n<li>Replace dynamic filters and dynamic <code>ORDER BY<\/code> with static SQL when possible.<\/li>\n<li>Decompose complex logic into variables or simpler statements.<\/li>\n<li>Rename stored procedures, functions and objects to meaningful names.<\/li>\n<\/ul>\n            <\/div>\n                    <h3 class=\"mt-4xl\">5. What risks should be managed during SQL refactoring?<\/h3>\n            <div class=\"faq-answer\">\n                <p>Changing code structure may affect performance, break dependencies, or introduce bugs if tests aren\u2019t in place. Ensure you have good regression tests.<\/p>\n            <\/div>\n                    <h3 class=\"mt-4xl\">6. How should you approach SQL refactoring in practice?<\/h3>\n            <div class=\"faq-answer\">\n                <p>Start small, refactor incrementally, test thoroughly before and after each change, and use tools where available. Keep old and new code supported when transitioning.<\/p>\n            <\/div>\n                    <h3 class=\"mt-4xl\">7. What benefits result from well-executed SQL refactoring?<\/h3>\n            <div class=\"faq-answer\">\n                <p>You\u2019ll get cleaner, more maintainable code, better resilience to schema changes, faster onboarding for new developers, and lower long-term maintenance costs.<\/p>\n            <\/div>\n            <\/section>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-appendix-i-sample-code\">Appendix I: Sample Code<\/h2>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"h-part-1-create-tables-and-load-test-data\">Part 1: Create tables and load test data<\/h4>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">CREATE TABLE employees (\n  emp_id \tINT NOT NULL,\n  name     \tVARCHAR(64),\n  hire_date\tDATE NOT NULL,\n  base_pay\tNUMERIC(9,2) NOT NULL,\n  bonus_pay\tNUMERIC(9,2) NOT NULL\n);\n\nCREATE TABLE team_events (\nevent_name VARCHAR(128) NOT NULL,\nevent_date DATE NOT NULL\n);\n\nINSERT INTO employees values\n  (101,'Joe','11-01-2023', 72400, 7250),\n  (102,'Alice','08-15-2024', 67000, 6700),\n  (103,'Keith','11-02-2024', 98250, 0),\n  (104,'Toby','10-21-2025', 59500, 5950),\n  (105,'Marge','04-30-2025', 48000, 1000),\n  (106,'Olaf','07-31-2025', 46500, 1000),\n  (107,'Amar','10-03-2022', 95000, 0);\n\nINSERT INTO team_events values\n  ('Downtown Scavenger Hunt','12-02-2022'),\n  ('Lakefront Luau','12-10-2023'),\n  ('Air Guitar Competition', '12-09-2024'),\n  ('Rocket Go Karts', '12-11-2025');<\/pre><\/div>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"h-part-2-examples-1-3-in-postgresql-format\">Part 2: Examples 1-3 in PostgreSQL format<br><\/h4>\n\n\n\n<p><strong>Example 1, unfactored<\/strong><\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">SELECT\n  emp_id,\n  (GREATEST((EXTRACT(YEAR from AGE(now(), hire_date))-1) * 12 + EXTRACT(MONTH from   AGE(now(), hire_date)),0) * .01 + 1),\n  (GREATEST((EXTRACT(YEAR from AGE(now(), hire_date))-1) * 12 + EXTRACT(MONTH from AGE(now(), hire_date)),0) * .01 + 1) * base_pay,\n  (GREATEST((EXTRACT(YEAR from AGE(now(), hire_date))-1) * 12 + EXTRACT(MONTH from AGE(now(), hire_date)),0) * .01 + 1) * bonus_pay\n  FROM employees;\n\n-- Example 1, factored\nSELECT emp_id, pay_mod, pay_mod * base_pay, pay_mod * bonus_pay\n  FROM\n  (SELECT GREATEST((EXTRACT(YEAR from AGE(now(), hire_date))-1) * 12 + EXTRACT(MONTH from AGE(now(), hire_date)),0) * .01 + 1 AS pay_mod, *\n  FROM employees) AS sub<\/pre><\/div>\n\n\n\n<p><strong>Example 1, factored<\/strong><\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">SELECT emp_id, pay_mod, pay_mod * base_pay, pay_mod * bonus_pay\n  FROM\n  (SELECT GREATEST((EXTRACT(YEAR from AGE(now(), hire_date))-1) * 12 + EXTRACT(MONTH from AGE(now(), hire_date)),0) * .01 + 1 AS pay_mod, *\n  FROM employees) AS sub<\/pre><\/div>\n\n\n\n<p><strong>Example 2, unfactored<\/strong><\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">SELECT e.emp_id,\n  ( SELECT event_date FROM team_events te\n    WHERE te.event_date &gt; e.hire_date ORDER BY event_date LIMIT 1),\n  ( SELECT event_name FROM team_events te\n    WHERE te.event_date &gt; e.hire_date ORDER BY event_date LIMIT 1)\n  FROM employees e<\/pre><\/div>\n\n\n\n<p><strong>Example 2, factored<\/strong><\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">SELECT e.emp_id, te.event_date, te.event_name\n  FROM employees e\n  LEFT JOIN LATERAL\n  (\n    SELECT * FROM team_events te\n    WHERE te.event_date &gt; e.hire_date ORDER BY event_date LIMIT 1\n  ) te ON true<\/pre><\/div>\n\n\n\n<p><strong>Example 3, unfactored<\/strong><\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">SELECT e1.emp_id, e2.emp_id, te1.event_name, te1.event_date\n  FROM employees e1\n  LEFT JOIN LATERAL\n  (\n  SELECT * FROM team_events te\n    WHERE te.event_date &gt; e1.hire_date ORDER BY event_date LIMIT 1\n  ) te1 ON true\n  INNER JOIN employees e2 ON e1.emp_id &lt; e2.emp_id\n  LEFT JOIN LATERAL\n  (\n  SELECT * FROM team_events te\n    WHERE te.event_date &gt; e2.hire_date ORDER BY event_date LIMIT 1\n  ) te2 ON true\n  WHERE te1.event_name = te2.event_name;<\/pre><\/div>\n\n\n\n<p><strong>Example 3, factored<\/strong><\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">WITH emp_event AS (\nSELECT * FROM employees e\nLEFT JOIN LATERAL\n(\n  SELECT * FROM team_events te\n  WHERE te.event_date &gt; e.hire_date ORDER BY event_date LIMIT 1\n) te ON true)\nSELECT ee1.emp_id, ee2.emp_id, ee1.event_name, ee1.event_date\nFROM emp_event ee1\nINNER JOIN emp_event ee2 ON ee1.event_name = ee2.event_name AND ee1.emp_id &lt; ee2.emp_id;<\/pre><\/div>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"h-part-3-examples-in-sql-server-format\">Part 3: Examples in SQL Server format<\/h4>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><\/div>\n\n\n\n<p><strong>Example 1, unfactored<\/strong><\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">SELECT\n  emp_id,\n  (GREATEST(DATEDIFF(month, hire_date, GETDATE())-12,0) * .01 + 1),\n  (GREATEST(DATEDIFF(month, hire_date, GETDATE())-12,0) * .01 + 1) * base_pay,\n  (GREATEST(DATEDIFF(month, hire_date, GETDATE())-12,0) * .01 + 1) * bonus_pay\nFROM employees;<\/pre><\/div>\n\n\n\n<p><strong>Example 1, factored<\/strong><\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">SELECT emp_id, pay_mod, pay_mod * base_pay, pay_mod * bonus_pay\n  FROM\n  (SELECT GREATEST(DATEDIFF(month, hire_date, GETDATE())-12,0) * .01 + 1 AS pay_mod, *\n    FROM employees) AS sub<\/pre><\/div>\n\n\n\n<p><strong>Example 2, unfactored<\/strong><\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">SELECT e.emp_id,\n  (SELECT TOP 1 event_date FROM team_events te\n  WHERE te.event_date &gt; e.hire_date ORDER BY event_date),\n  (SELECT TOP 1 event_name FROM team_events te\n  WHERE te.event_date &gt; e.hire_date ORDER BY event_date)\nFROM employees e<\/pre><\/div>\n\n\n\n<p><strong>Example 2, factored<\/strong><\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">SELECT e.emp_id, te.event_date, te.event_name\nFROM employees e\nOUTER APPLY\n(\n  SELECT TOP 1 * FROM team_events te\n  WHERE te.event_date &gt; e.hire_date ORDER BY event_date ) te<\/pre><\/div>\n\n\n\n<p><strong>Example 3, unfactored<\/strong><\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">SELECT e1.emp_id, e2.emp_id, te1.event_name, te1.event_date\nFROM employees e1\nOUTER APPLY\n(\n  SELECT TOP 1 * FROM team_events te\n  WHERE te.event_date &gt; e1.hire_date ORDER BY event_date\n) te1\n  INNER JOIN employees e2 ON e1.emp_id &lt; e2.emp_id\n  OUTER APPLY\n(\n  SELECT TOP 1 * FROM team_events te\n  WHERE te.event_date &gt; e2.hire_date ORDER BY event_date\n) te2\nWHERE te1.event_name = te2.event_name;<\/pre><\/div>\n\n\n\n<p><strong>Example 3, factored<\/strong><\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">WITH emp_event AS (\nSELECT * FROM employees e\nOUTER APPLY\n(\n  SELECT TOP 1 * FROM team_events te\n  WHERE te.event_date &gt; e.hire_date ORDER BY event_date\n) te )\nSELECT ee1.emp_id, ee2.emp_id, ee1.event_name, ee1.event_date\n  FROM emp_event ee1\n  INNER JOIN emp_event ee2 ON ee1.event_name = ee2.event_name AND ee1.emp_id &lt; ee2.emp_id;<\/pre><\/div>\n\n\n\n<section id=\"my-first-block-block_b3dc2f82da7f57b703ea4d7d10a87ecb\" class=\"my-first-block alignwide\">\n    <div class=\"bg-brand-600 text-base-white py-5xl px-4xl rounded-sm bg-gradient-to-r from-brand-600 to-brand-500 red\">\n        <div class=\"gap-4xl items-start md:items-center flex flex-col md:flex-row justify-between\">\n            <div class=\"flex-1 col-span-10 lg:col-span-7\">\n                <h3 class=\"mt-0 font-display mb-2 text-display-sm\">Subscribe to the Simple Talk newsletter<\/h3>\n                <div class=\"child:last-of-type:mb-0\">\n                                            Get selected articles, event information, podcasts and other industry content delivered straight to your inbox every two weeks.                                    <\/div>\n            <\/div>\n                            <a href=\"https:\/\/www.red-gate.com\/simple-talk\/subscribe\/\" class=\"btn btn--secondary btn--lg\">Subscribe now<\/a>\n                    <\/div>\n    <\/div>\n<\/section>","protected":false},"excerpt":{"rendered":"<p>Struggling with messy SQL queries? Discover 5 smart refactoring strategies to clean up your code, boost performance, and simplify maintenance.&hellip;<\/p>\n","protected":false},"author":343663,"featured_media":107820,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":true,"footnotes":""},"categories":[143523,143524],"tags":[4168,4170,5854,158978,4150,4151,4252],"coauthors":[159110],"class_list":["post-107814","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-databases","category-sql-server","tag-database","tag-database-administration","tag-mysql","tag-postgresql","tag-sql","tag-sql-server","tag-t-sql-programming"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/107814","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\/343663"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=107814"}],"version-history":[{"count":8,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/107814\/revisions"}],"predecessor-version":[{"id":108136,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/107814\/revisions\/108136"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media\/107820"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=107814"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=107814"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=107814"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=107814"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}