<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Database Insights</title>
    <description>The latest articles on DEV Community by Database Insights (@databaseinsights).</description>
    <link>https://dev.to/databaseinsights</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4056980%2Fe0231219-eac5-4173-ab98-0bc08d0f6f44.png</url>
      <title>DEV Community: Database Insights</title>
      <link>https://dev.to/databaseinsights</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/databaseinsights"/>
    <language>en</language>
    <item>
      <title>What AI Coding Assistants Need Next: Context, Model Choice and Human Review</title>
      <dc:creator>Database Insights</dc:creator>
      <pubDate>Fri, 02 Oct 2026 15:24:37 +0000</pubDate>
      <link>https://dev.to/databaseinsights/what-ai-coding-assistants-need-next-context-model-choice-and-human-review-14ip</link>
      <guid>https://dev.to/databaseinsights/what-ai-coding-assistants-need-next-context-model-choice-and-human-review-14ip</guid>
      <description>&lt;p&gt;AI coding assistants are past the novelty stage. Most developers have already seen a model write a working query in seconds. The harder question now is whether that query fits &lt;em&gt;your&lt;/em&gt; schema, &lt;em&gt;your&lt;/em&gt; conventions and &lt;em&gt;your&lt;/em&gt; production data.&lt;/p&gt;

&lt;p&gt;That shift is changing how developer tools are built. Instead of a chat window bolted onto the side of an IDE, assistants are getting deeper access to project context, more flexibility in the models behind them and a more permanent place in everyday work.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://www.devart.com/blog/whats-new-in-dbforge-2026-2-2.html" rel="noopener noreferrer"&gt;dbForge 2026.2 release&lt;/a&gt; is one example of this. &lt;a href="https://www.devart.com/dbforge/ai-assistant/" rel="noopener noreferrer"&gt;dbForge AI Assistant&lt;/a&gt; can now use the active document, uploaded files, sample code and multiple databases as context in the same chat. Developers can also choose between several Anthropic and OpenAI models, and there is now a free Express edition.&lt;/p&gt;

&lt;p&gt;Here is what that kind of update says about where AI developer tools are heading, and why the human in the loop matters more, not less.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Context is what separates plausible code from correct code
&lt;/h2&gt;

&lt;p&gt;A language model does not know your database. Without context, it fills the gaps with the most statistically likely answer, and that answer is often generic.&lt;/p&gt;

&lt;p&gt;Ask an assistant with no context for "active customers" and you might get this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'active'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It looks fine. But what if your schema has no &lt;code&gt;status&lt;/code&gt; column, and "active" actually means not soft-deleted and with an order in the last 90 days?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;full_name&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;sales&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customers&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_deleted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="k"&gt;EXISTS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;sales&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;
    &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt;
      &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_date&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;DATEADD&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;day&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;GETDATE&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first query is plausible. The second is correct for this database. The difference is not model intelligence but context: table names, column semantics, business rules and team conventions.&lt;/p&gt;

&lt;p&gt;This is why SQL is a particularly unforgiving test for AI assistants. A wrong join or a missing filter rarely throws an error. It returns a result that looks reasonable and is quietly wrong.&lt;/p&gt;

&lt;p&gt;The practical response is to give the assistant more of what a new team member would need on day one:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The code you are working on&lt;/strong&gt;, without copying it into the prompt by hand&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Related files&lt;/strong&gt;, such as application code, database projects, diagrams or written instructions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Examples of house style&lt;/strong&gt;, so generated code follows existing naming and formatting standards&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;More than one database&lt;/strong&gt;, for tasks like comparing two schemas or tracing data across systems&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;dbForge AI Assistant now covers each of these. It can auto-attach the active document, accept uploaded files and sample code, and work with multiple databases in a single chat. It can also compare two scripted databases.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. One model rarely fits every task
&lt;/h2&gt;

&lt;p&gt;Many early AI coding tools were built around a single model. More of them now offer a choice, because developers have learned that models differ in ways that matter day to day.&lt;/p&gt;

&lt;p&gt;Some are faster and cheaper, which suits quick completions and short explanations. Others handle long, multi-step reasoning better, which helps with complex refactoring or reviewing a large stored procedure. Teams also have their own constraints: an approved vendor list, a preferred provider or simply a model they have learned to trust.&lt;/p&gt;

&lt;p&gt;Letting developers pick the model turns the assistant into a configurable tool rather than a black box. It also protects teams from being locked into one vendor's roadmap at a time when new models arrive every few months.&lt;/p&gt;

&lt;p&gt;In dbForge 2026.2, the model is set under &lt;strong&gt;Tools &amp;gt; Options &amp;gt; AI Assistant &amp;gt; General&lt;/strong&gt;. The current options are:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Provider&lt;/th&gt;
&lt;th&gt;Models&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Anthropic&lt;/td&gt;
&lt;td&gt;Claude Haiku 4.5, Claude Sonnet 4.6, Claude Sonnet 5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OpenAI&lt;/td&gt;
&lt;td&gt;GPT-5.4, GPT-5.6 Luna&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A sensible approach is to start with a lighter model for routine work and switch to a stronger one when the task involves several objects, unfamiliar code or a high cost of error.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. The assistant moves into the workflow
&lt;/h2&gt;

&lt;p&gt;The first wave of AI tools lived in a browser tab. Developers copied code out of the editor, pasted it into a chat, then pasted the answer back. It worked, but every round trip lost context and added friction.&lt;/p&gt;

&lt;p&gt;The current direction is the opposite: put the assistant where the work already happens. In a database tool, that means next to the SQL editor, the schema and the connections a developer uses all day. The assistant can see what is open, and the developer does not have to explain the environment every time.&lt;/p&gt;

&lt;p&gt;This matters for adoption. A tool that sits inside the daily workflow gets used for small, frequent tasks, such as explaining an unfamiliar query, drafting a migration script or checking a schema difference. Those small tasks are where most of the time savings add up.&lt;/p&gt;

&lt;p&gt;Access is part of the same trend. dbForge 2026.2 adds a free Express edition of AI Assistant with a limited token allowance and 14-day chat history. Paid plans offer a considerably larger token allowance and unlimited history. A free tier lowers the barrier for developers who want to test an assistant on real work before a team commits to it.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Better context does not remove the need for review
&lt;/h2&gt;

&lt;p&gt;More context makes AI output more relevant. It does not make it guaranteed. A model can still misread a business rule, pick the wrong index or write a query that performs well on a test table and badly on 200 million rows.&lt;/p&gt;

&lt;p&gt;Database code raises the stakes. An &lt;code&gt;UPDATE&lt;/code&gt; without the right &lt;code&gt;WHERE&lt;/code&gt; clause is not a style issue. Neither is a migration that locks a busy table at peak hours.&lt;/p&gt;

&lt;p&gt;Treat AI-generated SQL the way you would treat a pull request from a capable colleague who joined last week:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Read it before you run it.&lt;/strong&gt; Check every join, filter and aggregate against what you actually asked for.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Run it somewhere safe first.&lt;/strong&gt; Use a development or staging database, never production.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test with realistic data.&lt;/strong&gt; Edge cases, NULLs and volume expose problems that a few sample rows hide. Generated test data helps here.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check the execution plan.&lt;/strong&gt; Correct results with a full table scan can still be a production incident.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wrap changes in a transaction.&lt;/strong&gt; For anything that modifies data, make rollback easy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Review the diff for schema changes.&lt;/strong&gt; Compare before and after, and keep the change in version control.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep humans accountable.&lt;/strong&gt; The developer who merges the code owns it, whoever drafted it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The goal is not to slow AI down. It is to make sure the time saved writing code is not lost later debugging it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The takeaway
&lt;/h2&gt;

&lt;p&gt;The next phase of AI developer tools is less about bigger claims and more about fit: the right context, the right model for the task and a place in the tools developers already use. Releases like dbForge 2026.2 show that direction in practice.&lt;/p&gt;

&lt;p&gt;What stays constant is the developer's judgment. AI can draft the query. You still decide whether it ships.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;What context do you give your AI assistant before trusting its SQL? Share your approach in the comments.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Full release notes: &lt;a href="https://www.devart.com/blog/whats-new-in-dbforge-2026-2-2.html" rel="noopener noreferrer"&gt;What's new in dbForge 2026.2&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>sql</category>
      <category>database</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Text-to-SQL in Practice: When to Trust AI Output and When to Gate It</title>
      <dc:creator>Database Insights</dc:creator>
      <pubDate>Wed, 23 Sep 2026 18:03:49 +0000</pubDate>
      <link>https://dev.to/databaseinsights/text-to-sql-in-practice-when-to-trust-ai-output-and-when-to-gate-it-24fe</link>
      <guid>https://dev.to/databaseinsights/text-to-sql-in-practice-when-to-trust-ai-output-and-when-to-gate-it-24fe</guid>
      <description>&lt;p&gt;AI assistants are now a normal part of SQL work. You describe what you need, get a query back in seconds and move on. The problem is not that these queries fail. Most of the time, they run perfectly. The problem is that a query can run perfectly and still be wrong.&lt;/p&gt;

&lt;p&gt;This post covers why that happens, how to sort database tasks by risk and how to build guardrails into a normal workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adoption is high, trust is not
&lt;/h2&gt;

&lt;p&gt;The Stack Overflow 2025 Developer Survey puts AI adoption at 84% of developers using or planning to use AI tools. The same survey found that 46% of developers distrust the accuracy of AI output, compared with 33% who trust it. And 66% named "almost right, but not quite" answers as their top frustration.&lt;/p&gt;

&lt;p&gt;That combination describes text-to-SQL well. The output looks correct, compiles and executes. Whether it answers the right question is a separate matter.&lt;/p&gt;

&lt;h2&gt;
  
  
  An illustrative example: the silent join
&lt;/h2&gt;

&lt;p&gt;Say you ask an assistant for customers with more than three orders in the last 90 days. Your hypothetical schema has &lt;code&gt;orders&lt;/code&gt; and &lt;code&gt;order_items&lt;/code&gt;, and the assistant decides it needs item-level data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;order_count&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;order_items&lt;/span&gt; &lt;span class="n"&gt;oi&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;oi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_date&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="k"&gt;CURRENT_DATE&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;INTERVAL&lt;/span&gt; &lt;span class="s1"&gt;'90 days'&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;
&lt;span class="k"&gt;HAVING&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This runs without error. But &lt;code&gt;COUNT(*)&lt;/code&gt; now counts order lines, not orders. A customer with one order of four items qualifies. The fix is small:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;DISTINCT&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;order_count&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_date&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="k"&gt;CURRENT_DATE&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;INTERVAL&lt;/span&gt; &lt;span class="s1"&gt;'90 days'&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;
&lt;span class="k"&gt;HAVING&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;DISTINCT&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing in the first version would trigger an alert. The numbers are simply inflated. Now imagine that query feeding a retention dashboard.&lt;/p&gt;

&lt;h2&gt;
  
  
  The four failure modes to watch for
&lt;/h2&gt;

&lt;p&gt;Most AI-generated SQL bugs fall into a few categories:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Schema misinterpretation.&lt;/strong&gt; Correct tables but wrong join keys, or a column whose meaning differs from what the model assumed. For example, &lt;code&gt;order_date&lt;/code&gt; might mean placed-date in one table and fulfilled-date in another.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Silent logic errors.&lt;/strong&gt; Wrong aggregation grain, fan-out from joins, off-by-one date boundaries. No exceptions, just wrong results.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Governance gaps.&lt;/strong&gt; Queries that touch PII or tables outside the user's intended scope because the model had no reason to avoid them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Context collapse.&lt;/strong&gt; Logic that holds on a simple schema and breaks on a real one with layered relationships and inconsistent naming.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The root cause is the same in each case: the model knows SQL but does not know your database.&lt;/p&gt;

&lt;h2&gt;
  
  
  A risk-tiered workflow
&lt;/h2&gt;

&lt;p&gt;Rather than a single rule for AI usage, sort tasks by the cost of an undetected error.&lt;/p&gt;

&lt;h3&gt;
  
  
  Low risk: use AI freely
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Exploratory queries in dev, where you see the result immediately&lt;/li&gt;
&lt;li&gt;Draft documentation for tables and stored procedures&lt;/li&gt;
&lt;li&gt;Explaining an unfamiliar schema you can inspect yourself&lt;/li&gt;
&lt;li&gt;First-pass troubleshooting on slow queries&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  High risk: gate the output
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Migration scripts and production deployments&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ALTER&lt;/code&gt; statements on objects with downstream dependencies such as views, ETL jobs and application queries&lt;/li&gt;
&lt;li&gt;Queries touching PII, audit logs or regulated data&lt;/li&gt;
&lt;li&gt;Financial and operational reporting&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the high-risk tier, AI output is a proposal. It needs review, testing and sign-off like any other change.&lt;/p&gt;

&lt;h2&gt;
  
  
  Guardrails that fit an existing pipeline
&lt;/h2&gt;

&lt;p&gt;You do not need a separate process for AI-generated SQL. You need to make sure it goes through the one you already have.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Validate reporting queries against a baseline.&lt;/strong&gt; Before a new query replaces an existing one, compare row counts and key aggregates on a known dataset.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Run AI changes through CI/CD.&lt;/strong&gt; If schema changes go through version control, automated tests and review, AI-generated migrations do too. No fast lane.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Enforce the same permissions.&lt;/strong&gt; AI-generated queries should run under the same role as the user requesting them, with row-level security and column masking still applied.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tag AI-assisted changes.&lt;/strong&gt; A commit message convention or a field in your change log is enough. When something breaks, you want to know what ran, who approved it and whether AI produced it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Review joins and aggregations first.&lt;/strong&gt; When reviewing generated SQL, these are the lines most likely to hide a silent error. Check join cardinality and &lt;code&gt;GROUP BY&lt;/code&gt; grain before anything else.&lt;/p&gt;

&lt;h2&gt;
  
  
  Context is the biggest lever
&lt;/h2&gt;

&lt;p&gt;Much of the failure surface comes from the model guessing at structure. Pasting a schema description into a chat window helps a little. An assistant that reads the live structure of the connected database helps more, because it has less to guess.&lt;/p&gt;

&lt;p&gt;This is why AI is moving into database IDEs. dbForge AI Assistant, for instance, runs inside dbForge Studio and dbForge Edge and works from the connected database's metadata, including tables, column types and relationships, for text-to-SQL, query optimization and error troubleshooting. It sends metadata for context rather than table data. Schema awareness like this reduces misinterpretation, but it does not catch every logic error, so the review steps above still apply.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;AI assistants are good at producing SQL quickly. They are not yet good at knowing whether that SQL matches what your business means by "customer" or "order."&lt;/p&gt;

&lt;p&gt;Treat AI output the way you would treat a pull request from a capable new team member: useful, often right and always reviewed when it matters.&lt;/p&gt;

&lt;p&gt;What guardrails does your team use for AI-generated SQL? Share them in the comments.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Source: This post builds on ideas from Victor Horlenko's HackerNoon article, &lt;a href="https://hackernoon.com/ai-assistants-in-databases-speed-vs-reliability" rel="noopener noreferrer"&gt;AI Assistants in Databases: Speed vs. Reliability&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>sql</category>
      <category>ai</category>
      <category>database</category>
      <category>devops</category>
    </item>
    <item>
      <title>Cleaning Up Unused Indexes Without Breaking Performance</title>
      <dc:creator>Database Insights</dc:creator>
      <pubDate>Mon, 21 Sep 2026 16:55:31 +0000</pubDate>
      <link>https://dev.to/databaseinsights/cleaning-up-unused-indexes-without-breaking-performance-2li</link>
      <guid>https://dev.to/databaseinsights/cleaning-up-unused-indexes-without-breaking-performance-2li</guid>
      <description>&lt;p&gt;Indexes tend to outlive the problems that created them. It usually starts with a slow query. An engineer adds an index, the immediate issue goes away, and everyone moves on. At the time, the decision makes sense. The problem is that the index often stays in place long after the application changes, a report is replaced, or a feature quietly falls out of use.&lt;/p&gt;

&lt;p&gt;Over time, a busy table can collect a long list of nonclustered indexes. Some are still doing useful work. Others are only there because nobody has had a good reason, or enough confidence, to remove them.&lt;/p&gt;

&lt;p&gt;That's where &lt;code&gt;sys.dm_db_index_usage_stats&lt;/code&gt; comes into play. An index that is updated frequently but has no recorded seeks, scans, or lookups is worth investigating. However, it is not proof that the index is no longer needed. The counters reset when SQL Server restarts, and some workloads only run at specific times. An index that looks unused today may still support month-end reporting, an annual reconciliation, or an audit query that runs once in a while.&lt;/p&gt;

&lt;p&gt;That is why DBAs are cautious about dropping indexes. Dropping based on one snapshot could create a problem that does not show up until the next business cycle. This is why DBAs are cautious, even when an index appears unnecessary.&lt;/p&gt;

&lt;p&gt;So the goal here is not to remove every index with a zero next to it. The goal is to get enough evidence to make a safe call, one thing at a time, then watch the workload after the change, and have a rollback script ready if the workload paints a different picture.&lt;/p&gt;

&lt;h2&gt;
  
  
  The cost of 'just in case' indexes
&lt;/h2&gt;

&lt;p&gt;The thing about an unused index is that it does not become free just because nobody is reading from it. If you update an order, SQL Server may have to update the order row and every index that includes the columns that changed. That adds work to inserts, updates, and deletes. A few useful indexes are worth that trade-off. Once an older index stops helping queries, though, SQL Server still has to keep it current.&lt;/p&gt;

&lt;p&gt;That work is recorded as &lt;code&gt;user_updates&lt;/code&gt; in &lt;code&gt;sys.dm_db_index_usage_stats&lt;/code&gt;. Wider indexes, particularly those with large &lt;code&gt;INCLUDE&lt;/code&gt; lists, also use more pages, create more log activity, and make rebuilds and maintenance jobs heavier. Microsoft &lt;a href="https://learn.microsoft.com/en-us/sql/relational-databases/sql-server-index-design-guide?view=sql-server-ver17" rel="noopener noreferrer"&gt;warns against speculative and overly wide indexes&lt;/a&gt; for the same reasons.&lt;/p&gt;

&lt;p&gt;That does not mean a table with many indexes is automatically a problem. A reporting table may need several of them. An insert-heavy queue table may need far fewer. The question is whether each index still helps enough to justify the work it adds.&lt;/p&gt;

&lt;p&gt;This balance can be lost, since indexes are added one slow query at a time. That pattern repeated, with one team describing tables with more than 70 indexes in a &lt;a href="https://dba.stackexchange.com/questions/331896/balancing-indexing-and-database-performance-how-many-indexes-are-too-many" rel="noopener noreferrer"&gt;2023 DBA Stack Exchange discussion&lt;/a&gt;. That does not mean all 70 were unnecessary. It does show how old fixes can linger in the database long after the original problem has changed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with a baseline, not a deletion list
&lt;/h2&gt;

&lt;p&gt;The first job is to build a picture of what is actually in the database. Look at usage, size, and basic index metadata together. If the index is big and is constantly being updated, then an index with no reads is more interesting. But on its own, it's not enough to bring about a decline.&lt;/p&gt;

&lt;p&gt;The query returns a starting inventory of standard rowstore indexes. This excludes heaps, primary keys, unique indexes and hypothetical indexes. That is deliberate. The other indexes are candidates for review, not indexes that are safe to delete.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;index_size&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;
 &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;object_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="n"&gt;index_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;used_page_count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;used_mb&lt;/span&gt;
 &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dm_db_partition_stats&lt;/span&gt;
 &lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;object_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index_id&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
 &lt;span class="k"&gt;schema_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="k"&gt;table_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="n"&gt;index_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;type_desc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="n"&gt;size_mb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;CAST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;COALESCE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;used_mb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="nb"&gt;decimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
 &lt;span class="k"&gt;reads&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;COALESCE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_seeks&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;COALESCE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_scans&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;COALESCE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_lookups&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
 &lt;span class="n"&gt;writes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;COALESCE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_updates&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
 &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;last_user_seek&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;last_user_scan&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;last_user_lookup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;last_user_update&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sqlserver_start_time&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;indexes&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tables&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;
 &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;object_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;object_id&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;schemas&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;
 &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;schema_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;schema_id&lt;/span&gt;
&lt;span class="k"&gt;LEFT&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dm_db_index_usage_stats&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;
 &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;database_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DB_ID&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;object_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;object_id&lt;/span&gt;
&lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;index_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;index_id&lt;/span&gt;
&lt;span class="k"&gt;LEFT&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;index_size&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt;
 &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;object_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;object_id&lt;/span&gt;
&lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;index_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;index_id&lt;/span&gt;
&lt;span class="k"&gt;CROSS&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dm_os_sys_info&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; 
&lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_hypothetical&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; 
&lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_primary_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; 
&lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_unique&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;writes&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;reads&lt;/span&gt; &lt;span class="k"&gt;ASC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size_mb&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keep the SQL Server restart time in the output. As &lt;a href="https://learn.microsoft.com/en-us/sql/relational-databases/system-dynamic-management-objects/sys-dm-db-index-usage-stats-transact-sql?view=sql-server-ver17" rel="noopener noreferrer"&gt;Microsoft's DMV documentation&lt;/a&gt; explains, the counters are cleared when the Database Engine starts. A restart, detach, or shutdown can leave an index looking unused simply because SQL Server has not been running long enough to see its normal workload. Zero reads after six days means six days of evidence. It does not tell you the full history of the index.&lt;/p&gt;

&lt;p&gt;So, store the results in an admin database or monitoring platform and keep collecting them over time. Ensure that the observation window includes the workloads that are most important. These include weekly jobs, month-end reporting, quarter-end processing, seasonal peaks, and disaster-recovery tests, as appropriate.&lt;/p&gt;

&lt;p&gt;There is no universal cut-off, but Azure SQL automatic tuning &lt;a href="https://learn.microsoft.com/en-us/azure/azure-sql/database/database-advisor-implement-performance-recommendations?view=azuresql" rel="noopener noreferrer"&gt;waits for more than 90 days&lt;/a&gt; before treating an index as unused. That is a useful check against making a decision after one quiet week.&lt;/p&gt;

&lt;h2&gt;
  
  
  Duplicate and overlapping are different findings
&lt;/h2&gt;

&lt;p&gt;Exact duplicates are the easiest candidates to investigate. They have the same key columns in the same order, including sort direction, as well as the same included columns and filter options. Their names do not matter.&lt;/p&gt;

&lt;p&gt;Overlaps need more judgment. Consider these two indexes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;IX_Order_Customer&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;Sales&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OrderHeader&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CustomerID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;INCLUDE&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OrderDate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TotalDue&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;IX_Order_Customer_Date&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;Sales&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OrderHeader&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CustomerID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;OrderDate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;INCLUDE&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TotalDue&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both start with &lt;code&gt;CustomerID&lt;/code&gt;, so the second may cover some of the same work as the first. But it is wider, and it can be better for queries that filter or sort by &lt;code&gt;OrderDate&lt;/code&gt;. That makes it an overlap, not a duplicate. The same caution applies to filtered indexes, unique indexes, and indexes with a different sort direction. Similar column names do not mean the indexes do the same job.&lt;/p&gt;

&lt;p&gt;This is also how missing-index recommendations can create clutter. SQL Server stores &lt;a href="https://learn.microsoft.com/en-us/sql/relational-databases/indexes/tune-nonclustered-missing-index-suggestions?view=sql-server-ver17" rel="noopener noreferrer"&gt;up to 600 missing-index groups&lt;/a&gt;, and Microsoft notes that similar suggestions often need to be combined. Treat each one as a clue about a query workload, not as a script to run.&lt;/p&gt;

&lt;p&gt;Before removing an overlapping index, check the queries and plans that use both indexes. Query Store is useful because it keeps plan and runtime history beyond the current plan cache. Also check application code for index hints. A hinted query may rely on an index that looks redundant in the metadata.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use a retirement queue
&lt;/h2&gt;

&lt;p&gt;Unused indexes should go into a review queue, not straight to a &lt;code&gt;DROP INDEX&lt;/code&gt; script.&lt;/p&gt;

&lt;p&gt;Check telemetry more than once and review queries and business timing around it then assign an owner and change window. Test the exact &lt;code&gt;CREATE INDEX&lt;/code&gt; rollback script in production before you create it. It should preserve key order, included columns, filters, uniqueness, compression, filegroup or partition scheme, and relevant options.&lt;/p&gt;

&lt;p&gt;Remove a small batch, then watch the workload through an agreed period. If performance changes, restore the index. This makes it possible to connect a regression to a specific change.&lt;/p&gt;

&lt;p&gt;Do not treat &lt;code&gt;ALTER INDEX ... DISABLE&lt;/code&gt; as an easy test. &lt;a href="https://learn.microsoft.com/en-us/sql/relational-databases/indexes/disable-indexes-and-constraints?view=sql-server-ver17" rel="noopener noreferrer"&gt;Disabling a nonclustered index&lt;/a&gt; removes its physical data and requires a rebuild to use it again. Disabling a clustered index makes the table inaccessible. A scripted drop with a tested create script is often simpler.&lt;/p&gt;

&lt;p&gt;Azure SQL does similar work automatically: it observes queries after a drop, and recreates the index if they slow down. The practical rule is the same. Change, watch and reverse when the evidence tells you to.&lt;/p&gt;

&lt;h2&gt;
  
  
  Put the change under operational control
&lt;/h2&gt;

&lt;p&gt;SQL Server telemetry should lead the investigation. &lt;a href="https://www.devart.com/dbforge/sql/studio/" rel="noopener noreferrer"&gt;dbForge Studio for SQL Server&lt;/a&gt; gives the team a practical place to inspect and control the change once they have a candidate.&lt;/p&gt;

&lt;p&gt;In Table Editor, engineers can review an index's key and included columns, type, storage details, fragmentation, and &lt;a href="https://docs.devart.com/studio-for-sql-server/user-interface-concepts/table-editor/working-with-table-editor-tab-indexes.html" rel="noopener noreferrer"&gt;usage statistics&lt;/a&gt;. This is particularly useful when one table has several similar index definitions and catalog output is becoming hard to read.&lt;/p&gt;

&lt;p&gt;Before removing anything, generate the &lt;code&gt;CREATE INDEX&lt;/code&gt; script and attach it to the change. dbForge can generate CREATE, DROP, and DROP and CREATE scripts, so the team can review the actual T-SQL and keep a usable rollback. &lt;a href="https://www.devart.com/dbforge/sql/schemacompare/" rel="noopener noreferrer"&gt;Schema Compare&lt;/a&gt; then helps confirm that the indexes selected for removal are the only intended schema differences.&lt;/p&gt;

&lt;p&gt;Use &lt;a href="https://www.devart.com/dbforge/sql/source-control/" rel="noopener noreferrer"&gt;dbForge Source Control&lt;/a&gt; to compare the change against the repository, then commit the removal and rollback scripts together. That gives the team a record of what changed, why it changed, and how to reverse it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Watch the workload that matters
&lt;/h2&gt;

&lt;p&gt;Compare performance before and after change over similar time periods. Query Store helps here, as it stores plans and run time history. This is the default option for new SQL Server 2022 databases.&lt;/p&gt;

&lt;p&gt;Do not use averages across the whole database. Watch for the queries that used the index, the application endpoints behind them, and the scheduled jobs that are most likely to detect its absence. Duration, CPU, reads, writes, timeouts, blocking and log IO checks. You should also see the benefit that is expected on the write side. If dropping a large index doesn't make a difference, it's worth finding out why.&lt;/p&gt;

&lt;p&gt;Set the rollback threshold prior to the change. If the overall CPU is not changed, but an important order lookup suddenly takes twice as many logical reads, restore the index. If the index was adding a lot of write and maintenance work, a little slowdown in a five minute reporting job might be acceptable. Query frequency is a factor but so is business impact.&lt;/p&gt;

&lt;h2&gt;
  
  
  Leave evidence for the next engineer
&lt;/h2&gt;

&lt;p&gt;Record what was reviewed and why. At a minimum, keep the index and table name, observation period, restart events, usage and size data, overlap analysis, affected queries, test results, monitoring window, final decision, and rollback script.&lt;/p&gt;

&lt;p&gt;Document the indexes you keep as well. A note such as "retained because it supports the quarterly close" can save the next engineer from repeating the same investigation six months later.&lt;/p&gt;

&lt;p&gt;This is what makes index cleanup normal engineering work rather than occasional housekeeping. New indexes have a reason and an owner. Old ones can be questioned with evidence.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;Unused indexes should not remain forever because nobody wants to drop them. But they should not disappear because one DMV returned zero either. Look across the right business cycle, make small reversible changes, and judge the result by the workload users actually feel. dbForge Studio helps keep the inspection, scripts, schema comparison, and repository history in one place. The process is what makes the change safe.&lt;/p&gt;

</description>
      <category>sqlserver</category>
      <category>database</category>
      <category>devops</category>
      <category>performance</category>
    </item>
    <item>
      <title>Making SSMS Smarter: Building a Productive T-SQL Workflow with Snippets and Code Analysis</title>
      <dc:creator>Database Insights</dc:creator>
      <pubDate>Tue, 15 Sep 2026 08:45:08 +0000</pubDate>
      <link>https://dev.to/databaseinsights/making-ssms-smarter-building-a-productive-t-sql-workflow-with-snippets-and-code-analysis-h4d</link>
      <guid>https://dev.to/databaseinsights/making-ssms-smarter-building-a-productive-t-sql-workflow-with-snippets-and-code-analysis-h4d</guid>
      <description>&lt;p&gt;Developer productivity is not just about writing code faster, it is about removing the friction around the work.&lt;/p&gt;

&lt;p&gt;In SQL development, that friction often comes from small, repetitive tasks. A developer opens SSMS, starts a stored procedure, and ends up rebuilding a &lt;code&gt;TRY/CATCH&lt;/code&gt; block, transaction handling, logging, or a few common &lt;code&gt;JOIN&lt;/code&gt;s they have already written many times before. Then comes the usual trip to Object Explorer to check whether the column was &lt;code&gt;CustomerId&lt;/code&gt;, &lt;code&gt;CustomerID&lt;/code&gt;, or something slightly different.&lt;/p&gt;

&lt;p&gt;None of this work is especially difficult, but repeating it wastes time. The problem extends well beyond database development. In 2025, Atlassian identified repetitive but necessary engineering tasks as a major opportunity for automation after looking at work across its &lt;a href="https://www.atlassian.com/blog/atlassian-engineering/hula-blog-autodev-paper-human-in-the-loop-software-development-agents" rel="noopener noreferrer"&gt;12,000 engineers&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Developers can obviously write this SQL themselves. The problem is having to rebuild the same structures again and again. The query still needs formatting, aliases need cleaning up, and someone has to catch the &lt;code&gt;SELECT *&lt;/code&gt;, questionable join, or type mismatch before the code gets merged.&lt;/p&gt;

&lt;p&gt;A better T-SQL workflow is about removing repetitive work, standardizing how SQL is written, and catching problems earlier.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the manual SSMS workflow starts to drag
&lt;/h2&gt;

&lt;p&gt;Take a normal stored procedure that pulls order and customer data, updates a status, writes an audit record, and rolls the transaction back if something fails.&lt;/p&gt;

&lt;p&gt;The SQL itself may not be complicated. The workflow around it is.&lt;/p&gt;

&lt;p&gt;The developer has to find the right tables and columns, build the &lt;code&gt;JOIN&lt;/code&gt;s, add transaction handling, write the logging logic, format the script, and then review it for obvious mistakes. If the schema is large or unfamiliar, there is usually some back-and-forth to Object Explorer as well.&lt;/p&gt;

&lt;p&gt;That interruption matters more than the typing.&lt;/p&gt;

&lt;p&gt;Say the developer is writing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OrderId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CustomerName&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Sales&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Orders&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;
&lt;span class="k"&gt;INNER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;Sales&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customers&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;
    &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CustomerId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CustomerId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;They already know the relationship they want. What slows them down is confirming whether the object is &lt;code&gt;Sales.Customers&lt;/code&gt; or &lt;code&gt;CRM.Customers&lt;/code&gt;, whether the key is &lt;code&gt;CustomerId&lt;/code&gt; or &lt;code&gt;CustomerID&lt;/code&gt;, and whether another table has to be joined in first.&lt;/p&gt;

&lt;p&gt;None of those checks is difficult. But every lookup breaks the flow of writing the query.&lt;/p&gt;

&lt;p&gt;The same thing happens with boilerplate. A developer stops solving the actual problem to rebuild a &lt;code&gt;TRY/CATCH&lt;/code&gt; block, transaction wrapper, or logging statement they have written before. Then another developer writes the same pattern slightly differently.&lt;/p&gt;

&lt;p&gt;The problem is not just lost keystrokes any more. Small differences start to creep in with formatting, aliases, error handling and common SQL patterns. Then they appear in code review where someone has to work out what actually changed and what is just inconsistent SQL.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stop rewriting the same SQL
&lt;/h2&gt;

&lt;p&gt;A simple fix is to stop rebuilding patterns the team is already using. There's little point in writing a standard transaction wrapper, logging block or upsert again for every script if one already exists. Save as a snippet and reuse it.&lt;/p&gt;

&lt;p&gt;A transaction wrapper is a good example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt; &lt;span class="n"&gt;TRY&lt;/span&gt;
    &lt;span class="k"&gt;BEGIN&lt;/span&gt; &lt;span class="n"&gt;TRANSACTION&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;-- work goes here&lt;/span&gt;

    &lt;span class="k"&gt;COMMIT&lt;/span&gt; &lt;span class="n"&gt;TRANSACTION&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="n"&gt;TRY&lt;/span&gt;
&lt;span class="k"&gt;BEGIN&lt;/span&gt; &lt;span class="n"&gt;CATCH&lt;/span&gt;
    &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="o"&gt;@@&lt;/span&gt;&lt;span class="n"&gt;TRANCOUNT&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="k"&gt;ROLLBACK&lt;/span&gt; &lt;span class="n"&gt;TRANSACTION&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="n"&gt;THROW&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="n"&gt;CATCH&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://www.devart.com/dbforge/sql/sqlcomplete/" rel="noopener noreferrer"&gt;dbForge SQL Complete&lt;/a&gt; snippets let developers save structures like this once and insert them with a shortcut. Placeholders can be used for the parts that change, while the rest stays fixed.&lt;/p&gt;

&lt;p&gt;The same applies to upserts, temp-table setup, or common procedure templates. The snippet gives everyone the same starting point, so developers are not rebuilding these patterns slightly differently each time.&lt;/p&gt;

&lt;p&gt;It also makes team standards easier to follow. Instead of keeping the preferred pattern in a document somewhere, the approved version is already there in the editor when it is needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Formatting is not just cosmetic
&lt;/h2&gt;

&lt;p&gt;Formatting often gets treated like a style preference, but in practice it affects how quickly someone can read and review the SQL.&lt;/p&gt;

&lt;p&gt;Compare this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OrderId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CustomerName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OrderDate&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;Sales&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Orders&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="k"&gt;inner&lt;/span&gt; &lt;span class="k"&gt;join&lt;/span&gt; &lt;span class="n"&gt;Sales&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customers&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CustomerId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CustomerId&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'Open'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;with this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OrderId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CustomerName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OrderDate&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Sales&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Orders&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;
&lt;span class="k"&gt;INNER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;Sales&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customers&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;
    &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CustomerId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CustomerId&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Open'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both run. The second one is much easier to scan.&lt;/p&gt;

&lt;p&gt;That matters in code review. Consistent indentation, &lt;code&gt;JOIN&lt;/code&gt; layout, aliases, column lists, and keyword casing make the structure obvious before the reviewer even gets into the logic. It also cuts down on formatting-only changes in version-control diffs.&lt;/p&gt;

&lt;p&gt;dbForge SQL Complete can apply formatting profiles for case, whitespace, indentation, wrapping, and line breaks. Teams can use a shared profile instead of leaving every developer to format SQL differently. There is no single perfect SQL style. The useful part is agreeing on one and applying it consistently.&lt;/p&gt;

&lt;h2&gt;
  
  
  Catch questionable SQL before review
&lt;/h2&gt;

&lt;p&gt;Completion and formatting help with writing and readability. They do not tell you whether the SQL itself deserves another look.&lt;/p&gt;

&lt;p&gt;Take:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Sales&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Orders&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It runs, but it also pulls every column, including ones the caller may not need. It can also make result sets harder to control when the schema changes.&lt;/p&gt;

&lt;p&gt;In the Enterprise edition,dbForge SQL Complete's T-SQL Code Analyzer can flag this kind of pattern and point the developer toward an explicit column list.&lt;/p&gt;

&lt;p&gt;Some issues are less obvious.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;DECLARE&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;CustomerCode&lt;/span&gt; &lt;span class="n"&gt;NVARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="s1"&gt;'C10042'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;CustomerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;CustomerName&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Sales&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customers&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;CustomerCode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;CustomerCode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nv"&gt;`&lt;/span&gt;&lt;span class="se"&gt;``&lt;/span&gt;&lt;span class="nv"&gt;

If `&lt;/span&gt;&lt;span class="n"&gt;CustomerCode&lt;/span&gt;&lt;span class="nv"&gt;` is actually `&lt;/span&gt;&lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="nv"&gt;`, SQL Server may need to convert one side of the comparison. Depending on the conversion, that can get in the way of index use and lead to more expensive scans.

JOIN logic can have similar problems. A query may be valid and still use a structure that deserves another look.

Contextual suggestions help earlier in the same workflow. dbForge SQL Complete can suggest tables, columns, aliases, and `&lt;/span&gt;&lt;span class="k"&gt;JOIN&lt;/span&gt;&lt;span class="nv"&gt;` conditions from the schema, so developers spend less time checking names manually and are less likely to reference the wrong object.

The analyzer then gives another layer of feedback. The developer runs it against the script and reviews the warnings, errors, and hints it returns.

That is the right way to treat code analysis: as an early warning system, not a replacement for testing, execution plans, or developer judgment.

## Refactor without playing search-and-replace roulette

SQL change. Aliases get renamed, columns change, and early shortcuts stop making sense.

Say a large script uses:



&lt;/span&gt;&lt;span class="se"&gt;``&lt;/span&gt;&lt;span class="nv"&gt;`&lt;/span&gt;&lt;span class="k"&gt;sql&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Sales&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SalesOrderHeader&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;throughout dozens of references.&lt;/p&gt;

&lt;p&gt;Later, &lt;code&gt;s&lt;/code&gt; is no longer clear enough and the developer wants:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Sales&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SalesOrderHeader&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;soh&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Doing that with search and replace can be risky. The same text may appear in variables, comments, procedure names, or unrelated identifiers. dbForge SQL Complete's Rename feature can update the relevant alias references together and show a preview before the change is applied.&lt;/p&gt;

&lt;p&gt;That matters more as scripts get larger. One small rename can touch a lot of places, and the goal is to change the right references without dragging unrelated text into the edit. Refactoring does not make the decision for the developer. It just makes the change easier to control.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a smarter SSMS workflow looks like
&lt;/h2&gt;

&lt;p&gt;At this point, the pattern is clear. The real gain comes from taking small bits of friction out of the whole SQL workflow, not from one feature doing everything.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Type → look up schema → recreate boilerplate → format manually → inspect → search and replace → review&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;After&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Insert snippet → complete from context → format → analyze → refactor → review&lt;/p&gt;

&lt;p&gt;The gain is not from any one feature. It comes from removing several small interruptions from the same development cycle.&lt;/p&gt;

&lt;p&gt;The developer spends less time retyping standard SQL, checking object names, fixing formatting, and chasing references through a script.&lt;/p&gt;

&lt;p&gt;The team gets more consistent SQL before code review starts. And the codebase benefits because questionable patterns are caught earlier and routine changes rely less on manual text editing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where dbForge Studio for SQL Server fits
&lt;/h2&gt;

&lt;p&gt;dbForge SQL Complete is the natural fit for developers who already work in SSMS and want to improve that workflow without changing environments.&lt;/p&gt;

&lt;p&gt;For teams that want more of the database development work in one place, &lt;a href="https://www.devart.com/dbforge/sql/studio/" rel="noopener noreferrer"&gt;dbForge Studio for SQL Server&lt;/a&gt; supports the same general approach with coding assistance, formatting, refactoring, and broader database development tools built into the IDE.&lt;/p&gt;

&lt;p&gt;So the difference is mostly about where the team wants to work. dbForge SQL Complete improves SSMS directly, while &lt;a href="https://www.devart.com/dbforge/sql/studio/" rel="noopener noreferrer"&gt;dbForge Studio for SQL Server&lt;/a&gt; gives teams a fuller standalone environment built around the same kind of workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway: Make the workflow smarter
&lt;/h2&gt;

&lt;p&gt;Better SQL development is not just about typing faster. The bigger gain is removing the repetitive work around the code.&lt;/p&gt;

&lt;p&gt;Snippets handle boilerplate. Contextual completion cuts down schema lookups. Formatting keeps SQL consistent. Code analysis flags questionable patterns earlier, and refactoring makes larger edits easier to control.&lt;/p&gt;

&lt;p&gt;The developer still owns the logic.&lt;/p&gt;

&lt;p&gt;For teams already working in SSMS, &lt;a href="https://www.devart.com/dbforge/sql/sqlcomplete/" rel="noopener noreferrer"&gt;dbForge SQL Complete&lt;/a&gt; brings those improvements into the environment they already use, without forcing a change in tools.&lt;/p&gt;

</description>
      <category>sql</category>
      <category>database</category>
      <category>sqlserver</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Standardizing T-SQL Formatting and Style Across a Team</title>
      <dc:creator>Database Insights</dc:creator>
      <pubDate>Mon, 17 Aug 2026 16:07:06 +0000</pubDate>
      <link>https://dev.to/databaseinsights/standardizing-t-sql-formatting-and-style-across-a-team-48lp</link>
      <guid>https://dev.to/databaseinsights/standardizing-t-sql-formatting-and-style-across-a-team-48lp</guid>
      <description>&lt;p&gt;For years, T-SQL formatting was treated as a matter of personal preference. Developers could write keywords in uppercase or lowercase, align every JOIN or not, and get away with it. That was possible because formatting has never affected how SQL Server executes a query. As long as the SQL worked, how it looked was largely considered a matter of taste. &lt;/p&gt;

&lt;p&gt;That's becoming harder to justify today. SQL codebases are bigger, they live longer, and more people work on them. A stored procedure that starts with one developer will probably be read, reviewed, and modified by several others over its lifetime. And since developers spend between &lt;a href="https://arxiv.org/pdf/2410.21990" rel="noopener noreferrer"&gt;58% and 70%&lt;/a&gt; of their time understanding existing code, inconsistent formatting becomes more than just a cosmetic issue. &lt;/p&gt;

&lt;h2&gt;
  
  
  Where inconsistent T-SQL style costs teams the most
&lt;/h2&gt;

&lt;p&gt;The impact of inconsistent formatting usually shows up during code review. Reviewers only have so much attention to spend on a pull request, and every minute spent looking at formatting is a minute they aren't spending on the SQL itself. &lt;/p&gt;

&lt;p&gt;When everyone on a team formats SQL differently, every change carries a little extra work for the reviewer. Part of the review becomes figuring out which changes affect the logic and which only affect the formatting. A one-line fix can also reformat dozens of existing lines because a different editor or formatter was used. That makes the pull request larger and the real change harder to spot. &lt;/p&gt;

&lt;p&gt;Keeping reviews focused matters. SmartBear's study of 2,500 Cisco code reviews found reviewers catch the most defects when reviews stay between &lt;a href="https://smartbear.com/learn/code-review/best-practices-for-peer-code-review/" rel="noopener noreferrer"&gt;200 and 400 lines&lt;/a&gt;. Formatting churn makes reviews bigger without changing what the code actually does. &lt;/p&gt;

&lt;p&gt;The same thing happens during maintenance. When every file has a different style, developers spend time adjusting to the formatting before they can focus on the logic. Each difference is small, but together they add friction to every maintenance task. &lt;/p&gt;

&lt;p&gt;That's why formatting matters. A consistent style helps developers spend more time understanding SQL and less time understanding how it was written. &lt;/p&gt;

&lt;h2&gt;
  
  
  Define a team T-SQL style guide
&lt;/h2&gt;

&lt;p&gt;Automation only works when everyone agrees on the rules. Without a written standard, a formatter or linter simply enforces one developer's preferences instead of the team's. The guide doesn't need to be long. It only needs to capture the decisions developers would otherwise make differently. &lt;/p&gt;

&lt;p&gt;Start with the formatting rules. They're usually the easiest to agree on and the easiest to automate. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Keyword casing.&lt;/strong&gt; Pick one (uppercase keywords (SELECT, FROM, WHERE) against lowercase identifiers is the usual call) and hold it everywhere. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;JOIN and ON formatting.&lt;/strong&gt; Decide whether each join opens a new line and where its ON clause sits. Nothing else pays off in readability quite as fast. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Indentation and line breaks.&lt;/strong&gt; Settle tabs or spaces, a width, and where long clauses wrap. This helps eliminate unnecessary whitespace-only changes across the codebase. &lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then document the coding conventions that should be consistent across the codebase. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fooa40gslfcaezn0m6le3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fooa40gslfcaezn0m6le3.png" alt=" " width="799" height="246"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The specific choices matter less than making them once and applying them consistently. GitLab, for example, publishes its &lt;a href="https://handbook.gitlab.com/handbook/enterprise-data/platform/sql-style-guide/" rel="noopener noreferrer"&gt;SQL style guide&lt;/a&gt; and enforces it through linting and code review. A written standard defines the target. Enforcing it consistently is the next challenge. &lt;/p&gt;

&lt;h2&gt;
  
  
  Automate formatting in SSMS or dbForge Studio
&lt;/h2&gt;

&lt;p&gt;A written style guide defines the standard. The next step is making sure everyone follows it. &lt;/p&gt;

&lt;p&gt;Manual enforcement does not last. Reviewers have bigger things to worry about than whitespace, and most people aren't going to send a pull request back because a JOIN is on the wrong line. After a few weeks, the codebase starts drifting again. &lt;/p&gt;

&lt;p&gt;The easiest way to stop that drift is to let a formatter apply the team's rules automatically. Most SQL development tools support configurable formatting profiles. Once the team agrees on a standard, formatting becomes a single command instead of something reviewers have to check by hand. &lt;/p&gt;

&lt;p&gt;For example, &lt;a href="https://www.devart.com/dbforge/sql/sqlcomplete/" rel="noopener noreferrer"&gt;dbForge SQL Complete&lt;/a&gt; lets you define a formatting profile that controls keyword casing, indentation, line breaks, whitespace, and wrapping. Developers simply format the file before committing it. &lt;/p&gt;

&lt;p&gt;Before&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="err"&gt;&lt;/span&gt;&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;orderid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CustomerName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;oi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;qty&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;oi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt; 

&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;Orders&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="k"&gt;join&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;custid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; 

&lt;span class="k"&gt;LEFT&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;orderitems&lt;/span&gt; &lt;span class="n"&gt;oi&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="n"&gt;oi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;orderid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;orderid&lt;/span&gt; 

&lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'shipped'&lt;/span&gt; &lt;span class="k"&gt;group&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;orderid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CustomerName&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;    &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 

          &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customer_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 

          &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;oi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;qty&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;oi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt; 

&lt;span class="k"&gt;FROM&lt;/span&gt;      &lt;span class="n"&gt;orders&lt;/span&gt;      &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt; 

&lt;span class="k"&gt;JOIN&lt;/span&gt;      &lt;span class="n"&gt;customers&lt;/span&gt;   &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;  &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cust_id&lt;/span&gt; 

&lt;span class="k"&gt;LEFT&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;order_items&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;oi&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;oi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt; 

&lt;span class="k"&gt;WHERE&lt;/span&gt;     &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'shipped'&lt;/span&gt; 

&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt;  &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 

          &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customer_name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The profile can be shared with the rest of the team, so everyone formats SQL the same way. Instead of relying on memory, the tool applies the agreed standard every time. If you don't want to spend time creating a custom formatting profile, you can simply choose one of the predefined formatting profiles that best matches your coding style. &lt;/p&gt;

&lt;p&gt;Cleaning up existing code is usually the harder part. dbForge SQL Complete includes a Formatter Wizard that applies the same profile across files or directories, making it practical to standardize an existing codebase. When a section needs to keep its manual layout, --noformat and --endnoformat leave it untouched. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvftdh4m5o7mnfmx7hjke.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvftdh4m5o7mnfmx7hjke.png" alt=" " width="800" height="603"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;SSMS 22 showing a SQL query editor with the dbForge SQL Complete context menu open and the Format Document command highlighted for SQL code formatting.&lt;/em&gt; &lt;/p&gt;

&lt;p&gt;Many teams also run formatting from the command line as part of a pre-commit hook or CI pipeline. At that point, formatting is no longer something reviewers enforce, it becomes part of the build. &lt;/p&gt;

&lt;p&gt;If your team prefers a full IDE, &lt;a href="https://www.devart.com/dbforge/sql/studio/" rel="noopener noreferrer"&gt;dbForge Studio for SQL Server&lt;/a&gt; provides the same approach with shared formatting profiles and bulk formatting. &lt;/p&gt;

&lt;p&gt;The tool matters less than the workflow. Agree on a standard, share it with the team, and automate it so developers don't have to think about it. &lt;/p&gt;

&lt;p&gt;Formatting standardizes how SQL looks. Standardizing how developers solve common SQL problems is the next step. &lt;/p&gt;

&lt;h2&gt;
  
  
  Use snippets and templates to standardize best practices
&lt;/h2&gt;

&lt;p&gt;While formatting tools make SQL look consistent, snippets and templates help make it consistent before the first line is written. &lt;/p&gt;

&lt;p&gt;Instead of starting every stored procedure from scratch, developers start from an approved template. That might be a standard procedure skeleton, a TRY/CATCH block, an upsert pattern, or a logging routine. The goal isn't to save a few keystrokes. It's to start from code the team has already agreed is correct. &lt;/p&gt;

&lt;p&gt;Many SQL development tools support this. For example, dbForge SQL Complete includes a Snippets Manager that lets teams create and share their own snippets. Rather than copying a procedure written last week and hoping it doesn't bring last week's mistakes with it, developers start from the same approved templates every time. &lt;/p&gt;

&lt;p&gt;The snippets can be shared across the team, so new developers start with the same procedure templates, logging patterns, and error handling as everyone else. Features like join autocompletion reinforce the same idea by suggesting JOIN conditions from existing primary and foreign key relationships instead of leaving every developer to write them from scratch. &lt;/p&gt;

&lt;p&gt;The result is that common patterns become the default instead of something reviewers have to ask for later. &lt;/p&gt;

&lt;h2&gt;
  
  
  Roll out the standard without disrupting the team
&lt;/h2&gt;

&lt;p&gt;The goal is less chaos, not more process. If the rollout feels like bureaucracy, people will work around it instead of with it. &lt;/p&gt;

&lt;p&gt;Start with the easy wins: keyword casing, JOIN formatting, and indentation. Those rules eliminate most formatting noise without much debate. Save the more opinionated discussions until the team has already seen the value of working from the same standard. &lt;/p&gt;

&lt;p&gt;Roll the standard out to new and modified code first. Resist the temptation to reformat the entire codebase in one pass. Large formatting commits rewrite blame history and create exactly the kind of oversized diffs that make reviews harder. &lt;/p&gt;

&lt;p&gt;When you do clean up older code, keep formatting and logic separate. Make one commit that only reformats the code and another that changes its behavior. Reviewers can then ignore the formatting commit and focus on the logic, knowing the layout changes didn't introduce bugs. &lt;/p&gt;

&lt;p&gt;Finally, make the standard easy to adopt. Include the style guide, formatting profile, and shared snippets as part of your onboarding process so new developers start with the same conventions from day one. Review the guide occasionally and change rules that create more friction than value. &lt;/p&gt;

&lt;p&gt;A good standard shouldn't make development feel more restrictive. It should remove small decisions so developers can spend more time thinking about the SQL itself. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;Standardizing T-SQL formatting is not about making code look nicer. It's about making SQL easier to read, review, and maintain. A shared style guide, automated formatting, and reusable snippets remove small inconsistencies before they become everyday friction. &lt;/p&gt;

&lt;p&gt;Tools such as dbForge SQL Complete and dbForge Studio for SQL Server help teams put those standards into practice with shared formatting profiles, snippets, and templates. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.devart.com/dbforge/sql/sqlcomplete/download.html" rel="noopener noreferrer"&gt;Download&lt;/a&gt; a free trial of dbForge SQL Complete and standardize formatting, snippets, and coding conventions across your team. &lt;/p&gt;

</description>
      <category>ai</category>
      <category>database</category>
      <category>sql</category>
    </item>
    <item>
      <title>Legacy to Modern: Migrating On-Premise Production Data to Cloud-Based Managed Instances</title>
      <dc:creator>Database Insights</dc:creator>
      <pubDate>Fri, 31 Jul 2026 17:47:05 +0000</pubDate>
      <link>https://dev.to/databaseinsights/legacy-to-modern-migrating-on-premise-production-data-to-cloud-based-managed-instances-4j46</link>
      <guid>https://dev.to/databaseinsights/legacy-to-modern-migrating-on-premise-production-data-to-cloud-based-managed-instances-4j46</guid>
      <description>&lt;p&gt;According to CoreSite’s 2025 State of the Data Center Report, &lt;a href="https://www.coresite.com/blog/ai-shifts-the-balance-of-where-to-host-workloads-the-2025-state-of-the-data-center-report-explains-why" rel="noopener noreferrer"&gt;62%&lt;/a&gt; of organizations now operate hybrid cloud environments. In reality, that often means companies are still stuck running a mix of old and new infrastructure because some systems are too risky, too interconnected, or too operationally fragile to move easily. Production databases usually sit right in the middle of that problem. &lt;/p&gt;

&lt;p&gt;And the hard part is rarely moving the data itself. It is dealing with years of schema drift, undocumented dependencies, rigid infrastructure, and operational workflows that became harder to change safely over time. &lt;/p&gt;

&lt;p&gt;Without a structured migration strategy, teams end up carrying downtime risk, inconsistent environments, rising operational costs, and technical debt straight into the cloud. Successful migration to managed cloud database instances depends on proper assessment, schema modernization, synchronization, automation, validation, and carefully planned low-downtime cutovers. &lt;/p&gt;

&lt;h2&gt;
  
  
  The problem with legacy production database environments
&lt;/h2&gt;

&lt;p&gt;Legacy databases usually seem stable until migration starts. Here are some of the most common problems teams encounter. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fixed infrastructure creates operational drag&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;The infrastructure is usually part of the problem. On-premise databases are sized around expected peak load, which works until workloads change faster than the hardware lifecycle. Teams either over-provision capacity they barely use or accept degraded performance during traffic spikes. That also makes migration planning harder because replication, validation, and parallel test workloads still need to run alongside live production traffic. &lt;/p&gt;

&lt;p&gt;A lot of these environments also depend on aging hardware that becomes harder and more expensive to maintain over time. High availability is often limited by the infrastructure itself, which makes failover testing and disaster recovery planning harder to modernize safely. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Manual workflows slow migration readiness&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;A surprising amount of database work is still handled manually in older environments. Test data preparation is one example. Redgate’s 2025 survey found that 71% of organizations still create test data manually, which becomes a serious problem once teams start preparing for migration rehearsals and cutover validation. &lt;/p&gt;

&lt;p&gt;Routine work like patching, backup validation, and failover testing also becomes harder to coordinate safely, especially in environments with tight uptime requirements. &lt;/p&gt;

&lt;p&gt;The issue is not just speed. Manual workflows make staging environments less reliable because they drift further away from production over time. Teams end up testing migrations against incomplete or inconsistent environments, then discover compatibility issues and broken dependencies much later during cutover. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Schema drift quietly increases migration risk&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;The other migration problem is usually schema drift. In many environments, development, staging, and production stopped matching years ago. Emergency fixes get applied directly in production, stored procedures change without documentation, and dependencies quietly pile up over time. By the time migration planning starts, teams are often working with environments nobody fully understands anymore. &lt;/p&gt;

&lt;p&gt;That is a big reason schema incompatibilities now contribute to up to &lt;a href="https://www.cloudficient.com/blog/10-common-data-migration-challenges-and-how-to-overcome-them" rel="noopener noreferrer"&gt;45%&lt;/a&gt; of migration failures, according to Cloudficient’s 2025 migration analysis. Most teams approach database migration like an infrastructure project. In reality, they are trying to untangle years of operational inconsistency while keeping production systems online. &lt;/p&gt;

&lt;p&gt;That operational pressure is one of the main reasons organizations move toward managed cloud database services in the first place. The goal is not just to change where the database runs, but to reduce the amount of infrastructure work teams carry around it. &lt;/p&gt;

&lt;h2&gt;
  
  
  What managed cloud database services actually change
&lt;/h2&gt;

&lt;p&gt;Moving to a managed database instance changes more than where the database runs. A large part of the operational workload shifts to the provider. Patching, backups, storage scaling, failover, and parts of high availability stop being recurring DBA tasks and become platform responsibilities instead. &lt;/p&gt;

&lt;p&gt;That shift is usually where the biggest value comes from. When Omnissa migrated self-managed SQL Server databases to Amazon RDS for SQL Server, the company reported a &lt;a href="https://aws.amazon.com/blogs/database/how-omnissa-saved-millions-by-migrating-to-amazon-rds-and-amazon-ec2/" rel="noopener noreferrer"&gt;39%&lt;/a&gt; infrastructure cost reduction. But internally, the bigger win was that engineering teams spent less time maintaining infrastructure and more time pushing modernization work forward. &lt;/p&gt;

&lt;p&gt;This is also where the difference between IaaS and fully managed DBaaS becomes important. Putting a database on a cloud VM gives teams elastic compute, but it does not remove operational ownership. A lot of organizations realize too late that they moved the infrastructure without simplifying the operational model around it. Patching, backup management, dependency handling, and HA configuration still stay with the team. &lt;/p&gt;

&lt;p&gt;That is why many organizations eventually move beyond basic IaaS deployments. Otherwise, they often end up carrying the same maintenance workload into the cloud. &lt;/p&gt;

&lt;p&gt;Managed services also introduce tradeoffs teams need to think about early. Provider-specific features can make operations easier, but they can also make future migrations or multi-cloud portability harder once those dependencies become deeply embedded in the environment. &lt;/p&gt;

&lt;p&gt;At the same time, managed services still require active lifecycle management. The platform handles more operational work, but teams still need to keep database engines, dependencies, and configurations current over time. &lt;/p&gt;

&lt;p&gt;Amazon RDS &lt;a href="https://cloudburn.io/blog/amazon-rds-pricing" rel="noopener noreferrer"&gt;extended support&lt;/a&gt; for older engines like MySQL 5.7 and PostgreSQL 11 introduced additional per-vCPU charges in 2024, and many teams quietly accumulated unnecessary costs simply because upgrades were delayed. In practice, modernizing unsupported engine versions is often one of the fastest post-migration cost optimization wins available. &lt;/p&gt;

&lt;p&gt;But before teams reach that stage, the migration path itself has to be realistic. A lot of production migration problems start much earlier during assessment and planning, especially when teams underestimate dependencies, schema drift, or the amount of operational cleanup required before cutover. &lt;/p&gt;

&lt;h2&gt;
  
  
  Migration strategy: Assessment, schema modernization, and the 7 Rs
&lt;/h2&gt;

&lt;p&gt;The 7 Rs framework is commonly used for cloud migrations: rehost, replatform, refactor, repurchase, relocate, retain, and retire. For production database migrations, though, most projects usually fall into three paths: rehosting, replatforming, or refactoring. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4cgq6a36zggwg3cf5c4d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4cgq6a36zggwg3cf5c4d.png" alt=" " width="799" height="245"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The assessment should decide the migration path, not deadline pressure. Teams trying to force large replatforming projects into aggressive timelines usually run into schema and dependency problems halfway through the migration instead of before it. Production environments are rarely as clean as the diagrams suggest. &lt;/p&gt;

&lt;p&gt;A lot of migration issues start during assessment because teams focus too narrowly on the database engine itself and miss the operational complexity around it. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Looking beyond the database engine&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;A lot of assessments also focus too much on the database engine itself. The bigger risk however, it’s usually everything around it: ETL jobs, reporting tools, linked servers, scheduled scripts, old ODBC connections, and internal applications nobody fully mapped anymore. Missing even one dependency can create production issues after cutover, especially in environments where nobody fully knows what is still connected to the database anymore. &lt;/p&gt;

&lt;p&gt;Schema drift makes this worse. In many environments, production stopped matching development years ago. A database restore into a cloud test environment is not a schema assessment. One copies the system. The other identifies compatibility problems, unsupported features, and hidden dependencies before migration starts. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Schema validation and synchronization&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;At that point, manual schema comparison usually stops being realistic. Large migrations need repeatable validation and synchronization workflows that teams can rerun throughout the migration timeline, especially after schema changes or failed test cutovers. Tools like dbForge Schema Compare help automate schema comparison and synchronization across on-premise, cloud, and hybrid SQL Server, MySQL, and PostgreSQL environments, while dbForge Data Compare validates the data itself to catch missing rows, transformed values, or corruption that schema checks alone will miss. &lt;/p&gt;

&lt;p&gt;Once the assessment, validation, and dependency mapping work is done properly, the next challenge becomes reducing production disruption during the actual migration itself. &lt;/p&gt;

&lt;h2&gt;
  
  
  Achieving near-zero-downtime migration: The engineering architecture
&lt;/h2&gt;

&lt;p&gt;Near-zero-downtime migration is not one technique. It is a combination of synchronization, validation, rollback planning, and keeping the live cutover window as small as possible. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CDC and incremental synchronization&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;Most modern migrations rely on Change Data Capture (CDC). The initial bulk transfer runs while production stays live, then CDC keeps the target synchronized by streaming incremental changes from the transaction log. By the time cutover starts, the only thing left is draining the replication lag and switching application traffic to the new environment. For instance, well-prepared Azure SQL Managed Instance migrations can reduce final cutover windows to minutes instead of hours. &lt;/p&gt;

&lt;p&gt;A good real-world example is Airbnb’s MySQL migration to Amazon RDS. The team pre-copied static tables while production traffic was still running, synchronized the remaining changes through binary logs, then cut over once the delta became small enough. Total downtime was around &lt;a href="https://medium.com/airbnb-engineering/mysql-in-the-cloud-at-airbnb-336e5666bc94" rel="noopener noreferrer"&gt;15 minutes&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;During cutover, teams usually monitor replication lag, query latency, connection failures, and application-level error rates closely to confirm the new environment is behaving correctly under live traffic. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Blue/green deployment and rollback planning&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;Blue/green deployment reduces risk further by keeping the existing production environment running alongside the prepared cloud environment until validation is complete. Teams can run smoke tests, benchmark performance, and verify dependencies against live data before switching traffic. If something fails after a cutover, rollback is usually a configuration change instead of a recovery operation. &lt;/p&gt;

&lt;p&gt;One mistake teams still make is treating rollback planning as optional. In practice, rollback plans are often written much later than they should be, usually by teams already under cutover pressure. If rollback cannot be tested before production cutover, the migration is probably not ready yet. High availability and failover configuration should already be in place before traffic moves, not added afterward. &lt;/p&gt;

&lt;p&gt;Once migrations stabilize, the conversation usually shifts from cutover risk to long-term operations. That is where teams start seeing whether the new environment actually reduced operational overhead or simply moved it somewhere else. &lt;/p&gt;

&lt;h2&gt;
  
  
  Cost, security, and long-term optimization after migration
&lt;/h2&gt;

&lt;p&gt;The cost savings from database modernization are real, but they usually do not appear immediately. In the first year, many teams are still running parts of the old environment while stabilizing the new one. The bigger savings tend to show up later, once reserved pricing, autoscaling, storage tiering, and infrastructure consolidation start reducing operational overhead. &lt;/p&gt;

&lt;p&gt;The bigger change is usually operational, not financial. Managed database platforms move patching, backups, failover, encryption, and parts of security governance into the platform itself. That removes a large amount of repetitive infrastructure work from internal teams. &lt;/p&gt;

&lt;p&gt;Managed cloud platforms also simplify disaster recovery planning by automating backup retention, failover orchestration, and cross-region replication workflows that are often maintained manually in on-premise environments. &lt;/p&gt;

&lt;p&gt;Security management also becomes more consistent. Encryption at rest, encryption in transit, IAM integration, and audit logging become standard platform capabilities instead of manually maintained controls.  &lt;/p&gt;

&lt;p&gt;Security also becomes harder to manage once teams start operating across multiple database platforms and cloud environments. That concern is growing quickly too. &lt;/p&gt;

&lt;p&gt;This is also where migration tooling becomes part of the long-term operating model, not just the migration itself. dbForge Schema Compare and &lt;a href="https://www.devart.com/dbforge/sql/datacompare/" rel="noopener noreferrer"&gt;dbForge Data Compare&lt;/a&gt; help teams validate schema and data consistency before cutover, while &lt;a href="https://www.devart.com/dbforge/sql/database-devops/" rel="noopener noreferrer"&gt;dbForge DevOps Automation&lt;/a&gt; integrates schema deployment into CI/CD pipelines to reduce manual deployment coordination after migration.  &lt;/p&gt;

&lt;p&gt;Mixed-engine environments create another operational challenge after migration. Teams often end up managing SQL Server, MySQL, PostgreSQL, and Oracle workloads side by side while also redesigning older ETL pipelines for cloud operation. Platforms like &lt;a href="https://www.devart.com/dbforge/edge/" rel="noopener noreferrer"&gt;dbForge Edge&lt;/a&gt; help reduce some of that operational fragmentation by centralizing database management and cloud-native integration workflows. &lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Legacy production databases rarely fail in one dramatic moment. The bigger problem is the operational weight that builds around them over time: manual deployments, environment drift, patching overhead, hidden dependencies, and engineering teams spending more time maintaining infrastructure than improving it. &lt;/p&gt;

&lt;p&gt;The tooling around database migration is no longer the hard part. Schema comparison, row-level validation, CDC synchronization, and CI/CD-driven deployments are already mature and widely used. The difference is usually how teams approach the migration itself.  &lt;/p&gt;

&lt;p&gt;Organizations that standardize assessment, validation, synchronization, and deployment workflows tend to recover faster after cutover and regain development velocity sooner. Teams that treat cloud migration as a simple infrastructure move usually recreate the same operational problems in a different environment, just with cloud billing attached to them. &lt;/p&gt;

&lt;p&gt;Database modernization is not really about moving servers. It is about building environments that are easier to scale, maintain, validate, and change safely over time. Managed cloud platforms make that possible, but only when the operational workflows around them are modernized too.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
