<?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: Bert Wagner</title>
    <description>The latest articles on DEV Community by Bert Wagner (@bertwagner).</description>
    <link>https://dev.to/bertwagner</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F114657%2F60a7d6b7-267a-4e00-ba64-90c82638cd18.jpg</url>
      <title>DEV Community: Bert Wagner</title>
      <link>https://dev.to/bertwagner</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/bertwagner"/>
    <language>en</language>
    <item>
      <title>Visualizing Hash Match Join Internals And Understanding Their Implications</title>
      <dc:creator>Bert Wagner</dc:creator>
      <pubDate>Wed, 02 Jan 2019 12:00:30 +0000</pubDate>
      <link>https://dev.to/bertwagner/visualizing-hash-match-join-internals-and-understanding-their-implications-3k4b</link>
      <guid>https://dev.to/bertwagner/visualizing-hash-match-join-internals-and-understanding-their-implications-3k4b</guid>
      <description>

&lt;p&gt;&lt;em&gt;This post is part 3 in a series about physical join operators (be sure to check out &lt;a href="https://bertwagner.com/2018/12/11/visualizing-nested-loops-joins-and-understanding-their-implications/"&gt;part 1 – nested loops joins&lt;/a&gt;, and &lt;a href="https://bertwagner.com/2018/12/18/visualizing-merge-join-internals-and-understanding-their-implications/"&gt;part 2 – merge joins&lt;/a&gt;).&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/59C8c7p_hII"&gt; &lt;/iframe&gt;&lt;/p&gt;

&lt;p&gt;Hash Match joins are the dependable workhorses of physical join operators.&lt;/p&gt;

&lt;p&gt;While Nested Loops joins will fail if the data is too large to fit into memory, and Merge Joins require that the input data are sorted, a Hash Match will join any two data inputs you throw at it (as long as the join has an equality predicate and you have enough space in tempdb). &lt;/p&gt;

&lt;p&gt;The base hash match algorithm has two phases that work like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--E_b3ubxp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://bertwagner.com/wp-content/uploads/2018/12/Hash-Match-Join-Looping-1.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--E_b3ubxp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://bertwagner.com/wp-content/uploads/2018/12/Hash-Match-Join-Looping-1.gif" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;During the first “Build” phase, SQL Server builds an in-memory hash table from one of the inputs (typically the smaller of the two).  The hashes are calculated based on the join keys of the input data and then stored along with the row in the hash table under that hash bucket.  Most of the time there is only 1 row of data per hash bucket except when:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;There are rows with duplicate join keys.&lt;/li&gt;
&lt;li&gt;The hashing function produces a collision and totally different join keys receive the same hash (uncommon but possible).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once the hash table is built, SQL Server begins the “Probe” phase.  During this second phase, SQL Server calculates the join key hash for each row in the second input, and checks to see if it exists in the hash table created in the first build phase.  If it finds a match for that hash, it then verifies if the join keys between the row(s) in the hash table and the row from the second table actually match (it needs to perform this verification due to potential hash collisions).&lt;/p&gt;

&lt;p&gt;A common variation on this hash match algorithm occurs when the build phase cannot create a hash table that can be fully stored in memory:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LblXB6m9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://bertwagner.com/wp-content/uploads/2018/12/Hash-Match-Join-spill-looping-1.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LblXB6m9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://bertwagner.com/wp-content/uploads/2018/12/Hash-Match-Join-spill-looping-1.gif" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This happens when the data is larger than what can be stored in memory or when SQL Server grants an inadequate amount of memory required for the hash match join.&lt;/p&gt;

&lt;p&gt;When SQL Server runs doesn’t have enough memory to store the build phase hash table, it proceeds by keeping some of the buckets in memory, while spilling the other buckets to tempdb. &lt;/p&gt;

&lt;p&gt;During the probe phase, SQL Server joins the rows of data from the second input to buckets from the build phase that are in memory. If the bucket that the row potentially matches isn’t currently in memory, SQL Server writes that row to tempdb for later comparison. &lt;/p&gt;

&lt;p&gt;Once the matches for one bucket are complete, SQL Server clears that data from memory and loads the next bucket(s) into memory. It then compares the second input’s rows (currently residing in tempdb) with the new in-memory buckets.&lt;/p&gt;

&lt;p&gt;As with every physical join operator in this series, there are way more details about the hash match operator on &lt;a href="https://sqlserverfast.com/epr/hash-match"&gt;Hugo Kornelis’s reference on hash matches&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Do Hash Match Joins Reveal?
&lt;/h2&gt;

&lt;p&gt;Knowing the internals of how a hash match join works allows us to infer what the optimizer thinks about our data and the join’s upstream operators, helping us focus our performance tuning efforts. &lt;/p&gt;

&lt;p&gt;Here are a few scenarios to consider the next time you see a hash match join being used in your execution plan:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;While hash match joins are able to join huge sets of data, building the hash table from the first input is a blocking operation that will prevent downstream operators from executing. Due to this, I &lt;em&gt;always&lt;/em&gt; check to see if there is an easy way to convert a hash match to either a nested loops or merge join.  Sometimes that won’t be possible (too many rows for nested loops or unsorted data for merge joins) but it’s always worth checking if a simple index change or improved estimates from a statistics update would cause SQL Server to pick a non-blocking hash match join operator. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Hash match joins are great for large joins – since they &lt;em&gt;can&lt;/em&gt; spill to tempdb, it allows them to perform joins on large datasets that would fail an in-memory join with either the nested loops or merge join operators.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Seeing a hash match join operator means SQL Server thinks the upstream inputs are big.  If we know our inputs shouldn’t be that big, then it’s worth checking if we have a stats/estimation problem that is causing SQL Server to choose a hash match join incorrectly.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;

&lt;p&gt;When executed in memory, hash match joins are fairly efficient. Problems arise when the build phase spills to tempdb.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If I notice the little yellow triangle indicating that the join is spilling to tempdb, I take a look to see why: if the data is larger than the server’s available memory, there’s not much that can be done there, but if the memory grant seems unusually small that means we probably have another statistics problem that is providing the SQL Server optimizer estimates that are too low.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Thanks for reading. You might also enjoy &lt;a href="https://twitter.com/bertwagner"&gt;following me on Twitter.&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;


</description>
      <category>sqlserver</category>
      <category>algorithms</category>
      <category>performancetuning</category>
      <category>programming</category>
    </item>
    <item>
      <title>Visualizing Merge Join Internals And Understanding Their Implications</title>
      <dc:creator>Bert Wagner</dc:creator>
      <pubDate>Tue, 18 Dec 2018 12:00:48 +0000</pubDate>
      <link>https://dev.to/bertwagner/visualizing-merge-join-internals-and-understanding-their-implications-26nf</link>
      <guid>https://dev.to/bertwagner/visualizing-merge-join-internals-and-understanding-their-implications-26nf</guid>
      <description>&lt;p&gt;&lt;em&gt;This post is part 2 in a series about physical join operators (be sure to check out &lt;a href="https://dev.to/bertwagner/visualizing-nested-loops-joins-and-understanding-their-implications-130d"&gt;part 1 – nested loops joins&lt;/a&gt;, and &lt;a href="https://bertwagner.com/2019/01/02/visualizing-hash-match-join-internals-and-understanding-their-implications/" rel="noopener noreferrer"&gt;part 3 – hash match joins&lt;/a&gt;).&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/IFUB8iw46RI"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Merge joins are theoretically the fastest* physical join operators available, however they require that data from both inputs is sorted:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbertwagner.com%2Fwp-content%2Fuploads%2F2018%2F12%2FMerge-Join-1.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbertwagner.com%2Fwp-content%2Fuploads%2F2018%2F12%2FMerge-Join-1.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The base algorithm works as follows: SQL Server compares the first rows from both sorted inputs.  It then continues comparing the next rows from the second input as long as the values match the first input’s value.&lt;/p&gt;

&lt;p&gt;Once the values no longer match, SQL Server increments the row of whichever input has the smaller value – it then continues performing comparisons and outputting any joined records. (For more detailed information, be sure to check out &lt;a href="https://blogs.msdn.microsoft.com/craigfr/2006/08/03/merge-join/" rel="noopener noreferrer"&gt;Craig Freedman’s post on merge joins&lt;/a&gt;.)&lt;/p&gt;

&lt;p&gt;This is efficient because in most instances SQL Server never has to go back and read any rows multiple times.  The exception here happens when duplicate values exist in both input tables (or rather, SQL Server doesn’t have meta data available proving that duplicates &lt;em&gt;don’t exist&lt;/em&gt; in both tables) and SQL Server has to perform a many-to-many merge join:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbertwagner.com%2Fwp-content%2Fuploads%2F2018%2F12%2FMerge-Join-many-to-many.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbertwagner.com%2Fwp-content%2Fuploads%2F2018%2F12%2FMerge-Join-many-to-many.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: The image above and the explanation below are “good enough” for understanding this process for practical purposes – if you want to dive into the peek-ahead buffers, optimizations, and other inner workings of this process, I highly recommend reading through &lt;a href="https://sqlserverfast.com/epr/merge-join/" rel="noopener noreferrer"&gt;Hugo Kornelis’s reference on merge joins&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A many-to-many join forces SQL Server to write any duplicated values in the second table into a worktable in tempdb and do the comparisons there.  If those duplicated values are also duplicated in the first table, SQL Server then compares the first table’s values to those already stored in the worktable.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Do Merge Joins Reveal?
&lt;/h2&gt;

&lt;p&gt;Knowing the internals of how a merge join works allows us to infer what the optimizer thinks about our data and the join’s upstream operators, helping us focus our performance tuning efforts. &lt;/p&gt;

&lt;p&gt;Here are a few scenarios to consider the next time you see a merge join being used in your execution plan:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The optimizer chooses to use a merge join when the input data is already sorted or SQL Server can sort the data for a low enough cost.  Additionally, the optimizer is fairly pessimistic at calculating the costs of merge joins (&lt;a href="https://dba.stackexchange.com/a/166054" rel="noopener noreferrer"&gt;great explanation by Joe Obbish&lt;/a&gt;), so if a merge join makes its way into your plans, it probably means that it is fairly efficient. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;While a merge join may be efficient, it’s always worth looking at &lt;strong&gt;why&lt;/strong&gt; the data coming in to the merge join operator is already sorted:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If it’s sorted because the merge join is pulling data directly from an index sorted on your join keys, then there is not much to be concerned about.&lt;/li&gt;
&lt;li&gt;If the optimizer added a sort to the upstream merge join though, it may be worth investigating whether it’s possible to presort that data so SQL Server doesn’t need to sort it on its own.  Often times this can be as simple as redefining an included index column to a key column – if you are adding it as the last key column in the index then regression impact is usually minor but you may be able to allow SQL Server to use the merge join without any additional sorting required.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;If your inputs contain many duplicates, it may be worth checking if a merge join is really the most efficient operator for the join.  As outlined above, many-to-many merge joins require tempdb usage which could become a bottle neck!  &lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;So while merge joins are typically not the high-cost problem spots in your execution plans, it’s always worth investigating upstream operators to see if some additional improvements can be made.&lt;/p&gt;

&lt;p&gt;*NOTE: There are always exceptions to the rule.  Merge joins have the fastest algorithm since each row only needs to be read once from the source inputs.  Also, optimizations occurring in other join operators can give those operators better performance under certain conditions.&lt;/p&gt;

&lt;p&gt;For example, a single row outer table with an indexed inner table using a nested loops join will outperform the same setup with a merge join because of the inner loops joins’ optimizations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DROP TABLE IF EXISTS T1;
GO
CREATE TABLE T1 (Id int identity PRIMARY KEY, Col1 CHAR(1000));
GO

INSERT INTO T1 VALUES('');
GO

DROP TABLE IF EXISTS T2;
GO
CREATE TABLE T2 (Id int identity PRIMARY KEY, Col1 CHAR(1000));
GO

INSERT INTO T2 VALUES('');
GO 100

-- Turn on execution plans and check actual rows for T2
SELECT *
FROM T1 INNER LOOP JOIN T2 ON T1.Id = T2.Id;

SELECT *
FROM T1 INNER MERGE JOIN T2 ON T1.Id = T2.Id;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There might also be instances where inputs with many duplicate records that require worktables may be slower than a nested loop join.&lt;/p&gt;

&lt;p&gt;As I mentioned though, I typically find these types of scenarios to be the exceptions when encountering merge joins in the real-world.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Thanks for reading. You might also enjoy &lt;a href="https://twitter.com/bertwagner" rel="noopener noreferrer"&gt;following me on Twitter.&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>sqlserver</category>
      <category>algorithms</category>
      <category>performancetuning</category>
    </item>
    <item>
      <title>Visualizing Nested Loops Joins And Understanding Their Implications</title>
      <dc:creator>Bert Wagner</dc:creator>
      <pubDate>Tue, 11 Dec 2018 12:00:48 +0000</pubDate>
      <link>https://dev.to/bertwagner/visualizing-nested-loops-joins-and-understanding-their-implications-130d</link>
      <guid>https://dev.to/bertwagner/visualizing-nested-loops-joins-and-understanding-their-implications-130d</guid>
      <description>&lt;h2&gt;
  
  
  What Physical Join Operators Tell Us
&lt;/h2&gt;

&lt;p&gt;Everyone has their own method of reading an execution plan when performance tuning a slow SQL query.  One of the first things I like to look at are what kind of join operators are being used:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbertwagner.com%2Fwp-content%2Fuploads%2F2018%2F12%2Fimage-1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbertwagner.com%2Fwp-content%2Fuploads%2F2018%2F12%2Fimage-1.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These three little icons may not seem like the most obvious place to begin troubleshooting a slow query, but with larger plans especially I like starting with a quick glance at the join operators because they allow you to infer a lot about what SQL Server thinks about your data.&lt;/p&gt;

&lt;p&gt;This will be a three part series where we’ll learn how each join algorithm works and what they can reveal about our upstream execution plan operators.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/0arjvMJihJo"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Nested Loops Join
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbertwagner.com%2Fwp-content%2Fuploads%2F2018%2F12%2FNested-Loop-Join-50fps-1.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbertwagner.com%2Fwp-content%2Fuploads%2F2018%2F12%2FNested-Loop-Join-50fps-1.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nested loops joins work like this: SQL Server takes the first value from our first table (our “outer” table – &lt;a href="https://bertwagner.com/2017/11/21/does-the-join-order-of-my-tables-matter/" rel="noopener noreferrer"&gt;by default SQL Server decides for us which table of the two this will be&lt;/a&gt;), and compares it to every value in our second “inner” table to see if they match. &lt;/p&gt;

&lt;p&gt;Once every inner value has been checked, SQL Server moves to the next value in the outer table and the process repeats until every value from our outer table has been compared to every value in our inner table.&lt;/p&gt;

&lt;p&gt;This description is a worst case example of the performance of a nested loop join.  Several optimizations exist that can make the join more efficient.  For example, if the inner table join values are sorted (because of an index you created or a spool that SQL Server created), SQL Server can process the rows much faster:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbertwagner.com%2Fwp-content%2Fuploads%2F2018%2F12%2Fnest-loops-sorted-50fps.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbertwagner.com%2Fwp-content%2Fuploads%2F2018%2F12%2Fnest-loops-sorted-50fps.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the above animation, SQL Server has the inner input data sorted, allowing it to seek directly to the rows it needs, reducing the total number of comparisons that need to be made.&lt;/p&gt;

&lt;p&gt;For more in-depth explanations of the internals and optimizations of nested loops joins, I recommend reading &lt;a href="https://blogs.msdn.microsoft.com/craigfr/2006/07/26/nested-loops-join/" rel="noopener noreferrer"&gt;this post by Craig Freedman&lt;/a&gt; as well as &lt;a href="https://sqlserverfast.com/epr/nested-loops/" rel="noopener noreferrer"&gt;Hugo Kornelis’s reference on nested loops&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Do Nested Loops Joins Reveal?
&lt;/h2&gt;

&lt;p&gt;Knowing the internals of how a nested loops join works allows us to infer what the optimizer thinks about our data and the join’s upstream operators, helping us focus our performance tuning efforts. &lt;/p&gt;

&lt;p&gt;Here are a few scenarios to consider the next time you see a nested loops join being used in your execution plan:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Nested loops joins are CPU intensive; at worst, every row needs to be compared to every other row and this can take some time.  This means when you see a nested loops join, SQL Server &lt;em&gt;probably&lt;/em&gt; thinks that one of the two inputs is relatively small.  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;… and if one of the inputs &lt;em&gt;is&lt;/em&gt; relatively small, great!  If instead you see upstream operators that are moving large amounts of data, you may have a estimation problem going on in this area of the plan and may need to update stats/add indexes/refactor the query to have SQL Server provide better estimates (and maybe a more appropriate join).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Nested loops sometimes accompany RID or key lookups.  I always check for one of these because they often leave room for some performance improvements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If a RID lookup exists, it’s usually easy enough to add a clustered index to that underlying table to squeeze out some extra performance.
&lt;/li&gt;
&lt;li&gt;If either RID or key lookup exist, I always check what columns are being returned to see if a smaller index could be used instead (by including a column in a key/column of an existing index) or if the query can be refactored to not bring back those columns (eg. get rid of the SELECT *).
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Nested loops joins do not require data to be sorted on input.  However, performance can improve with a sorted inner data source (see animation above), and SQL Server might choose a more efficient operator if the inputs are both sorted.  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;At the very least, nested loops joins make me think to check whether the input data isn’t sorted because of some upstream transformations, or because of missing indexes.
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;So while nested loops in your plans will always require more investigation, looking at them and the operators around them can provide some good insight into what SQL Server thinks about your data.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Thanks for reading. You might also enjoy &lt;a href="https://twitter.com/bertwagner" rel="noopener noreferrer"&gt;following me on Twitter.&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>executionplans</category>
      <category>performancetuning</category>
      <category>sqllessons</category>
    </item>
    <item>
      <title>4 SQL Injection Techniques For Stealing Data</title>
      <dc:creator>Bert Wagner</dc:creator>
      <pubDate>Tue, 20 Nov 2018 12:00:18 +0000</pubDate>
      <link>https://dev.to/bertwagner/4-sql-injection-techniques-for-stealing-data-1fo0</link>
      <guid>https://dev.to/bertwagner/4-sql-injection-techniques-for-stealing-data-1fo0</guid>
      <description>

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/UMJV3OpjsoM"&gt; &lt;/iframe&gt;&lt;/p&gt;

&lt;a href="https://youtu.be/UMJV3OpjsoM"&gt;Watch this week’s episode on YouTube&lt;/a&gt;

&lt;p&gt;I’m not advocating that you start using &lt;a href="https://bertwagner.com/2017/08/29/warning-are-your-queries-vulnerable-to-sql-injection/"&gt;SQL injection&lt;/a&gt; to start stealing other people’s data.&lt;/p&gt;

&lt;p&gt;However, I do think that you should familiarize yourself with the various SQL injection techniques so that you will be better prepared to prevent them from happening in your own queries.&lt;/p&gt;

&lt;p&gt;The rest of this post will go over four common techniques used for extracting information from SQLServer, as well as simple solutions to prevent them. &lt;/p&gt;

&lt;p&gt;We’ll be looking at all of these techniques directly in SQL Server, but be aware that all of this information is potentially obtainable from an app front-end as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  UNION-Based Attacks
&lt;/h2&gt;

&lt;p&gt;Perhaps the easiest way for a hacker to retrieve additional data from an injection vulnerable query is through a UNION-based attack.&lt;/p&gt;

&lt;p&gt;A UNION-based injection attack adds a UNION or UNION ALL statement to your original stored procedure query, effectively returning any data requested by the second query.&lt;/p&gt;

&lt;p&gt;Let’s say we have a stored procedure that returns user information for the @Username value passed in:&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-- Create our sample table data
CREATE TABLE dbo.Users
(
    Id int IDENTITY(1,1),
    Username NVARCHAR(100),
    FirstName NVARCHAR(100),
    LastName NVARCHAR(100)
);
GO
INSERT INTO dbo.Users VALUES ('BDubs','Bert','Wagner');
INSERT INTO dbo.Users VALUES ('JaneDough', 'Jane', 'Doe');

SELECT * FROM dbo.Users;

-- Create our procedure to retrieve name information
CREATE PROCEDURE dbo.USP_GetUserFullName
    @Username NVARCHAR(100)
AS
BEGIN
    DECLARE @Query NVARCHAR(MAX) = N'
        SELECT
            UserName,
            FirstName,
            LastName
        FROM
            dbo.Users
        WHERE
            Username = ''' + @UserName + N'''';
    EXEC(@Query);
END
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This query is poorly written and easily injectable:&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-- No injection
EXEC dbo.USP_GetUserFullName @Username = N'BDubs'

-- Injection, returns all rows
EXEC dbo.USP_GetUserFullName @Username = N'BDubs'' OR 1=1--';
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Let’s pretend we’re a nefarious hacker and want to determine what SQL Server logins are available to us on the server.  We can use a UNION-based injection attack to query sys.syslogins to get this information and return it as part of the original query:&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;EXEC dbo.USP_GetUserFullName @Username = N'BDubs'' UNION ALL SELECT loginname,null,null FROM master.sys.syslogins;--';
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--fwE8p6Q2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bertwagner.com/wp-content/uploads/2018/11/image.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fwE8p6Q2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bertwagner.com/wp-content/uploads/2018/11/image.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This union based attack simply concatenates the results of another query to our original row of data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Error-Based Attacks
&lt;/h2&gt;

&lt;p&gt;Let’s say the UNION-based technique doesn’t work or we want an alternate way of determining the currently logged in account.&lt;/p&gt;

&lt;p&gt;Another option is to have the application reveal information about the database through error output.&lt;/p&gt;

&lt;p&gt;We can pass in a query that we know will produce an error, in this case casting a string as an INT:&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;EXEC dbo.USP_GetUserFullName @Username = N'''; SELECT CAST(SYSTEM_USER AS INT);--';
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HgxhmW70--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bertwagner.com/wp-content/uploads/2018/11/image-1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HgxhmW70--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bertwagner.com/wp-content/uploads/2018/11/image-1.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Voila! If the application doesn’t handle the error message correctly, it will conveniently show the system login as part of the error message.&lt;/p&gt;

&lt;h2&gt;
  
  
  Out-Of-Band Delivery
&lt;/h2&gt;

&lt;p&gt;The first two techniques we looked at have been &lt;strong&gt;in-bound&lt;/strong&gt; attacks: that is, we used the injectable query to return data to us directly.&lt;/p&gt;

&lt;p&gt;But what if there is sufficient security preventing unexpected data from being directly returned to our app?  We have to get the data we want off of the server via other means.&lt;/p&gt;

&lt;p&gt;This example uses xp_cmdshell to write our data to a text file, but we could have just as easily used this to send ourselves an email, etc…&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;EXEC dbo.USP_GetUserFullName @Username = N'''; EXEC xp_cmdshell ''bcp "SELECT * FROM master.sys.syslogins" queryout "%TEMP%\pwned.txt" -c -T -q --';
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JefFwVAC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bertwagner.com/wp-content/uploads/2018/11/image-2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JefFwVAC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bertwagner.com/wp-content/uploads/2018/11/image-2.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Blind Injection&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;A secured server may not allow us to directly output the data we want, but that doesn’t mean we can’t infer certain information.&lt;/p&gt;

&lt;p&gt;Normally we pride ourselves in being able to write consistently fast queries. But our dedication to consistently fast executions provides hackers with ways of discerning information without any explicit data output.&lt;/p&gt;

&lt;p&gt;For example, let’s say we want to guess if the currently logged in account is “sa”.  We can write logic to check this and purposely slow the injectable query’s execution to determine we our guess is correct:&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;EXEC dbo.USP_GetUserFullName @Username = N'''; if (SELECT SYSTEM_USER) = ''sa'' waitfor delay ''00:00:05'';--';
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If our query with an expected parameter normally returns in milliseconds, forcing a 5-second delay will indirectly inform us that our guessed “sa” account name is correct.&lt;/p&gt;

&lt;h2&gt;
  
  
  Protecting Yourself
&lt;/h2&gt;

&lt;p&gt;The easiest way to prevent SQL injection is to avoid using dynamic SQL when it’s unnecessary. In our example stored procedure above, there is no reason we should be using dynamic SQL –  this should be a parameterized query that is completely safe from injection attacks:&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE PROCEDURE dbo.USP_GetUserFullName
    @Username NVARCHAR(100)
AS
BEGIN
    SELECT
        UserName,
        FirstName,
        LastName
    FROM
        dbo.Users
    WHERE
        Username =  @UserName;
END
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If you must use dynamic SQL, then &lt;a href="https://bertwagner.com/2017/08/29/warning-are-your-queries-vulnerable-to-sql-injection/"&gt;execute your programmatically built query string with sp_executesql&lt;/a&gt;. This procedure will safely parameterize your inputs and prevent from injection from occurring.&lt;/p&gt;

&lt;p&gt;Finally, make sure the accounts executing your queries have as few allowed permissions as possible.  This way, even if your query has an injection vulnerability, the amount of damage an attacker can do is minimal.  Many of the examples above would have failed if our account simply didn’t have access to certain system tables or system procedures like xp_cmdshell.&lt;/p&gt;

&lt;p&gt;These solutions will not cover every scenario, but they will cover the majority of scenarios and improve our chances of keeping our information secure.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Thanks for reading. You might also enjoy &lt;a href="https://twitter.com/bertwagner"&gt;following me on Twitter.&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;


</description>
      <category>sqlinjection</category>
      <category>sqlserver</category>
      <category>hacking</category>
      <category>security</category>
    </item>
    <item>
      <title>Bert &amp; Pinal Troubleshoot a Slow Performing SQL Server</title>
      <dc:creator>Bert Wagner</dc:creator>
      <pubDate>Tue, 13 Nov 2018 12:00:24 +0000</pubDate>
      <link>https://dev.to/bertwagner/bert--pinal-troubleshoot-a-slow-performing-sql-server-1o64</link>
      <guid>https://dev.to/bertwagner/bert--pinal-troubleshoot-a-slow-performing-sql-server-1o64</guid>
      <description>&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/K7o3SUhvgFs"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;What happens when you run into performance tuning expert &lt;a href="https://blog.sqlauthority.com/" rel="noopener noreferrer"&gt;Pinal Dave&lt;/a&gt;?  Talk about how to troubleshoot a slow performing SQL Server of course!&lt;/p&gt;

&lt;p&gt;This week is all about the video, so if you can’t view it above, &lt;a href="https://youtu.be/K7o3SUhvgFs" rel="noopener noreferrer"&gt;head on over to my YouTube channel to watch it there&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After watching, be sure to check out Pinal’s free performance diagnostic scripts over at SQLAuthority.com:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://blog.sqlauthority.com/2016/12/04/dmv-replace-dbcc-inputbuffer-command-interview-question-week-100/" rel="noopener noreferrer"&gt;Replacement for DBCC INPUTBUFFER: sys.dm_exec_input_buffer&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://blog.sqlauthority.com/2016/11/20/find-longest-running-query-execution-plan-interview-question-week-098/" rel="noopener noreferrer"&gt;Finding the longest running queries&lt;/a&gt;.
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://blog.sqlauthority.com/2011/02/01/sql-server-introduction-to-wait-stats-and-wait-types-wait-type-day-1-of-28/" rel="noopener noreferrer"&gt;Introduction to wait statistics&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Thanks for reading. You might also enjoy &lt;a href="https://twitter.com/bertwagner" rel="noopener noreferrer"&gt;following me on Twitter.&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>sql</category>
      <category>performance</category>
      <category>sqlserver</category>
    </item>
  </channel>
</rss>
