{"id":141,"date":"2016-01-12T00:00:00","date_gmt":"2013-01-01T00:00:00","guid":{"rendered":"https:\/\/www.sqlhammer.com\/t-sql-tuesday-74-be-the-change"},"modified":"2026-03-18T21:52:21","modified_gmt":"2026-03-18T21:52:21","slug":"t-sql-tuesday-74-be-the-change","status":"publish","type":"post","link":"https:\/\/sqlhammer.com\/index.php\/2016\/01\/12\/t-sql-tuesday-74-be-the-change\/","title":{"rendered":"T-SQL Tuesday #74 \u2013 Be the change (MERGE static data)"},"content":{"rendered":"<p><img decoding=\"async\" src=\"\/wp-content\/uploads\/TSQL2sDay150x150-2.jpg\" alt=\"\" \/><\/p>\n<p>Each month, on the first Tuesday of the month, the announcement for the blog party T-SQL Tuesday comes out. Those that are interested then post their blogs, on the subject selected, on the second Tuesday of the month. If you\u2019ve never heard of T-SQL Tuesday it\u2019s a blog party started by Adam Machanic (<a href=\"http:\/\/sqlblog.com\/blogs\/adam_machanic\/\">b<\/a>\/<a href=\"https:\/\/twitter.com\/AdamMachanic\">t<\/a>) over 6 years ago. Robert Davis (<a href=\"http:\/\/www.sqlsoldier.com\/wp\/sqlserver\/tsqltuesday74invitationbethechange\">b<\/a>\/<a href=\"https:\/\/twitter.com\/SQLSoldier\">t<\/a>), our host this month, has chosen a subject of <strong>Be The (data) Change<\/strong>. Specifically data changes.<\/p>\n<p>As my contribution to <a href=\"https:\/\/twitter.com\/search?q=%23tsql2sday\">#tsql2sday<\/a> 74 I will be discussing continuous integration and when static data is not truly static.<\/p>\n<hr \/>\n<p>Applications change over time. This is the nature of an application because the purpose of an application is to serve a need, usually a business need. People and their business\u2019 needs change which provokes the application drift. That is why there is never truly\u00a0<strong><em>static data.<\/strong><\/em>\u00a0Hard-coded values are frowned upon and lookup or dictionary tables are common in databases.<\/p>\n<p>The traditional way of handling lookup value changes would be to directly change them. This could be with an ad hoc script or even a planned release but it was typically a meat bag pressing F5 that kicked everything off.\u00a0Over the years tooling for database development has taken major steps forward and now we are encouraged to put our databases in source control. Sadly, our tools are much more targeted toward scheme changes than data. A\u00a0SQL Server Data Tools (SSDT) database project, such as, does not go very far towards helping us push data changes into our environments.<\/p>\n<h3>Here is what I like to do<\/h3>\n<p>One method that I find works well for keeping your lookup tables up-to-date is to create <a href=\"https:\/\/msdn.microsoft.com\/en-us\/library\/bb510625.aspx\">MERGE<\/a> scripts for your developers to change in source control. Then you create a deployment script which calls the various MERGE statement files immediately before or after the scheme deployment of your database.<\/p>\n<h4>My lookup table<\/h4>\n<pre><code class=\"language-\">CREATE TABLE [Common].[DisbursementType] (\n[disbursementTypeId] SMALLINT IDENTITY (1, 1) NOT NULL,\n[name] VARCHAR (50) NOT NULL,\n[description] VARCHAR (100) NULL,\n[isEnabled] BIT CONSTRAINT [DF_DisbursementType_isEnabled] DEFAULT ((1)) NOT NULL,\n[taxModelValue] VARCHAR (50) CONSTRAINT [DF_DisbursementType_taxModelValue] DEFAULT ('') NOT NULL,\n[rowCreated] DATETIME2 (7) CONSTRAINT [DF_DisbursementType_rowCreated] DEFAULT (sysutcdatetime()) NOT NULL,\n[rowCreatedUserId] UNIQUEIDENTIFIER NOT NULL,\n[rowLastUpdated] DATETIME2 (7) CONSTRAINT [DF_DisbursementType_rowLastUpdated] DEFAULT (sysutcdatetime()) NOT NULL,\n[rowLastUpdatedUserId] UNIQUEIDENTIFIER NOT NULL,\nCONSTRAINT [PK_DisbursementType] PRIMARY KEY CLUSTERED ([disbursementTypeId] ASC) ON [Common]\n);<\/code><\/pre>\n<h4>My MERGE command<\/h4>\n<pre><code class=\"language-\">--:SETVAR userAdmin '00000000-0000-0000-0000-000000000000'\n--:SETVAR dtm SYSUTCDATETIME()\n\n<p>SET IDENTITY_INSERT Common.DisbursementType ON<\/p>\n\n<p>MERGE INTO Common.DisbursementType AS [target]\nUSING\n(VALUES\n(1, 'Direct Deposit', 'Direct Deposit Refund', 1, 'DirectDeposit'),\n(2, 'Mail Me A Check', 'Mail Me A Refund Check', 1, 'RefundCheck'),\n(3, 'Mail Me A Debit Card', 'Mail Me A Debit For Refund', 1, 'RefundDebitCard'),\n(4, 'Direct Withdrawal', 'Direct Withdrawal For Payment', 1, 'DirectWithdrawal'),\n(5, 'Mail Check', 'Mail Check For Payment', 1, 'SendCheck'),\n(6, 'Credit Card', 'Credit Card For Payment', 1, 'CreditCard')\n)\nAS [source] (disbursementTypeId, name, [description], isEnabled, taxModelValue)\nON [target].disbursementTypeId = [source].disbursementTypeId<\/p>\n\n<p>WHEN MATCHED THEN\nUPDATE SET name = [source].name,\n[description] = [source].[description],\nisEnabled = [source].isEnabled,\ntaxModelValue = [source].taxModelValue,\nrowLastUpdatedUserId = $(userAdmin),\nrowLastUpdated = $(dtm)\nWHEN NOT MATCHED BY TARGET THEN\nINSERT (disbursementTypeId, name, [description], isEnabled, taxModelValue, rowCreated, rowCreatedUserId, rowLastUpdated, rowLastUpdatedUserId)\nVALUES (disbursementTypeId, name, [description], isEnabled, taxModelValue, $(dtm), $(userAdmin), $(dtm), $(userAdmin))\nWHEN NOT MATCHED BY SOURCE THEN\nDELETE;<\/p>\n\n<p>set IDENTITY_INSERT Common.DisbursementType OFF<\/code><\/pre>\n<\/p>\n<p><em>NOTE: You will see some SQLCMD variables in this script. That is because I modified this from a script that I using in an SSDT database project and the deployment process used SQLCMD mode. Those can be removed, if desired.<\/em><\/p>\n<h3>Wrap-up<\/h3>\n<p>This\u00a0method is very simple to use because you only ever have to edit the VALUES section of the script when changing your lookup values. You control the exact identity values so that they match in all environments, and deploy using a\u00a0<em>desired state<\/em> mentality.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Each month, on the first Tuesday of the month, the announcement for the blog party T-SQL Tuesday comes out. Those that are interested then post their blogs, on the subject selected, on the second Tuesday of the month. If you\u2019ve never heard of T-SQL Tuesday it\u2019s a blog party started by Adam Machanic (b\/t) over [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":541,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3,4,6,7,8,9,10],"tags":[],"class_list":["post-141","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-administration","category-alwayson-availability-groups","category-career","category-community","category-continuous-integration-deployment","category-development","category-disaster-recovery"],"_links":{"self":[{"href":"https:\/\/sqlhammer.com\/index.php\/wp-json\/wp\/v2\/posts\/141","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/sqlhammer.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/sqlhammer.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/sqlhammer.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/sqlhammer.com\/index.php\/wp-json\/wp\/v2\/comments?post=141"}],"version-history":[{"count":1,"href":"https:\/\/sqlhammer.com\/index.php\/wp-json\/wp\/v2\/posts\/141\/revisions"}],"predecessor-version":[{"id":403,"href":"https:\/\/sqlhammer.com\/index.php\/wp-json\/wp\/v2\/posts\/141\/revisions\/403"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/sqlhammer.com\/index.php\/wp-json\/wp\/v2\/media\/541"}],"wp:attachment":[{"href":"https:\/\/sqlhammer.com\/index.php\/wp-json\/wp\/v2\/media?parent=141"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/sqlhammer.com\/index.php\/wp-json\/wp\/v2\/categories?post=141"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/sqlhammer.com\/index.php\/wp-json\/wp\/v2\/tags?post=141"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}