<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged t-sql</title>
<link>https://coderlegion.com/tag/t-sql</link>
<description>Powered by Question2Answer</description>
<item>
<title>Cool SQL Server Features You Can Use in Real World.</title>
<link>https://coderlegion.com/12233/cool-sql-server-features-you-can-use-in-real-world</link>
<description>&lt;p&gt;If you ever had to “fix a batch” of invoices, reset workflow statuses, or reprocess failed documents, you know the pain, one mistake and you either update too much, or you update the right rows but lose the evidence of what changed.&lt;/p&gt;
&lt;p&gt;SQL Server has a few features that make these scripts safer, easier to review, and much easier to debug later. Below is a clean demo that you can paste in SSMS and use in your daily work.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SET NOCOUNT ON;
SET XACT_ABORT ON;

BEGIN TRY
    BEGIN TRAN;

    /* 1) Demo table (drop + create) */
    IF OBJECT_ID('dbo.DocumentRepositoryDemo', 'U') IS NOT NULL
        DROP TABLE dbo.DocumentRepositoryDemo;

    CREATE TABLE dbo.DocumentRepositoryDemo
    (
        DocumentRepositoryID bigint IDENTITY(1,1) NOT NULL CONSTRAINT PK_DocumentRepositoryDemo PRIMARY KEY,
        OriginalInvoiceID    varchar(50) NOT NULL,
        WorkflowStatus       int NOT NULL,
        HasResponse          bit NOT NULL,
        IsForwardedTP        bit NOT NULL,
        ModifiedAtUtc        datetime2(3) NULL,
        ModifiedBy           sysname NULL
    );

    /* 2) Seed some fake data */
    INSERT dbo.DocumentRepositoryDemo
        (OriginalInvoiceID, WorkflowStatus, HasResponse, IsForwardedTP, ModifiedAtUtc, ModifiedBy)
    VALUES
        ('7601002721400-318242', 2, 1, 1, SYSUTCDATETIME(), SUSER_SNAME()),
        ('7601002721400-318161', 3, 1, 0, SYSUTCDATETIME(), SUSER_SNAME()),
        ('7601001392038-318127', 4, 0, 1, SYSUTCDATETIME(), SUSER_SNAME()),
        ('7601000777447-967851', 9, 0, 0, SYSUTCDATETIME(), SUSER_SNAME()),
        ('7601000777447-968159', 1, 1, 1, SYSUTCDATETIME(), SUSER_SNAME());

    /* 3) Target list (table variable) */
    DECLARE @InvoiceIds TABLE
    (
        OriginalInvoiceID varchar(50) NOT NULL PRIMARY KEY
    );

    INSERT INTO @InvoiceIds (OriginalInvoiceID)
    VALUES
        ('7601002721400-318242'),
        ('7601002721400-318161'),
        ('7601000777447-968159');

    /* 4) Change tracking (audit table variable) */
    DECLARE @Changed TABLE
    (
        DocumentRepositoryID bigint,
        OriginalInvoiceID    varchar(50),
        OldWorkflowStatus    int,
        NewWorkflowStatus    int,
        OldHasResponse       bit,
        NewHasResponse       bit,
        OldIsForwardedTP     bit,
        NewIsForwardedTP     bit,
        ChangedAtUtc         datetime2(3),
        ChangedBy            sysname
    );

    /* 5) Update + capture before/after with OUTPUT */
    UPDATE dr
        SET dr.WorkflowStatus = 5,
            dr.HasResponse = 0,
            dr.IsForwardedTP = 0,
            dr.ModifiedAtUtc = SYSUTCDATETIME(),
            dr.ModifiedBy = SUSER_SNAME()
    OUTPUT
        deleted.DocumentRepositoryID,
        deleted.OriginalInvoiceID,
        deleted.WorkflowStatus, inserted.WorkflowStatus,
        deleted.HasResponse, inserted.HasResponse,
        deleted.IsForwardedTP, inserted.IsForwardedTP,
        inserted.ModifiedAtUtc,
        inserted.ModifiedBy
    INTO @Changed
    (
        DocumentRepositoryID,
        OriginalInvoiceID,
        OldWorkflowStatus, NewWorkflowStatus,
        OldHasResponse, NewHasResponse,
        OldIsForwardedTP, NewIsForwardedTP,
        ChangedAtUtc,
        ChangedBy
    )
    FROM dbo.DocumentRepositoryDemo dr
    INNER JOIN @InvoiceIds i
        ON i.OriginalInvoiceID = dr.OriginalInvoiceID
    WHERE dr.WorkflowStatus &amp;lt;&amp;gt; 5
       OR dr.HasResponse &amp;lt;&amp;gt; 0
       OR dr.IsForwardedTP &amp;lt;&amp;gt; 0;

    /* 6) Results */
    SELECT *
    FROM @Changed
    ORDER BY OriginalInvoiceID, DocumentRepositoryID;

    SELECT COUNT(*) AS RowsUpdated
    FROM @Changed;

    COMMIT TRAN
END TRY
BEGIN CATCH
    IF XACT_STATE() &amp;lt;&amp;gt; 0
        ROLLBACK TRAN
    THROW
END CATCH
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;What makes this “SQL Server cool” (and why it matters)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;SQL Server normally prints messages like “(1 row affected)” after every statement. That is fine in ad hoc work, but noisy in scripts that do multiple steps.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;With SET NOCOUNT ON:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;result sets stay clean (only the output you intentionally select),&lt;/li&gt;
&lt;li&gt;less chatter between server and client,&lt;/li&gt;
&lt;li&gt;stored procedures often become easier to consume from apps and jobs.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;SET XACT_ABORT ON&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;With XACT_ABORT ON, when a runtime error happens (conversion, constraint violation, etc.), SQL Server forces the transaction into a state where it cannot silently continue. In practice, this reduces the chance you end up with a transaction left open, or partially executed logic that you forget to undo.&lt;/p&gt;
&lt;p&gt;It pairs perfectly with TRY/CATCH because it pushes you toward “all or nothing”.&lt;/p&gt;
&lt;p&gt;3) TRY/CATCH + XACT_STATE(), predictable rollback logic&lt;/p&gt;
&lt;p&gt;XACT_STATE() helps because it tells you the transaction state:&lt;/p&gt;
&lt;p&gt; &lt;strong&gt;1 there is an active, committable transaction&lt;br&gt;
-1 the transaction is uncommittable, you must rollback &lt;br&gt;
 0 no active transaction&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;That is why this pattern is so common:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;IF XACT_STATE() &amp;lt;&amp;gt; 0
    ROLLBACK TRAN;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;4) Table variables as “scoped in memory tables”&lt;/p&gt;
&lt;p&gt;This script uses two table variables:&lt;/p&gt;
&lt;p&gt;@InvoiceIds is a safe target list&lt;/p&gt;
&lt;p&gt;@Changed is an audit collector&lt;/p&gt;
&lt;p&gt;Why that’s useful:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;you avoid long IN (...) lists inside the update,&lt;/li&gt;
&lt;li&gt;your target list can be validated and deduped (the PRIMARY KEY&lt;br&gt;
prevents duplicates),&lt;/li&gt;
&lt;li&gt;you keep the update set based, readable, and reviewable.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Important detail for accuracy: table variables are often called “in memory”, but they can spill to tempdb depending on size and query plan. Still, they are great for small and medium sets.&lt;/p&gt;
&lt;p&gt;5) OUTPUT deleted, inserted, instant before/after audit&lt;/p&gt;
&lt;p&gt;This is one of the most underrated features for operational work.&lt;/p&gt;
&lt;p&gt;OUTPUT gives you:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;deleted.* values before the update &lt;/li&gt;
&lt;li&gt;inserted.* values after the update&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So you can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;show exactly what changed, &lt;/li&gt;
&lt;li&gt;store it into @Changed, optionally persist&lt;/li&gt;
&lt;li&gt;it into a real audit table.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is the difference between “I ran a script” and “I can prove exactly what the script did”.&lt;/p&gt;
&lt;p&gt;Takeaways : &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;NOCOUNT keeps scripts readable and reduces noise.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;XACT_ABORT makes failure behavior safer.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;TRY/CATCH plus XACT_STATE() gives you consistent rollback behavior.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Table variables make targeting and audit capture clean and scoped.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;OUTPUT gives you real auditing with almost no extra effort.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Avoiding unnecessary updates is a performance win and a safety win.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/12233/cool-sql-server-features-you-can-use-in-real-world</guid>
<pubDate>Fri, 27 Feb 2026 08:17:57 +0000</pubDate>
</item>
</channel>
</rss>