Cyotek Development Bloghttps://devblog.cyotek.com/tag/timeout/atom.xml2016-05-06T18:43:43ZSQL Woes - Mismatched parameter types in stored proceduresurn:uuid:b2ffc139-f7e5-49b7-88c0-c3ad8716ba1b2016-05-06T18:43:43Z2016-05-06T18:43:43Z<p>We had a report of crashes occurring for certain users when
accessing a system. From the stack data in the production logs,
a timeout was occurring when running a specific stored
procedure. This procedure was written around 5 years ago and is
in use in many customer databases without issue. Why would the
same SQL suddenly start timing out in one particular database?</p>
<p>The stored procedure in question is called for users with
certain permissions to highlight outstanding units of work that
their access level permits them to do, and is a fairly popular
(and useful) feature of the software.</p>
<p>After obtaining session information from the crash logs, it was
time to run the procedure on a copy of the live database with
session details. The procedure only reads information, but doing
this on a copy helps ensure no ... accidents occur.</p>
<figure class="lang-sql highlight"><figcaption><span>sql</span></figcaption><pre class="code">
<span class="keyword">EXEC</span> [Data].[GetX] @strSiteId = <span class="string">&#39;XXX&#39;</span>, @strUserGroupId = <span class="string">&#39;XXX&#39;</span>, @strUserName = <span class="string">&#39;XXX&#39;</span>
</pre>
</figure>
<p>And it took... <em>27 seconds to return 13 rows</em>. Not good, not
good at all.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/sql-parameters-1a.png" class="gallery" title="An example of a warning and explanation in a query plan" ><img src="https://images.cyotek.com/image/thumbnail/devblog/sql-parameters-1a.png" alt="An example of a warning and explanation in a query plan" decoding="async" loading="lazy" /></a><figcaption>An example of a warning and explanation in a query plan</figcaption></figure>
<p>Viewing the query plan showed something interesting though - one
of the nodes was flagged with a warning symbol, and when the
mouse was hovered over it it stated</p>
<blockquote>
<p>Type conversion in expression
(CONVERT_IMPLICIT(nvarchar(50),[Pn].[SiteId],0)) may affect
&quot;CardinalityEstimate&quot; in query plan choice</p>
</blockquote>
<p>Time to check the procedure's SQL as there shouldn't actually be
any conversions being done, let alone implicit ones.</p>
<p>I can't publish the full SQL in this blog, so I've chopped out
all the table names and field names and used dummy aliases. The
important bits for the purposes of this post are present though,
although I apologize that it's less than readable now.</p>
<figure class="lang-sql highlight"><figcaption><span>sql</span></figcaption><pre class="code">
<span class="keyword">CREATE</span> <span class="keyword">PROCEDURE</span> [Data].[GetX]
 @strSiteId <span class="keyword">nvarchar</span> (50)
, @strUserGroupId <span class="keyword">varchar</span> (20)
, @strUserName <span class="keyword">nvarchar</span> (50)
<span class="keyword">AS</span>
<span class="keyword">BEGIN</span>

 <span class="keyword">SELECT</span> [Al1].[X]
 , [Al1].[X]
 , [Al1].[X]
 , [Al1].[X]
 <span class="keyword">INTO</span> [#Access]
 <span class="keyword">FROM</span> [X].[X] [Al1]
 <span class="keyword">WHERE</span> [Al1].[X] = @strUserName
 <span class="keyword">AND</span> [Al1].[X] = @strUserGroupId
 <span class="keyword">AND</span> [Al1].[X] = 1
 <span class="keyword">AND</span> [Al1].[X] = 1

 <span class="keyword">SELECT</span> <span class="keyword">DISTINCT</span> [Pn].[Id] [X]
 <span class="keyword">FROM</span> [Data].[X] [Pn]
 <span class="keyword">INNER</span> <span class="keyword">JOIN</span> [Data].[X] [Al2]
 <span class="keyword">ON</span> [Al2].[X] = [Pn].[Id]
 <span class="keyword">AND</span> [Al2].[X] = 0
 <span class="keyword">INNER</span> <span class="keyword">JOIN</span> [Data].[X] [Al3]
 <span class="keyword">ON</span> [Al3].[X] = [Al2].[Id]
 <span class="keyword">AND</span> [Al3].[X] = 0
 <span class="keyword">INNER</span> <span class="keyword">JOIN</span> [Data].[X] [Al4]
 <span class="keyword">ON</span> [Al4].[X] = [Al3].[Id]
 <span class="keyword">AND</span> [Al4].[X] = 0
 <span class="keyword">INNER</span> <span class="keyword">JOIN</span> [Data].[X] [Al5]
 <span class="keyword">ON</span> [Al5].[X] = [Al4].[Id]
 <span class="keyword">AND</span> [Al5].[X] = 0
 <span class="keyword">AND</span> [Al5].[X] = 1
 <span class="keyword">AND</span> [Al5].[X] = 0
 <span class="keyword">INNER</span> <span class="keyword">JOIN</span> [#Access]
 <span class="keyword">ON</span> [#Access].[X] = [Al5].[X]
 <span class="keyword">AND</span> [#Access].[X] = [Al2].[X]
 <span class="keyword">AND</span> [#Access].[X] = [Al3].[X]
 <span class="keyword">AND</span> [#Access].[X] = [Al4].[X]
 <span class="keyword">WHERE</span> <span class="keyword">EXISTS</span> (
 <span class="keyword">SELECT</span> [X]
 <span class="keyword">FROM</span> [X].[X] [Al6]
 <span class="keyword">WHERE</span> [Al5].[X] = [Al6].[X]
 <span class="keyword">AND</span> [Al5].[X] = [Al6].[X]
 <span class="keyword">AND</span> [Al6].[X] = 1
 )
 <span class="keyword">AND</span> [Pn].[SiteId] = @strSiteId;
 
 <span class="keyword">DROP</span> <span class="keyword">TABLE</span> [#Access]

<span class="keyword">END</span>;
</pre>
</figure>
<p>The SQL is fairly straight forward - we join a bunch of
different data tables together based on permissions, data status
and where the <code>[SiteId]</code> column matches the lookup value, return
return a unique list of core identifiers. With the exception of
<code>[SiteId]</code> all those joins on <code>[Id]</code> columns are integers.</p>
<blockquote>
<p>Yes, <code>[SiteId]</code> is the primary key in a table. Yes, I know it
isn't a good idea using string keys. It was a design decision
made over 8 years ago and I'm sure at some point these
anomalies will be changed. But it's a side issue to what this
post is about.</p>
</blockquote>
<p>As the warning from the query plan is quite explicit about the
column it's complaining about, it is now time to check the
definition of the table containing the <code>[SiteId]</code> column. Again,
I'm not at liberty to include anything other than the barest
information to show the problem.</p>
<figure class="lang-sql highlight"><figcaption><span>sql</span></figcaption><pre class="code">
<span class="keyword">CREATE</span> <span class="keyword">TABLE</span> [X].[X]
(
 [SiteId] <span class="keyword">varchar</span>(50) <span class="keyword">NOT</span> <span class="keyword">NULL</span> <span class="keyword">CONSTRAINT</span> [PK_X] <span class="keyword">PRIMARY</span> <span class="keyword">KEY</span>
 ...
);
GO
</pre>
</figure>
<p>Can you see the problem? The <em>table</em> defines <code>[SiteId]</code> as
<code>varchar(50)</code> - that is, up to 50 ASCII characters. The <em>stored
procedure</em> on the other hand defines the <code>@strSiteId</code> parameter
(that is used as a <code>WHERE</code> clause for <code>[SiteId]</code>) as
<code>nvarchar(50)</code>, i.e. up to 50 Unicode characters. And there we
go, implicit conversion from Unicode to ASCII that for some
(still unknown at this stage) reason destroyed the performance
of this particular database.</p>
<p>After changing the stored procedure (remember I'm on a copy of
the production database!) to remove that innocuous looking <code>n</code>,
I reran the procedure which completed instantly. And the warning
has disappeared from the plan.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/sql-parameters-1b.png" class="gallery" title="A plan for the same procedure after deleting a single character" ><img src="https://images.cyotek.com/image/thumbnail/devblog/sql-parameters-1b.png" alt="A plan for the same procedure after deleting a single character" decoding="async" loading="lazy" /></a><figcaption>A plan for the same procedure after deleting a single character</figcaption></figure>
<p>The error probably originally occurred as a simple oversight -
almost all character fields in the database are <code>nvarchar</code>'s.
Those that are <code>varchar</code> are ones that control definition data
that cannot be entered, changed or often even viewed by end
users. Anything that the end user can input is always <code>nvarchar</code>
due to the global nature of the software in question.</p>
<p>Luckily, it's a simple fix, although potentially easy to miss,
especially as you might immediately assume the SQL itself is to
blame and try to optimize that.</p>
<p>The take away from this story is simple - ensure that the data
types for variables you use in SQL match the data types of the
fields to avoid implicit conversions that can cause some very
unexpected and unwelcome performance issues - even years after
you originally wrote the code.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2016-05-06 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/sql-woes-mismatched-parameter-types-in-stored-procedures .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.com