﻿{"id":244,"date":"2014-10-20T08:30:57","date_gmt":"2014-10-20T07:30:57","guid":{"rendered":"http:\/\/www.codereview.co\/?p=244"},"modified":"2014-10-15T15:04:15","modified_gmt":"2014-10-15T14:04:15","slug":"how-to-drop-and-recreate-sql-server-foreign-keys","status":"publish","type":"post","link":"http:\/\/www.codereview.co\/index.php\/sql-server-tutorials\/how-to-drop-and-recreate-sql-server-foreign-keys\/","title":{"rendered":"How to drop and recreate SQL Server foreign keys?"},"content":{"rendered":"<p><span style=\"color: #000000;\"><strong>Scenario<\/strong><\/span><\/p>\n<p><span style=\"color: #000000;\">You may have been in a scenario where you needed to quickly generate a script to drop and then subsequently re-create all of the foreign keys in a database (or in a specific schema, or matching a specific naming scheme). In some situations you can simply disable and re-enable the constraints, which isn&#8217;t all that complex at all. In other cases (say, you want to truncate all tables), you actually need to drop and re-create the constraints.<\/span><\/p>\n<p><span style=\"color: #000000;\">Regardless of the underlying purpose, this is rather tedious to do in Management Studio, since there is no top-level &#8220;Foreign Keys&#8221; node in the Object Explorer tree &#8211; otherwise you could just select multiple items in Object Explorer Details, right-click, and be on your way.<\/span><\/p>\n<p><span style=\"color: #000000;\">When you start thinking about how to solve this problem, and there are existing tips that do offer solutions already, your first thought is probably: &#8220;I&#8217;ll just use a cursor against sys.foreign_keys and build the scripts dynamically!&#8221; Then you realize that some of your foreign key constraints are comprised of more than one column &#8211; certainly an often and understandably unforeseen complication. This definitely throws a wrench in your plans, as now it&#8217;s a nested cursor: one to loop through all the constraints, and then for each constraint, a loop for the 1-n columns referenced.<\/span><\/p>\n<p><span style=\"color: #000000;\"><strong>\u00a0<\/strong><\/span><\/p>\n<p><span style=\"color: #000000;\"><strong>Solution<\/strong><\/span><\/p>\n<p><span style=\"color: #000000;\">I have what I think is a better way than trying to write convoluted and nested cursors, and no, it doesn&#8217;t involve PowerShell. (That&#8217;s not saying PowerShell is a bad approach for this kind of problem, and I invite you to share your solutions from that angle. I&#8217;m just trying to stay within the database here.)<\/span><\/p>\n<p><span style=\"color: #000000;\">I&#8217;ve recently blogged about the FOR XML PATH() approach to grouped concatenation but I didn&#8217;t really get into any real, practical solutions, like this one, in those posts.<\/span><\/p>\n<p><span style=\"color: #000000;\">I have grown quite fond of using this method to solve problems like this, where I can eliminate tedious and repetitive cursor code and\/or while loops. Note that this shift is not in the name of performance &#8211; after all, in most cases, it is unimportant whether this specific task is accomplished in 8.7 seconds or 11.2 seconds. It doesn&#8217;t end up being any simpler either, really, but it sure is less boring to come up with a working solution that covers all edge cases.<\/span><\/p>\n<p><span style=\"color: #000000;\">The code below generates two separate sets of commands: one to drop all foreign key constraints, and one to create them again. These scripts are stored in a table so that, if you drop the constraints and then disaster of some kind strikes during the create, you still have everything handy and can troubleshoot if needed &#8211; including extracting the scripts for all the constraints that haven&#8217;t yet run, but aren&#8217;t causing any issues otherwise.<\/span><\/p>\n<p><span style=\"color: #000000;\"><em>CREATE TABLE #x &#8212; feel free to use a permanent table<br \/>\n(<br \/>\ndrop_script NVARCHAR(MAX),<br \/>\ncreate_script NVARCHAR(MAX)<br \/>\n);<\/em><\/span><\/p>\n<p>DECLARE @drop\u00a0\u00a0 NVARCHAR(MAX) = N&#8221;,<br \/>\n@create NVARCHAR(MAX) = N&#8221;;<\/p>\n<p>&#8212; drop is easy, just build a simple concatenated list from sys.foreign_keys:<br \/>\nSELECT @drop += N&#8217;<br \/>\nALTER TABLE &#8216; + QUOTENAME(cs.name) + &#8216;.&#8217; + QUOTENAME(ct.name)<br \/>\n+ &#8216; DROP CONSTRAINT &#8216; + QUOTENAME(fk.name) + &#8216;;&#8217;<br \/>\nFROM sys.foreign_keys AS fk<br \/>\nINNER JOIN sys.tables AS ct<br \/>\nON fk.parent_object_id = ct.[object_id]<br \/>\nINNER JOIN sys.schemas AS cs<br \/>\nON ct.[schema_id] = cs.[schema_id];<\/p>\n<p>INSERT #x(drop_script) SELECT @drop;<\/p>\n<p>&#8212; create is a little more complex. We need to generate the list of<br \/>\n&#8212; columns on both sides of the constraint, even though in most cases<br \/>\n&#8212; there is only one column.<br \/>\nSELECT @create += N&#8217;<br \/>\nALTER TABLE &#8216;<br \/>\n+ QUOTENAME(cs.name) + &#8216;.&#8217; + QUOTENAME(ct.name)<br \/>\n+ &#8216; ADD CONSTRAINT &#8216; + QUOTENAME(fk.name)<br \/>\n+ &#8216; FOREIGN KEY (&#8216; + STUFF((SELECT &#8216;,&#8217; + QUOTENAME(c.name)<br \/>\n&#8212; get all the columns in the constraint table<br \/>\nFROM sys.columns AS c<br \/>\nINNER JOIN sys.foreign_key_columns AS fkc<br \/>\nON fkc.parent_column_id = c.column_id<br \/>\nAND fkc.parent_object_id = c.[object_id]<br \/>\nWHERE fkc.constraint_object_id = fk.[object_id]<br \/>\nORDER BY fkc.constraint_column_id<br \/>\nFOR XML PATH(N&#8221;), TYPE).value(N&#8217;.[1]&#8217;, N&#8217;nvarchar(max)&#8217;), 1, 1, N&#8221;)<br \/>\n+ &#8216;) REFERENCES &#8216; + QUOTENAME(rs.name) + &#8216;.&#8217; + QUOTENAME(rt.name)<br \/>\n+ &#8216;(&#8216; + STUFF((SELECT &#8216;,&#8217; + QUOTENAME(c.name)<br \/>\n&#8212; get all the referenced columns<br \/>\nFROM sys.columns AS c<br \/>\nINNER JOIN sys.foreign_key_columns AS fkc<br \/>\nON fkc.referenced_column_id = c.column_id<br \/>\nAND fkc.referenced_object_id = c.[object_id]<br \/>\nWHERE fkc.constraint_object_id = fk.[object_id]<br \/>\nORDER BY fkc.constraint_column_id<br \/>\nFOR XML PATH(N&#8221;), TYPE).value(N&#8217;.[1]&#8217;, N&#8217;nvarchar(max)&#8217;), 1, 1, N&#8221;) + &#8216;);&#8217;<br \/>\nFROM sys.foreign_keys AS fk<br \/>\nINNER JOIN sys.tables AS rt &#8212; referenced table<br \/>\nON fk.referenced_object_id = rt.[object_id]<br \/>\nINNER JOIN sys.schemas AS rs<br \/>\nON rt.[schema_id] = rs.[schema_id]<br \/>\nINNER JOIN sys.tables AS ct &#8212; constraint table<br \/>\nON fk.parent_object_id = ct.[object_id]<br \/>\nINNER JOIN sys.schemas AS cs<br \/>\nON ct.[schema_id] = cs.[schema_id]<br \/>\nWHERE rt.is_ms_shipped = 0 AND ct.is_ms_shipped = 0;<\/p>\n<p>UPDATE #x SET create_script = @create;<\/p>\n<p>PRINT @drop;<br \/>\nPRINT @create;<\/p>\n<p>\/*<br \/>\nEXEC sp_executesql @drop<br \/>\n&#8212; clear out data etc. here<br \/>\nEXEC sp_executesql @create;<br \/>\n*\/<\/p>\n<h2><span style=\"color: #000000;\"><em>Conclusion<\/em><\/span><\/h2>\n<p><span style=\"color: #000000;\">I will be the first to admit: the script is a lot to digest. However, before trying to completely reverse engineer all of the logic on first glance, I urge you to try this code (with the EXEC lines still commented out of course) in your hairiest, most complex schemas. Please let me know if you have a scenario where you find any discrepancies in the comments section below.<\/span><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"color: #000000;\"><strong>Thanks for reading this article,<\/strong><\/span><\/p>\n<p><span style=\"color: #000000;\"><strong>Next steps :<\/strong><\/span><\/p>\n<ol>\n<li><span style=\"color: #000000;\"><strong>Add this script to your database toolkit<\/strong><\/span><\/li>\n<li><span style=\"color: #000000;\"><strong>Share this with your colleagues because Sharing\u00a0 is Learning<\/strong><\/span><\/li>\n<li><span style=\"color: #000000;\"><strong>Comment below if you need any assistance<\/strong><\/span><\/li>\n<\/ol>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Scenario You may have been in a scenario where you needed to quickly generate a script to drop and then subsequently re-create all of the foreign keys in a database (or in a specific schema, or matching a specific naming scheme). In some situations you can simply disable and re-enable the constraints, which isn&#8217;t all [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[32,3],"tags":[44,41,42,45,13,12],"_links":{"self":[{"href":"http:\/\/www.codereview.co\/index.php\/wp-json\/wp\/v2\/posts\/244"}],"collection":[{"href":"http:\/\/www.codereview.co\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.codereview.co\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.codereview.co\/index.php\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"http:\/\/www.codereview.co\/index.php\/wp-json\/wp\/v2\/comments?post=244"}],"version-history":[{"count":2,"href":"http:\/\/www.codereview.co\/index.php\/wp-json\/wp\/v2\/posts\/244\/revisions"}],"predecessor-version":[{"id":374,"href":"http:\/\/www.codereview.co\/index.php\/wp-json\/wp\/v2\/posts\/244\/revisions\/374"}],"wp:attachment":[{"href":"http:\/\/www.codereview.co\/index.php\/wp-json\/wp\/v2\/media?parent=244"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.codereview.co\/index.php\/wp-json\/wp\/v2\/categories?post=244"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.codereview.co\/index.php\/wp-json\/wp\/v2\/tags?post=244"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}