{"id":186,"date":"2018-01-29T00:00:00","date_gmt":"2013-01-01T00:00:00","guid":{"rendered":"https:\/\/www.sqlhammer.com\/linked-server-elimination"},"modified":"2026-03-18T21:53:11","modified_gmt":"2026-03-18T21:53:11","slug":"linked-server-elimination","status":"publish","type":"post","link":"https:\/\/sqlhammer.com\/index.php\/2018\/01\/29\/linked-server-elimination\/","title":{"rendered":"Linked Server Elimination &#8211; SQL Hammer"},"content":{"rendered":"<p>SQL Server has a feature for <a href=\"https:\/\/docs.microsoft.com\/en-us\/sql\/relational-databases\/partitions\/partitioned-tables-and-indexes\">partitioning tables and indexes<\/a>. Partitioning can be implemented at many levels, however. You can create multiple tables for one logical data set, you can split the set into multiple databases, and you can even split it among different servers. <a href=\"https:\/\/docs.microsoft.com\/en-us\/azure\/architecture\/patterns\/sharding\">Sharding<\/a> is another term. It refers to partitioning data to horizontally scale out compute resources and storage.<\/p>\n<p>There are different methods of handling sharding. Each of them need a central point of connection to handle querying the data on the other shards. This is typically called the control node. The method I am to discuss today is one where linked servers are used to connect the various shards.<\/p>\n<p><a href=\"https:\/\/sqlhammer.com\/?attachment_id=3978\"><img decoding=\"async\" src=\"https:\/\/i0.wp.com\/www.sqlhammer.com\/wp-content\/plugins\/a3-lazy-load\/assets\/images\/lazy_placeholder.gif?resize=645%2C826&#038;ssl=1\" alt=\"\" \/><img decoding=\"async\" src=\"https:\/\/i1.wp.com\/www.sqlhammer.com\/wp-content\/uploads\/2018\/01\/Sharding_1.png?resize=645%2C826&#038;ssl=1\" alt=\"\" \/><\/a><\/p>\n<p>It is always important to expend the least amount of resources with any query to a database. With this architecture, eliminating nodes from each query execution is an easy and effective means of limiting the processing cost of a query.<\/p>\n<h2>Demonstration<\/h2>\n<p>This view <a href=\"https:\/\/docs.microsoft.com\/en-us\/sql\/t-sql\/language-elements\/set-operators-union-transact-sql\">UNIONs<\/a> the dbo.Sales table on the control node and two remote shards. The view explicitly defines the id ranges which exist on each shard. This leverages SQL Server\u2019s <a href=\"http:\/\/www.benjaminnevarez.com\/2011\/06\/the-query-optimizer-and-contradiction-detection\/\">contradiction detection<\/a> to drop remote servers calls from the query, when those ids are not required for the query output.<\/p>\n<p><em>Click <a href=\"https:\/\/sqlhammer.com\/wp-content\/uploads\/2018\/01\/LinkedServerElimination-20180128.zip\">here<\/a> to download the full setup script with create database statements, object creation, and sample data.<\/em><\/p>\n<pre><code class=\"language-\">DROP VIEW IF EXISTS dbo.vwSales;\nGO\nCREATE VIEW dbo.vwSales\nAS\nSELECT id, amount\nFROM [LinkedServerElimination].dbo.Sales\nWHERE id &lt;= 20\nUNION ALL\nSELECT id, amount\nFROM [.\\SQL2017_1].[LinkedServerElimination].dbo.Sales\nWHERE id &gt; 20 AND id &lt;= 40\nUNION ALL\nSELECT id, amount\nFROM [.\\SQL2016].[LinkedServerElimination].dbo.Sales\nWHERE id &gt; 40\nGO<\/code><\/pre>\n<p>With this view, we can see the linked server elimination in action by tracing the connections on each shard and verifying that no activity occurs on shards which do not have the necessary Sales ids. That method is the most complete but not as easy to visualize. A better picture of what is happen can be seen with the execution plans. Below are the actual execution plans on freshly recompiled versions of the same query, with different parameter inputs.<\/p>\n<p>This query spans all three shards and produces a plan with two remote server calls and the self-reference on the control node. This is meant to show that the view can cover the full scope of data and to call out the cost of each remote server call.<\/p>\n<pre><code class=\"language-\">DECLARE @min_id TINYINT = 0\nDECLARE @max_id TINYINT = 100\n\n<p>SELECT <em>\nFROM dbo.vwSales s\nWHERE s.id &gt;= @min_id\nAND s.id &lt;= @max_id<\/code><\/pre>\n<\/p>\n<p><a href=\"https:\/\/sqlhammer.com\/?attachment_id=3979\"><img decoding=\"async\" src=\"https:\/\/i0.wp.com\/www.sqlhammer.com\/wp-content\/plugins\/a3-lazy-load\/assets\/images\/lazy_placeholder.gif?resize=645%2C308&#038;ssl=1\" alt=\"\" \/><img decoding=\"async\" src=\"https:\/\/i1.wp.com\/www.sqlhammer.com\/wp-content\/uploads\/2018\/01\/Linked-Server-Elimination-plan-1.png?resize=645%2C308&#038;ssl=1\" alt=\"\" \/><\/a><\/p>\n<p>Next, the query only references a small number of Sales ids, which all live on the local node\u2019s dbo.Sales table. Therefore, no remote server calls are required.<\/p>\n<pre><code class=\"language-\">DECLARE @min_id TINYINT = 0\nDECLARE @max_id TINYINT = 10\n\n<p>SELECT <\/em>\nFROM dbo.vwSales s\nWHERE s.id &gt;= @min_id\nAND s.id &lt;= @max_id<\/code><\/pre>\n<\/p>\n<p><a href=\"https:\/\/sqlhammer.com\/?attachment_id=3980\"><img decoding=\"async\" src=\"https:\/\/i0.wp.com\/www.sqlhammer.com\/wp-content\/plugins\/a3-lazy-load\/assets\/images\/lazy_placeholder.gif?resize=645%2C144&#038;ssl=1\" alt=\"\" \/><img decoding=\"async\" src=\"https:\/\/i1.wp.com\/www.sqlhammer.com\/wp-content\/uploads\/2018\/01\/Linked-Server-Elimination-plan-2.png?resize=645%2C144&#038;ssl=1\" alt=\"\" \/><\/a><\/p>\n<p>This query spans two nodes, the local node and one remote node.<\/p>\n<pre><code class=\"language-\">DECLARE @min_id TINYINT = 19\nDECLARE @max_id TINYINT = 33\n\n<p>SELECT *\nFROM dbo.vwSales s\nWHERE s.id &gt;= @min_id\nAND s.id &lt;= @max_id<\/code><\/pre>\n<\/p>\n<p><a href=\"https:\/\/sqlhammer.com\/?attachment_id=3981\"><img decoding=\"async\" src=\"https:\/\/i0.wp.com\/www.sqlhammer.com\/wp-content\/plugins\/a3-lazy-load\/assets\/images\/lazy_placeholder.gif?resize=645%2C205&#038;ssl=1\" alt=\"\" \/><img decoding=\"async\" src=\"https:\/\/i0.wp.com\/www.sqlhammer.com\/wp-content\/uploads\/2018\/01\/Linked-Server-Elimination-plan-3.png?resize=645%2C205&#038;ssl=1\" alt=\"\" \/><\/a><\/p>\n<p>There you have it, contradiction detection, <a href=\"https:\/\/www.sqlskills.com\/blogs\/conor\/an-introduction-to-partition-elimination\/\">partition elimination<\/a>, and sharding, in action.<\/p>\n<p>I must admit, however, I used the <a href=\"https:\/\/docs.microsoft.com\/en-us\/sql\/t-sql\/queries\/hints-transact-sql-query\">OPTION (RECOMPILE)<\/a> on all those queries to make sure the plans looked nice for this demonstration. If I had not, the execution plans would all have looked like the one with three legs to the plan. Without the query hint, work would still not occur on unnecessary shards, though. To confirm this, you would have to check the sessions on each shard, as I mentioned above. With that fact, I have technically not demonstrated proof of my claims. This is good because you should never believe anything you read on the internet anyways.<\/p>\n<p>Go test it out for yourself and have fun with partition elimination.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>SQL Server has a feature for partitioning tables and indexes. Partitioning can be implemented at many levels, however. You can create multiple tables for one logical data set, you can split the set into multiple databases, and you can even split it among different servers. Sharding is another term. It refers to partitioning data to [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-186","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/sqlhammer.com\/index.php\/wp-json\/wp\/v2\/posts\/186","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=186"}],"version-history":[{"count":1,"href":"https:\/\/sqlhammer.com\/index.php\/wp-json\/wp\/v2\/posts\/186\/revisions"}],"predecessor-version":[{"id":448,"href":"https:\/\/sqlhammer.com\/index.php\/wp-json\/wp\/v2\/posts\/186\/revisions\/448"}],"wp:attachment":[{"href":"https:\/\/sqlhammer.com\/index.php\/wp-json\/wp\/v2\/media?parent=186"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/sqlhammer.com\/index.php\/wp-json\/wp\/v2\/categories?post=186"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/sqlhammer.com\/index.php\/wp-json\/wp\/v2\/tags?post=186"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}