<?xml version="1.0" encoding="UTF-8"?> <rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:creativeCommons="http://backend.userland.com/creativeCommonsRssModule"><channel><title>Idea Excursion &#187; SQL Server</title> <atom:link href="http://www.ideaexcursion.com/category/microsoft/windows/sql-server/feed/" rel="self" type="application/rss+xml" /><link>http://www.ideaexcursion.com</link> <description>Technology Musings</description> <lastBuildDate>Wed, 18 Jan 2012 16:33:17 +0000</lastBuildDate> <language>en</language> <sy:updatePeriod>hourly</sy:updatePeriod> <sy:updateFrequency>1</sy:updateFrequency> <generator>http://wordpress.org/?v=3.3.1</generator> <atom:link rel='hub' href='http://www.ideaexcursion.com/?pushpress=hub'/> <creativeCommons:license>http://creativecommons.org/licenses/by-sa/3.0/us/</creativeCommons:license> <item><title>Access system date in SSIS without a Script Task</title><link>http://www.ideaexcursion.com/2011/09/28/access-system-date-within-ssis-without-a-script-task/</link> <comments>http://www.ideaexcursion.com/2011/09/28/access-system-date-within-ssis-without-a-script-task/#comments</comments> <pubDate>Wed, 28 Sep 2011 18:53:26 +0000</pubDate> <dc:creator>Taylor Gerring</dc:creator> <category><![CDATA[SSIS]]></category> <category><![CDATA[T-SQL]]></category><guid isPermaLink="false">http://www.ideaexcursion.com/?p=1590</guid> <description><![CDATA[Set variables to contain the package start datetime with the time truncated, making it easier to compare dates directly.]]></description> <content:encoded><![CDATA[<p>In the world of <abbr title="Extract Transform Load">ETL</abbr>, it&#8217;s quite common to need to know the current date, especially without the time component. Because Script Tasks allows us to code up anything in .NET, it should be fairly obvious how to generate and expose the current date. for example:</p><div class="wp_syntax"><div class="code"><pre class="csharp" style="font-family:monospace;">Dts<span style="color: #008000;">.</span><span style="color: #0000FF;">Variables</span><span style="color: #008000;">&#91;</span><span style="color: #666666;">&quot;TheCurrentDate&quot;</span><span style="color: #008000;">&#93;</span><span style="color: #008000;">.</span><span style="color: #0000FF;">Value</span> <span style="color: #008000;">=</span> DateTime<span style="color: #008000;">.</span><span style="color: #0000FF;">Date</span><span style="color: #008000;">;</span></pre></div></div><p>That little snippet of code will store the current date into a variable named &#8220;TheCurrentDate&#8221;. This is straightforward and there&#8217;s absolutely nothing wrong with the approach, however, there&#8217;s a simpler way to fetch this information without the need of adding a whole control flow item for such a minute amount of work. It&#8217;s brilliantly simple, only requires the creation of a variable, and can be added in about 10 seconds:<br /> <span id="more-1590"></span></p><ol><li>Create new variable with Data Type = DateTime</li><ol><li>Set property EvaluateAsExpression = True</li><li>Set property Expression = <div class="wp_syntax"><div class="code"><pre class="vb" style="font-family:monospace;">DATEADD( <span style="color: #800000;">&quot;day&quot;</span>, DATEDIFF( <span style="color: #800000;">&quot;day&quot;</span>, (DT_DATE) <span style="color: #800000;">&quot;1900-01-01&quot;</span>, @[System::StartTime]  ) , (DT_DATE) <span style="color: #800000;">&quot;1900-01-01&quot;</span> )</pre></div></div></li></ol></ol><p>That&#8217;s it! The variable will contain the package start time with the time truncated, making it easier to compare dates directly. In fact, this method can be used to truncate the time portion of any DateTime Data Type. Simply replace @[System::StartTime] with another variable.</p><p>Want to know how it works? The inner DATEDIFF() function calculates the number of days since 1900. This value is supplied to the outer DATEADD() function, adding days forward from 1900, resulting in a DT_DBTIMESTAMP with the time rounded off. And we accomplished it with pure math—no messy string manipulation required! I borrowed the idea from the same expression in T-SQL: DATEADD(day, DATEDIFF(day, 0, getdate()), 0).</p><p>There&#8217;s one caveat to this: Because SSIS doesn&#8217;t have a native date-only variable type, the value technically contains the date at midnight. However, many tools—such as SQL Server—will implicitly convert to the requisite data type, making &#8220;2011-09-28 12:00:00 AM&#8221; equal to &#8220;2011-09-28&#8243;</p><p>&nbsp;</p><p>&nbsp;</p><p>&nbsp;</p> ]]></content:encoded> <wfw:commentRss>http://www.ideaexcursion.com/2011/09/28/access-system-date-within-ssis-without-a-script-task/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item><title>Connecting to Oracle with SSIS 2008</title><link>http://www.ideaexcursion.com/2011/09/07/connecting-to-oracle-with-ssis-2008/</link> <comments>http://www.ideaexcursion.com/2011/09/07/connecting-to-oracle-with-ssis-2008/#comments</comments> <pubDate>Wed, 07 Sep 2011 21:44:22 +0000</pubDate> <dc:creator>Taylor Gerring</dc:creator> <category><![CDATA[SSIS]]></category> <category><![CDATA[Oracle]]></category><guid isPermaLink="false">http://www.ideaexcursion.com/?p=1571</guid> <description><![CDATA[Quickly configure a connection from SSIS to Oracle, optionally without TNSNAMES.ora, using the free Attunity Connector provided by Microsoft.]]></description> <content:encoded><![CDATA[<p>This isn&#8217;t my first adventure in connecting to Oracle from SQL Server. No, in fact, I wrote <a title="Connecting to Oracle from SQL Server" href="http://www.ideaexcursion.com/2009/01/05/connecting-to-oracle-from-sql-server/">a guide on that very topic</a> nearly 3 years ago. Unfortunately—as a technologist, but fortunately as a blogger—the times change as does the method for setting up the configuration. On the plus side, I&#8217;m much more confident in the setup and am convinced that it&#8217;s easier than ever to connect SSIS to Oracle. Without further adieu, let&#8217;s get started by downloading and installing a couple of necessary components:<br /> <span id="more-1571"></span></p><ul><li><a title="Microsoft Connectors Version 1.1 for Oracle and Teradata by Attunity" href="http://www.microsoft.com/downloads/en/details.aspx?FamilyID=6732934c-2eea-4a7f-85a8-8ba102e6b631">Attunity SSIS Oracle Connector v1.1</a></li><ul><li>After installation, you’ll need to enable the menu items in the Data Flow Source &amp; Destination</li><ul><li>Create Data Flow Task</li><li>Switch to Data Flow</li><li>Pull the Toolbox out</li><li>Right-click&rarr;Choose items…</li><li>Activate the SSIS Data Flow Items tab</li><li>Enable &#8220;Oracle Destination&#8221; and &#8220;Oracle Source&#8221;</li></ul></ul><li><a title="Oracle Database Instant Client" href="http://www.oracle.com/technetwork/database/features/instant-client/index-100365.html">Oracle Instant Client</a> Basic package</li><ul><li>Download and extract both 64-bit and 32-bit versions if you’re using a 64-bit OS</li><ul><li>Only need the 32-bit version if installing on 32-bit OS</li></ul><li>Installation location is up to you, but you’ll need to note it later. This tutorial assumes C:\oracle\ as the base location.</li><ul><li>For example, install the 64-bit package at &#8220;C:\oracle\instantclient_x64_11_2&#8243; and the 32-bit package at &#8220;C:\oracle\instantclient_x86_11_2&#8243;</li></ul></ul></ul><p>Once you have all the items installed, you’ll need to add a couple of registry keys with regedit so that the SSIS connector can find the Oracle Instant Client. Create the necessary keys and string values below where they don’t exist, updating files paths to point to where you extracted the items above (again, C:\oracle\ is assumed for this tutorial):</p><ul><li> [HKEY_LOCAL_MACHINE\SOFTWARE\ORACLE]</li><ul><li>New&rarr;String Value&rarr;Name = TNS_ADMIN&rarr;Value = C:\oracle\tnsnames.ora</li><li>New&rarr;String Value&rarr;Name = ORACLE_HOME&rarr;Value</li><ul><li>For 64-bit OS: C:\oracle\instantclient_x64_11_2</li><li>For 32-bit OS: C:\oracle\instantclient_x32_11_2</li></ul></ul><li>[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\ORACLE] <strong>(64-bit OS only)</strong></li><ul><li>New&rarr;String Value&rarr;Name = TNS_ADMIN&rarr;Value = C:\oracle\tnsnames.ora</li><li>New&rarr;String Value&rarr;Name = ORACLE_HOME&rarr;Value = C:\oracle\instantclient_x86_11_2</li></ul></ul><div><p>If you prefer, simply save the following text as a file with extension .reg and run it.</p><p><strong>64-bit OS:</strong></p><div class="wp_syntax"><div class="code"><pre class="none" style="font-family:monospace;">Windows Registry Editor Version 5.00
&nbsp;
[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\ORACLE]
&quot;TNS_ADMIN&quot;=&quot;C:\\oracle\\tnsnames.ora&quot;
&quot;ORACLE_HOME&quot;=&quot;C:\\oracle\\instantclient_x32_11_2&quot;
&nbsp;
[HKEY_LOCAL_MACHINE\SOFTWARE\ORACLE]
&quot;TNS_ADMIN&quot;=&quot;C:\\oracle\\tnsnames.ora&quot;
&quot;ORACLE_HOME&quot;=&quot;C:\\oracle\\instantclient_x64_11_2&quot;</pre></div></div><p><strong>32-bit OS:</strong></p><div class="wp_syntax"><div class="code"><pre class="none" style="font-family:monospace;">Windows Registry Editor Version 5.00
&nbsp;
[HKEY_LOCAL_MACHINE\SOFTWARE\ORACLE]
&quot;TNS_ADMIN&quot;=&quot;C:\\oracle\\tnsnames.ora&quot;
&quot;ORACLE_HOME&quot;=&quot;C:\\oracle\\instantclient_x32_11_2&quot;</pre></div></div></div><p>This setup allows you the choice of either using the names defined in TNSNAMES.ORA at the location specified in the registry above OR specifying the server inline in the format of &lt;server.name.trb:port/service&gt;. For example: &lt;servername.path.to.tld:1521/ORA10DEV&gt; (without the angle brackets). That&#8217;s really it! The process is much more streamlined than it was just a short while ago. Two installations and a couple regedit tweaks and you should be all set!</p> ]]></content:encoded> <wfw:commentRss>http://www.ideaexcursion.com/2011/09/07/connecting-to-oracle-with-ssis-2008/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item><title>Set Permissions Across SQL Server Database Instances</title><link>http://www.ideaexcursion.com/2011/05/31/setting-user-permissions-across-database-instances/</link> <comments>http://www.ideaexcursion.com/2011/05/31/setting-user-permissions-across-database-instances/#comments</comments> <pubDate>Tue, 31 May 2011 20:35:17 +0000</pubDate> <dc:creator>Taylor Gerring</dc:creator> <category><![CDATA[SQL Server]]></category> <category><![CDATA[T-SQL]]></category><guid isPermaLink="false">http://www.ideaexcursion.com/?p=1567</guid> <description><![CDATA[Code to streamline adding and fixing roles for users, groups, and principals across multiple databases on a single SQL Server instance.]]></description> <content:encoded><![CDATA[<p>As a followup to my post on <a title="SQL Server Principals &amp; Roles Permission Audit" href="http://www.ideaexcursion.com/2011/04/11/sql-server-principals-roles-permission-audit/">SQL Server Principals &amp; Roles Permission Audit</a>, it&#8217;s completely expected that you may want to act on the results of that audit. Specifically, let&#8217;s say you want to organize users into groups and enforce group permissions across the instance. In a particular scenario at my company, we are constantly refreshing databases down from production, which has a nasty side-effect of destroying the finely-tuned permissions we&#8217;ve carefully set in place. What made the most sense to reduce the friction this would cause was to reset the permissions via a job. This allows us to schedule it nightly, fixing any broken permissions, but also makes it easy to fire on demand, should needs dictate. Of course, it would be easiest if this were all contained in a stored procedure, but how do we juggle all these users and groups when you&#8217;re hosting 50 databases?</p><p>What made sense for us was to be able to supply the name of a principal, a &#8220;like name&#8221; for the database, and the role that they should have. What the &#8220;like name&#8221; allows us to do is apply a permission to a series of databases based on name. For example: ideaexcursion1, ideaexcursion2, ideaexcursion3 can all be set by supplying the paramater ideaexcursion%. This increases the complexity by forcing us to use a cursor in the proc, but reduces the number of actions we need to manage.</p><div class="wp_syntax"><div class="code"><pre class="tsql" style="font-family:monospace;"><span style="color: #0000FF;">exec</span> dbo.<span style="color: #202020;">pr_SetGroupPermissions</span>
      @principal <span style="color: #808080;">=</span> <span style="color: #FF0000;">'domain\principalname'</span>
    , @db_likename <span style="color: #FF0000;">'ideaexcursion%'</span>
    , @addrole <span style="color: #FF0000;">'db_owner'</span></pre></div></div><p><span id="more-1567"></span><br /> This snippet will—as you&#8217;ll soon see—give domain\principalname the db_owner role in any databases that match LIKE ideaexcursion%. Here&#8217;s the magic behind the stored procedure:</p><div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
</pre></td><td class="code"><pre class="tsql" style="font-family:monospace;"><span style="color: #0000FF;">create</span> <span style="color: #0000FF;">proc</span> dbo.<span style="color: #202020;">pr_SetGroupPermissions</span> @principal sysname, @db_likename sysname, @addrole sysname, @printonly <span style="color: #0000FF;">bit</span> <span style="color: #808080;">=</span> <span style="color: #000;">0</span>
<span style="color: #0000FF;">as</span>
<span style="color: #0000FF;">declare</span>
	  @<span style="color: #0000FF;">database</span> sysname
	, @cmd <span style="color: #0000FF;">nvarchar</span><span style="color: #808080;">&#40;</span><span style="color: #FF00FF;">max</span><span style="color: #808080;">&#41;</span>
&nbsp;
<span style="color: #0000FF;">declare</span> csr_dbname <span style="color: #0000FF;">cursor</span> <span style="color: #0000FF;">for</span>
<span style="color: #0000FF;">select</span> name <span style="color: #0000FF;">from</span> sys.<span style="color: #202020;">databases</span> <span style="color: #0000FF;">where</span> <span style="color: #0000FF;">state</span> <span style="color: #808080;">=</span> <span style="color: #000;">0</span> and name like @db_likename
&nbsp;
<span style="color: #0000FF;">open</span> csr_dbname
&nbsp;
<span style="color: #0000FF;">fetch</span> <span style="color: #0000FF;">next</span> <span style="color: #0000FF;">from</span> csr_dbname
<span style="color: #0000FF;">into</span> @<span style="color: #0000FF;">database</span>
&nbsp;
<span style="color: #0000FF;">while</span> <span style="color: #FF00FF;">@@fetch_status</span> <span style="color: #808080;">=</span> <span style="color: #000;">0</span>
<span style="color: #0000FF;">begin</span>
&nbsp;
	<span style="color: #0000FF;">set</span> @cmd <span style="color: #808080;">=</span> <span style="color: #FF0000;">'
USE '</span><span style="color: #808080;">+</span><span style="color: #FF00FF;">quotename</span><span style="color: #808080;">&#40;</span>@<span style="color: #0000FF;">database</span><span style="color: #808080;">&#41;</span><span style="color: #808080;">+</span><span style="color: #FF0000;">'
&nbsp;
IF EXISTS (SELECT * FROM sys.server_principals WHERE name = N'</span><span style="color: #FF0000;">''</span><span style="color: #808080;">+</span>@principal<span style="color: #808080;">+</span><span style="color: #FF0000;">''</span><span style="color: #FF0000;">')
BEGIN
	IF NOT EXISTS (SELECT * FROM sys.database_principals WHERE name = N'</span><span style="color: #FF0000;">''</span><span style="color: #808080;">+</span>@principal<span style="color: #808080;">+</span><span style="color: #FF0000;">''</span><span style="color: #FF0000;">')
		CREATE USER '</span><span style="color: #808080;">+</span><span style="color: #FF00FF;">quotename</span><span style="color: #808080;">&#40;</span>@principal<span style="color: #808080;">&#41;</span><span style="color: #808080;">+</span><span style="color: #FF0000;">' FOR LOGIN '</span><span style="color: #808080;">+</span><span style="color: #FF00FF;">quotename</span><span style="color: #808080;">&#40;</span>@principal<span style="color: #808080;">&#41;</span><span style="color: #808080;">+</span><span style="color: #FF0000;">'
	ELSE
		ALTER USER '</span><span style="color: #808080;">+</span><span style="color: #FF00FF;">quotename</span><span style="color: #808080;">&#40;</span>@principal<span style="color: #808080;">&#41;</span><span style="color: #808080;">+</span><span style="color: #FF0000;">' WITH LOGIN = '</span><span style="color: #808080;">+</span><span style="color: #FF00FF;">quotename</span><span style="color: #808080;">&#40;</span>@principal<span style="color: #808080;">&#41;</span><span style="color: #808080;">+</span><span style="color: #FF0000;">'
&nbsp;
	EXEC dbo.sp_addrolemember N'</span><span style="color: #FF0000;">''</span><span style="color: #808080;">+</span>@addrole<span style="color: #808080;">+</span><span style="color: #FF0000;">''</span><span style="color: #FF0000;">', N'</span><span style="color: #FF0000;">''</span><span style="color: #808080;">+</span>@principal<span style="color: #808080;">+</span><span style="color: #FF0000;">''</span><span style="color: #FF0000;">'
END
'</span>
&nbsp;
	<span style="color: #0000FF;">print</span> @cmd
	<span style="color: #0000FF;">if</span> @printonly <span style="color: #808080;">=</span> <span style="color: #000;">0</span>
		<span style="color: #0000FF;">exec</span><span style="color: #808080;">&#40;</span>@cmd<span style="color: #808080;">&#41;</span>
&nbsp;
	<span style="color: #0000FF;">fetch</span> <span style="color: #0000FF;">next</span> <span style="color: #0000FF;">from</span> csr_dbname
	<span style="color: #0000FF;">into</span> @<span style="color: #0000FF;">database</span>
&nbsp;
<span style="color: #0000FF;">end</span>
&nbsp;
<span style="color: #0000FF;">close</span> csr_dbname
<span style="color: #0000FF;">deallocate</span> csr_dbname</pre></td></tr></table></div><p>And here&#8217;s a few notes on what the code does:</p><ul><li>Line 1: Parameter declaration. Notice that there is an optional @printonly param if you&#8217;d like to see the resulting code, but not execute it</li><li>Line 7-8: Set up the cursor. On line 8 is the trick that allows us to use a &#8220;like name&#8221; to simplify the rules. Also, note that we check for &#8220;state = 0&#8243; in the predicate, which corresponds to those with a state of ONLINE. If we didn&#8217;t check this, we would potentially attempt to execute commands against databases in RESTORING, RECOVERING, RECOVERY_PENDING, SUSPECT, EMERGENCY, or OFFLINE states</li><li>Line 18-30: The main code which will be evaluated and executed. There are several parts to it to accommodate a variety of situations:<ul><li>Line 19: Switch context to the correct database, since we&#8217;re executing within the confides of dynamic SQL. Also, the name is qualified with quotename(), which means that it should not be qualified when being passed in</li><li>Line 21: Validate that the user or group exists, otherwise we will get an error when trying to set it. Since we&#8217;re concerning ourselves with permission to a database, and not an instance, we don&#8217;t bother creating a user if it doesn&#8217;t exist. This is probably smart from a security perspective</li><li>Line 23-24: Check if the principal exists within the specific database. If not, create the user</li><li>Line 26: If the user already exists, fix any mismatched SIDswith ALTER USER. This is the preferred way to fix mismatched a mismatched SIDwhich was previously handled by sp_change_users_login, which has since been deprecated</li><li>Line 28: Assume database login is not valid and add the user or group to the specified role</li></ul></li><li>Line 32:  Always print the commands to be executed</li><li>Line 33-34: Execute the code, unless the @printonly parameter has been specified</li></ul><p>There you have it. A proc that grants specific roles to a principal across your entire database instance.</p> ]]></content:encoded> <wfw:commentRss>http://www.ideaexcursion.com/2011/05/31/setting-user-permissions-across-database-instances/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item><title>SQL Server Principals &amp; Roles Permission Audit</title><link>http://www.ideaexcursion.com/2011/04/11/sql-server-principals-roles-permission-audit/</link> <comments>http://www.ideaexcursion.com/2011/04/11/sql-server-principals-roles-permission-audit/#comments</comments> <pubDate>Mon, 11 Apr 2011 20:50:06 +0000</pubDate> <dc:creator>Taylor Gerring</dc:creator> <category><![CDATA[SQL Server]]></category><guid isPermaLink="false">http://www.ideaexcursion.com/?p=1561</guid> <description><![CDATA[If you largely manage users by way of roles, SQL exposes this relationship in a single view, which can produce a report of what principal is part of what role.]]></description> <content:encoded><![CDATA[<p>Thanks to the expansion of catalog views in SQL Server 2005, managing users and permissions has become much easier. Specifically, if you largely manage users by way of roles, rather than per-object permissions, SQL exposes this relationship in a single view, which can produce a report of what principal is part of what role. Check out the query:</p><div class="wp_syntax"><div class="code"><pre class="tsql" style="font-family:monospace;"><span style="color: #0000FF;">select</span> <span style="color: #FF00FF;">db_name</span><span style="color: #808080;">&#40;</span><span style="color: #808080;">&#41;</span> <span style="color: #808080;">&#91;</span>database_name<span style="color: #808080;">&#93;</span>
	, dbpm.<span style="color: #202020;">name</span>, dbpm.<span style="color: #202020;">type_desc</span>, dbpm.<span style="color: #202020;">create_date</span>, dbpm.<span style="color: #202020;">modify_date</span>
	, dbpr.<span style="color: #202020;">name</span>, dbpr.<span style="color: #202020;">type_desc</span>
<span style="color: #0000FF;">from</span> sys.<span style="color: #202020;">database_role_members</span> dbrm
<span style="color: #0000FF;">left</span> join sys.<span style="color: #202020;">database_principals</span> dbpm
	<span style="color: #0000FF;">on</span> dbrm.<span style="color: #202020;">member_principal_id</span> <span style="color: #808080;">=</span> dbpm.<span style="color: #202020;">principal_id</span>
<span style="color: #0000FF;">left</span> join sys.<span style="color: #202020;">database_principals</span> dbpr
	<span style="color: #0000FF;">on</span> dbrm.<span style="color: #202020;">role_principal_id</span> <span style="color: #808080;">=</span> dbpr.<span style="color: #202020;">principal_id</span>
<span style="color: #0000FF;">order</span> <span style="color: #0000FF;">by</span>
	dbpm.<span style="color: #202020;">name</span>, dbpr.<span style="color: #202020;">name</span></pre></div></div><p>This is handily fantastic and works well when you&#8217;re just managing access to a single database. What about if you want to audit logins for the entirety of the database instance? Unfortunately, we have to rely on database cursors to build a query and execute it. The concept is easy, but the details can be a little tricky to manage. Here&#8217;s what I came up with:<br /> <span id="more-1561"></span></p><div class="wp_syntax"><div class="code"><pre class="tsql" style="font-family:monospace;"><span style="color: #0000FF;">declare</span> @cmd <span style="color: #0000FF;">nvarchar</span><span style="color: #808080;">&#40;</span><span style="color: #FF00FF;">max</span><span style="color: #808080;">&#41;</span> <span style="color: #808080;">=</span> <span style="color: #FF0000;">''</span>, @dbname sysname
&nbsp;
<span style="color: #0000FF;">declare</span> csr_dbname <span style="color: #0000FF;">cursor</span> <span style="color: #0000FF;">for</span>
<span style="color: #0000FF;">select</span> <span style="color: #FF00FF;">quotename</span><span style="color: #808080;">&#40;</span>name<span style="color: #808080;">&#41;</span> <span style="color: #0000FF;">from</span> sys.<span style="color: #202020;">databases</span> <span style="color: #0000FF;">where</span> <span style="color: #0000FF;">state</span> <span style="color: #808080;">=</span> <span style="color: #000;">0</span>
&nbsp;
<span style="color: #0000FF;">open</span> csr_dbname
&nbsp;
<span style="color: #0000FF;">fetch</span> <span style="color: #0000FF;">next</span> <span style="color: #0000FF;">from</span> csr_dbname
<span style="color: #0000FF;">into</span> @dbname
&nbsp;
<span style="color: #0000FF;">while</span> <span style="color: #FF00FF;">@@fetch_status</span> <span style="color: #808080;">=</span> <span style="color: #000;">0</span>
<span style="color: #0000FF;">begin</span>
	<span style="color: #0000FF;">set</span> @cmd <span style="color: #808080;">=</span> @cmd <span style="color: #808080;">+</span> <span style="color: #FF0000;">'
select '</span><span style="color: #FF0000;">''</span><span style="color: #808080;">+</span>@dbname<span style="color: #808080;">+</span><span style="color: #FF0000;">''</span><span style="color: #FF0000;">' [database_name]
, dbpm.name [principal_name], dbpm.type_desc [principal_type], dbpm.create_date [principal_create], dbpm.modify_date [principal_modify]
, dbpr.name [role_name], dbpr.type_desc [role_type]
from '</span><span style="color: #808080;">+</span>@dbname<span style="color: #808080;">+</span><span style="color: #FF0000;">'.sys.database_role_members dbrm
left join '</span><span style="color: #808080;">+</span>@dbname<span style="color: #808080;">+</span><span style="color: #FF0000;">'.sys.database_principals dbpm
	on dbrm.member_principal_id = dbpm.principal_id
left join '</span><span style="color: #808080;">+</span>@dbname<span style="color: #808080;">+</span><span style="color: #FF0000;">'.sys.database_principals dbpr
	on dbrm.role_principal_id = dbpr.principal_id
	union all
	'</span>
&nbsp;
	<span style="color: #0000FF;">fetch</span> <span style="color: #0000FF;">next</span> <span style="color: #0000FF;">from</span> csr_dbname
	<span style="color: #0000FF;">into</span> @dbname
&nbsp;
<span style="color: #0000FF;">end</span>
&nbsp;
<span style="color: #0000FF;">close</span> csr_dbname
<span style="color: #0000FF;">deallocate</span> csr_dbname
&nbsp;
<span style="color: #0000FF;">set</span> @cmd <span style="color: #808080;">=</span> <span style="color: #0000FF;">left</span><span style="color: #808080;">&#40;</span>@cmd, <span style="color: #FF00FF;">len</span><span style="color: #808080;">&#40;</span>@cmd<span style="color: #808080;">&#41;</span> <span style="color: #808080;">-</span> <span style="color: #000;">12</span><span style="color: #808080;">&#41;</span>
&nbsp;
<span style="color: #0000FF;">exec</span><span style="color: #808080;">&#40;</span>@cmd<span style="color: #808080;">&#41;</span></pre></div></div><p>There are a couple hacky things in this solution that could be factored out, but I&#8217;m keeping it simple. Here&#8217;s the walkthrough:</p><ul><li>Line 1: Declare and initialize necessary variables. Note that we&#8217;re combining declaration and initialization in a single step. If you&#8217;re not using SQL Server 2008, you&#8217;ll have to separate the initialization with a distinct &#8220;set @cmd = &#8221;&#8221; command</li><li>Line 3-4: Declare the query which defines the cursor. On line 4, we limit state = 0, because otherwise we might get databases in restoring or offline mode. Additionally, QUOTENAME() is used to ensure the input is sanitized for string concatenation. If you wanted to limit the scope of your databases, do it here.</li><li>Line 6-12: Standard cursor setup</li><li>Line 13-23: Build the query we&#8217;ll eventually use to scan all databases. We inject the database name from the cursor into the query to three-part qualify it, effectively neutralizing the current database context. Also, there&#8217;s a &#8220;union all&#8221; at the end that we&#8217;ll need to take care of later on</li><li>Line 25-31: Standard cursor tear-down</li><li>Line 33: Chuck the last &#8220;union all&#8221; from the query, lest it be dangling and we receive a parse error. If we were targeting re-usability, it would be smart to put the &#8220;union all&#8221; in a separate variable, rather than calculating the length of a literal</li><li>Line 35: Execute the  query. We could optionally print the results here, but they&#8217;re typically longer than what Management Studio will display before truncating</li></ul><p>It&#8217;s all quite simple and easy to toss into a stored procedure if you want to audit on a regular basis. Setting up a SQL Server Agent job to run monthly and return the results in an email would be straightforward enough. One of the issues I ran into and did not address was the sorting, which you may have noticed is absent in the extended script. This could be rectified by using &#8220;exec insert&#8221; notation, thought the results aren&#8217;t always dependable.</p> ]]></content:encoded> <wfw:commentRss>http://www.ideaexcursion.com/2011/04/11/sql-server-principals-roles-permission-audit/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item><title>Default column value to identity of different column</title><link>http://www.ideaexcursion.com/2010/04/19/default-column-value-to-identity-of-different-column/</link> <comments>http://www.ideaexcursion.com/2010/04/19/default-column-value-to-identity-of-different-column/#comments</comments> <pubDate>Mon, 19 Apr 2010 21:09:36 +0000</pubDate> <dc:creator>Taylor Gerring</dc:creator> <category><![CDATA[SQL Server]]></category><guid isPermaLink="false">http://www.ideaexcursion.com/?p=1507</guid> <description><![CDATA[Given a table with one column as an identity, how you default another column to the identity of the first, while retaining the ability to change later]]></description> <content:encoded><![CDATA[<p>Thanks to my friend Paul Mayle for putting this one out there:</p><blockquote><p>Given a table with columns:<br /> A: int, primary key, identity<br /> B: int, not null</p><p>How can I set a default for column B to be the value in column A?</p></blockquote><p>This is an interesting problem on a couple levels. First, a computed column won&#8217;t work, because we need the ability to arbitrarily update column B. We could program a trigger, but I prefer to avoid them when possible. Preferably, we could actualize the purpose of the DEFAULT option. Unfortunately, this isn&#8217;t immediately an apparent choice because SQL Server doesn&#8217;t allow you to reference a column in a DEFAULT specification. In fact, according to <abbr title="Books Online">BOL</abbr>, &#8220;Only a constant value, such as a character string; a scalar function (either a  system, user-defined, or CLR function); or NULL can be used as a default.&#8221;<br /> <span id="more-1507"></span><br /> Fortunately, the column which we&#8217;re trying to set a default from had an identity property, and system functions dealing with identity values is the key to using the DEFAULT specification. First, a little code:</p><div class="wp_syntax"><div class="code"><pre class="tsql" style="font-family:monospace;"><span style="color: #0000FF;">CREATE</span> <span style="color: #0000FF;">TABLE</span> dbo.<span style="color: #202020;">IdentDefault</span>
<span style="color: #808080;">&#40;</span>
	A <span style="color: #0000FF;">int</span> <span style="color: #808080;">NOT</span> <span style="color: #808080;">NULL</span> <span style="color: #0000FF;">IDENTITY</span> <span style="color: #808080;">&#40;</span><span style="color: #000;">1</span>, <span style="color: #000;">1</span><span style="color: #808080;">&#41;</span> <span style="color: #0000FF;">PRIMARY</span> <span style="color: #0000FF;">KEY</span>,
	B <span style="color: #0000FF;">int</span> <span style="color: #808080;">NOT</span> <span style="color: #808080;">NULL</span> <span style="color: #0000FF;">DEFAULT</span> <span style="color: #FF00FF;">SCOPE_IDENTITY</span><span style="color: #808080;">&#40;</span><span style="color: #808080;">&#41;</span>
<span style="color: #808080;">&#41;</span>
&nbsp;
<span style="color: #0000FF;">INSERT</span> <span style="color: #0000FF;">INTO</span> dbo.<span style="color: #202020;">IdentDefault</span> <span style="color: #0000FF;">DEFAULT</span> <span style="color: #0000FF;">VALUES</span>
<span style="color: #0000FF;">INSERT</span> <span style="color: #0000FF;">INTO</span> dbo.<span style="color: #202020;">IdentDefault</span> <span style="color: #0000FF;">DEFAULT</span> <span style="color: #0000FF;">VALUES</span>
<span style="color: #0000FF;">INSERT</span> <span style="color: #0000FF;">INTO</span> dbo.<span style="color: #202020;">IdentDefault</span> <span style="color: #0000FF;">DEFAULT</span> <span style="color: #0000FF;">VALUES</span>
&nbsp;
<span style="color: #0000FF;">UPDATE</span> dbo.<span style="color: #202020;">IdentDefault</span> <span style="color: #0000FF;">SET</span> B <span style="color: #808080;">=</span> <span style="color: #000;">10</span> <span style="color: #0000FF;">WHERE</span> A <span style="color: #808080;">=</span> <span style="color: #000;">1</span>
&nbsp;
<span style="color: #0000FF;">SELECT</span> <span style="color: #808080;">*</span> <span style="color: #0000FF;">FROM</span> dbo.<span style="color: #202020;">IdentDefault</span></pre></div></div><p>And the result:</p><div class="wp_syntax"><div class="code"><pre class="none" style="font-family:monospace;">A           B
----------- -----------
1           10
2           2
3           3</pre></div></div><p>As you can see from the above result set, column B defaults to whatever column A contains, yet we can still update column B to whatever value necessary.</p> ]]></content:encoded> <wfw:commentRss>http://www.ideaexcursion.com/2010/04/19/default-column-value-to-identity-of-different-column/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item><title>Import Excel 2007 with SQL Server Import and Export Wizard</title><link>http://www.ideaexcursion.com/2010/01/06/import-excel-2007-with-sql-server-import-and-export-wizard/</link> <comments>http://www.ideaexcursion.com/2010/01/06/import-excel-2007-with-sql-server-import-and-export-wizard/#comments</comments> <pubDate>Wed, 06 Jan 2010 15:38:02 +0000</pubDate> <dc:creator>Taylor Gerring</dc:creator> <category><![CDATA[SQL Server]]></category><guid isPermaLink="false">http://www.ideaexcursion.com/?p=1466</guid> <description><![CDATA[Neither SQL Server 2008 nor Office 2007 install Microsoft.ACE.OLEDB.12.0 providers. With a small tweak, we can enable connections to Excel and Access 2007.]]></description> <content:encoded><![CDATA[<p>If you need to pull in data from an external source ad hoc, using <abbr title="SQL Server Integration Services">SSIS</abbr> is often overkill. Instead, SQL Server Import and Export Wizard (called &#8220;Import and Export Data&#8221; in the Start Menu, but DTSWizard.exe in the filesystem) usually does a good job. This is especially great way to pull in data from users, which typically comes in the form of an Excel attachment. Unfortunately, the providers to import from the newer Office 2007 and 2010 XLSX  file format (also referred to as &#8220;Open Office XML&#8221;) are not available by default and will likely result in a &#8220;Microsoft.ACE.OLEDB.12.0 Provider is not registered&#8221; error. The fix is as easy as  installing the &#8220;<a title="Download Details: 2007 Office System Driver: Data Connectivity Components" href="http://www.microsoft.com/downloads/details.aspx?FamilyID=7554F536-8C28-4598-9B72-EF94E038C891&amp;displaylang=en" target="_blank">2007 Office System Driver: Data Connectivity Components</a>&#8221; package from Microsoft. This same package will also enable access to Access 2007.<br /> <span id="more-1466"></span><br /> After installation, simply re-run DTSWizard and try to import again. If you can&#8217;t find Excel or Access in the Data Source dropdown list, remember that the providers only work in 32-bit mode, and therefore you need to run &#8220;Import and Export Data (32-bit),&#8221; which is located at &#8220;C:\Program Files (x86)\Microsoft SQL Server\100\DTS\Binn\DTSWizard.exe&#8221;.</p> ]]></content:encoded> <wfw:commentRss>http://www.ideaexcursion.com/2010/01/06/import-excel-2007-with-sql-server-import-and-export-wizard/feed/</wfw:commentRss> <slash:comments>1</slash:comments> </item> <item><title>Accessing Custom .NET Assemblies in SSIS 2008 Script Tasks</title><link>http://www.ideaexcursion.com/2009/10/14/accessing-custom-net-assemblies-in-ssis-2008-script-tasks/</link> <comments>http://www.ideaexcursion.com/2009/10/14/accessing-custom-net-assemblies-in-ssis-2008-script-tasks/#comments</comments> <pubDate>Wed, 14 Oct 2009 19:36:24 +0000</pubDate> <dc:creator>Taylor Gerring</dc:creator> <category><![CDATA[SSIS]]></category> <category><![CDATA[.NET]]></category> <category><![CDATA[C#]]></category> <category><![CDATA[SQL Server]]></category> <category><![CDATA[VB.NET]]></category><guid isPermaLink="false">http://www.ideaexcursion.com/?p=1429</guid> <description><![CDATA[If you need to access a custom .NET Assembly from an SSIS Script Task, Microsoft doesn't make things very easy - but it's still possible with a little setup.]]></description> <content:encoded><![CDATA[<p>If you need to access a custom .NET Assembly from an <abbr title="SQL Server Integration Services">SSIS</abbr> Script Task, Microsoft doesn&#8217;t make things very easy &#8211; but it&#8217;s still possible with a little setup. This is a great way to introduce custom data types or some new functionality without having to replicate that code in a new environment.<br /> <span id="more-1429"></span></p><h2>The Setup</h2><ul><li>Windows 7 64-bit</li><li>Visual Studio 2008</li><li>SQL Server 2008 64-bit</li></ul><h2>The Process</h2><ol><li>Create a signing key (See also, <a title="How to: Create a Public/Private Key Pair" href="http://msdn.microsoft.com/en-us/library/6f05ezxy.aspx " target="_blank">How to: Create a Public/Private Key Pair</a>)<ol><li>Open Visual Studio 2008 command Prompt &#8211; the regular command prompt <em>will not</em> work</li><li>Change to a friendly directory: cd %userprofile%\Desktop</li><li>Create the key file: sn -k key.snk</li></ol></li><li>Sign the assembly &#8211; There are a few ways to do this, but I found this to be the easiest. If you want to sign it some other way, check out <a title="http://msdn.microsoft.com/en-us/library/xc31ft41.aspx" href="http://msdn.microsoft.com/en-us/library/xc31ft41.aspx" target="_blank">How to: Sign an Assembly with a Strong Name</a><ol><li>Right-click the Project</li><li>Select &#8220;Properties&#8221;</li><li>Navigate to the &#8220;Signing&#8221; tab</li><li>Browse to strong name key file (which was created in the previous step)</li><li>Recompile the project</li></ol></li><li>Copy the re-compiled assembly to your <acronym title="Global Assembly Cache">GAC</acronym><ol><li>gacutil -i &#8220;C:\Path\to\CustomAssemblyName.dll&#8221;</li></ol></li><li>Copy assembly to &#8220;%programfiles(x86)%\Microsoft SQL Server\100\SDK\Assemblies&#8221;</li><li>Add reference in script task. Repeat this for <strong>every </strong>Script Task you want to access this assembly from<ol><li>Right-click References</li><li>Click &#8220;Add Reference&#8230;&#8221;<p><div id="attachment_1437" class="wp-caption alignnone" style="width: 310px"><a rel="attachment wp-att-1437" href="http://www.ideaexcursion.com/2009/10/14/accessing-custom-net-assemblies-in-ssis-2008-script-tasks/add-reference/"><img class="size-medium wp-image-1437" title="Add Reference..." src="http://static.ideaexcursion.com/wp-content/uploads/2009/10/add-reference-300x139.png" alt="Add Reference..." width="300" height="139" /></a><p class="wp-caption-text">Add Reference...</p></div></li><li>On the .NET tab, scroll to find your assembly</li><li>Press &#8220;OK&#8221;</li><li>The Assembly should now appear under the References list</li></ol></li><li> Add a reference to the assembly in code, at the top<ol><li>(C#) Using CustomAssemblyName;</li><li> (VB.NET) Imports CustomAssemblyName</li></ol></li><li>You should now have full access to the imported <abbr title="Dynamic Link Library">DLL</abbr></li></ol><h2>Caveats</h2><p>This method works pretty well, but deployment isn&#8217;t exactly seamless &#8211; you&#8217;ll have to repeat this for each server and re-register &amp; copy the <abbr title="Dynamic Link Library">DLL</abbr> separately for any updates. Additionally, there is no way to globally add the assembly reference to the entire project or package. Instead, you&#8217;ll have to repeat step 6 (adding the reference) for every Script Task.</p> ]]></content:encoded> <wfw:commentRss>http://www.ideaexcursion.com/2009/10/14/accessing-custom-net-assemblies-in-ssis-2008-script-tasks/feed/</wfw:commentRss> <slash:comments>2</slash:comments> </item> <item><title>SQL Server Regular Expression CLR UDF</title><link>http://www.ideaexcursion.com/2009/08/18/sql-server-regular-expression-clr-udf/</link> <comments>http://www.ideaexcursion.com/2009/08/18/sql-server-regular-expression-clr-udf/#comments</comments> <pubDate>Tue, 18 Aug 2009 19:55:21 +0000</pubDate> <dc:creator>Taylor Gerring</dc:creator> <category><![CDATA[SQL Server]]></category> <category><![CDATA[CLR]]></category> <category><![CDATA[RegEx]]></category> <category><![CDATA[Regular Expressions]]></category> <category><![CDATA[SQL Server 2005]]></category> <category><![CDATA[SQL Server 2008]]></category> <category><![CDATA[UDF]]></category><guid isPermaLink="false">http://www.ideaexcursion.com/?p=1378</guid> <description><![CDATA[How to create a .NET user-defined function that exposes RegEx functionality to Microsoft SQL Server.]]></description> <content:encoded><![CDATA[<p>Face it: data cleanup is a fact of life. While SQL Server has a handful of string manipulation functions, nothing even comes close to the power of RegEx. Fortunately, by leveraging the <abbr title="Common Language Runtime">CLR</abbr> functionality in SQL Server 2005 and SQL Server 2008, we can add a host of new features, including regular expressions.<br /> <span id="more-1378"></span></p><h3>Steps</h3><ol><li>First, fire up Visual Studio (2005 or 2008 &#8211; it doesn&#8217;t matter).</li><li>Create a new project &#8211; name it something clever, like &#8220;RegEx&#8221;</li><li>After creating the project, you should be prompted to connect to a database where you&#8217;ll eventually want to deploy the project. This is completely optional and can be changed later.</li><li>Right-click the project name (&#8220;RegEx&#8221;) and choose Add &rarr; User-Defined Function<div id="attachment_1389" class="wp-caption alignnone" style="width: 310px"><a href="http://www.ideaexcursion.com/2009/08/18/sql-server-regular-expression-clr-udf/add-udf/" rel="attachment wp-att-1389"><img src="http://static.ideaexcursion.com/wp-content/uploads/2009/08/add-udf-300x202.PNG" alt="Add User Defined Function" title="Add User Defined Function" width="300" height="202" class="size-medium wp-image-1389" /></a><p class="wp-caption-text">Add User Defined Function</p></div></li><li>Name the file RegExMatch.</li><li>Paste the following code into that file</li><li>When done, simple build (Ctrl+Shift+B) and deploy (Right-click the project name (&#8220;RegEx&#8221;) &rarr; Deploy).</li></ol><div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
</pre></td><td class="code"><pre class="csharp" style="font-family:monospace;"><span style="color: #0600FF; font-weight: bold;">using</span> <span style="color: #008080;">System</span><span style="color: #008000;">;</span>
<span style="color: #0600FF; font-weight: bold;">using</span> <span style="color: #008080;">System.Data</span><span style="color: #008000;">;</span>
<span style="color: #0600FF; font-weight: bold;">using</span> <span style="color: #008080;">System.Data.SqlClient</span><span style="color: #008000;">;</span>
<span style="color: #0600FF; font-weight: bold;">using</span> <span style="color: #008080;">System.Data.SqlTypes</span><span style="color: #008000;">;</span>
<span style="color: #0600FF; font-weight: bold;">using</span> <span style="color: #008080;">Microsoft.SqlServer.Server</span><span style="color: #008000;">;</span>
<span style="color: #0600FF; font-weight: bold;">using</span> <span style="color: #008080;">System.Text.RegularExpressions</span><span style="color: #008000;">;</span>
&nbsp;
<span style="color: #0600FF; font-weight: bold;">public</span> <span style="color: #0600FF; font-weight: bold;">partial</span> <span style="color: #6666cc; font-weight: bold;">class</span> UserDefinedFunctions
<span style="color: #008000;">&#123;</span>
    <span style="color: #008000;">&#91;</span>Microsoft<span style="color: #008000;">.</span><span style="color: #0000FF;">SqlServer</span><span style="color: #008000;">.</span><span style="color: #0000FF;">Server</span><span style="color: #008000;">.</span><span style="color: #0000FF;">SqlFunction</span><span style="color: #008000;">&#40;</span>IsDeterministic <span style="color: #008000;">=</span> <span style="color: #0600FF; font-weight: bold;">true</span>, IsPrecise <span style="color: #008000;">=</span> <span style="color: #0600FF; font-weight: bold;">true</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#93;</span>
    <span style="color: #0600FF; font-weight: bold;">public</span> <span style="color: #0600FF; font-weight: bold;">static</span> SqlString RegExMatch<span style="color: #008000;">&#40;</span>SqlString expression, SqlString pattern<span style="color: #008000;">&#41;</span>
    <span style="color: #008000;">&#123;</span>
        <span style="color: #0600FF; font-weight: bold;">if</span> <span style="color: #008000;">&#40;</span>expression<span style="color: #008000;">.</span><span style="color: #0000FF;">IsNull</span> <span style="color: #008000;">||</span> pattern<span style="color: #008000;">.</span><span style="color: #0000FF;">IsNull</span><span style="color: #008000;">&#41;</span>
            <span style="color: #0600FF; font-weight: bold;">return</span> SqlString<span style="color: #008000;">.</span><span style="color: #0600FF; font-weight: bold;">Null</span><span style="color: #008000;">;</span>
&nbsp;
        Match match <span style="color: #008000;">=</span> <span style="color: #008000;">new</span> Regex<span style="color: #008000;">&#40;</span>pattern<span style="color: #008000;">.</span><span style="color: #0000FF;">ToString</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">.</span><span style="color: #0000FF;">Match</span><span style="color: #008000;">&#40;</span>expression<span style="color: #008000;">.</span><span style="color: #0000FF;">ToString</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">;</span>
&nbsp;
        <span style="color: #0600FF; font-weight: bold;">return</span> match<span style="color: #008000;">.</span><span style="color: #0000FF;">Success</span> <span style="color: #008000;">?</span> <span style="color: #008000;">new</span> SqlString<span style="color: #008000;">&#40;</span>match<span style="color: #008000;">.</span><span style="color: #0000FF;">Value</span><span style="color: #008000;">&#41;</span> <span style="color: #008000;">:</span> SqlString<span style="color: #008000;">.</span><span style="color: #0600FF; font-weight: bold;">Null</span><span style="color: #008000;">;</span>
&nbsp;
    <span style="color: #008000;">&#125;</span>
<span style="color: #008000;">&#125;</span><span style="color: #008000;">;</span></pre></td></tr></table></div><h3>Code Explanation</h3><ul><li>Line 1-6: Including necessary assemblies. The only item you need to add is line 6 &#8211; System.Text.RegularExpressions</li><li>Line 11: We indicate the function requires 2 parameters, the input string and the regular expression to apply.</li><li>Line 13-14: Check if either input string is NULL. If so, return NULL and do nothing else.</li><li>Line 16: There&#8217;s a lot packed on this line, but essentially, it creates an object named <em>match</em> based on the results of the regular expression match operation.</li><li>Line 18: Use the ternary operator to check if the match was a success. If so, return the matching string. Otherwise, return a NULL.</li></ul><h3>Examples</h3><p>We&#8217;re going to use a simple regular expression to check for a valid US postal code (AKA Zipcode + 4):</p><pre>^\d{5}(-\d{4})?$</pre><p>This regular expressions checks for exactly 5 digits followed by an option group of hyphen and 4 more digits.</p><div class="wp_syntax"><div class="code"><pre class="tsql" style="font-family:monospace;"><span style="color: #0000FF;">select</span>
	  dbo.<span style="color: #202020;">RegExMatch</span><span style="color: #808080;">&#40;</span><span style="color: #FF0000;">'90210'</span>,<span style="color: #FF0000;">'^\d{5}(-\d{4})?$'</span><span style="color: #808080;">&#41;</span>
	, dbo.<span style="color: #202020;">RegExMatch</span><span style="color: #808080;">&#40;</span><span style="color: #FF0000;">'90210-1234'</span>,<span style="color: #FF0000;">'^\d{5}(-\d{4})?$'</span><span style="color: #808080;">&#41;</span>
	, dbo.<span style="color: #202020;">RegExMatch</span><span style="color: #808080;">&#40;</span><span style="color: #FF0000;">'90210-'</span>,<span style="color: #FF0000;">'^\d{5}(-\d{4})?$'</span><span style="color: #808080;">&#41;</span>
	, dbo.<span style="color: #202020;">RegExMatch</span><span style="color: #808080;">&#40;</span><span style="color: #FF0000;">'9021A'</span>,<span style="color: #FF0000;">'^\d{5}(-\d{4})?$'</span><span style="color: #808080;">&#41;</span></pre></div></div><p>And results:</p><div class="wp_syntax"><div class="code"><pre class="none" style="font-family:monospace;">90210	90210-1234	NULL	NULL</pre></div></div><h3>Other notes</h3><p>If you need to change the target database, do so in the project&#8217;s properties:</p><ol><li>Right-click the proeject name (&#8220;RegEx&#8221;) &rarr; Properties</li><li>Select the Database tab (2nd item from the bottom)</li><li>Click &#8220;Browse&#8230;&#8221; to create and test the connection string.<div id="attachment_1390" class="wp-caption alignnone" style="width: 310px"><a href="http://www.ideaexcursion.com/2009/08/18/sql-server-regular-expression-clr-udf/database-properties/" rel="attachment wp-att-1390"><img src="http://static.ideaexcursion.com/wp-content/uploads/2009/08/database-properties-300x148.PNG" alt="Database Properties" title="Database Properties" width="300" height="148" class="size-medium wp-image-1390" /></a><p class="wp-caption-text">Database Properties</p></div></li></ol><p>Thanks to <a title="Regular Expression Replace in SQL 2005 (via the CLR)" href="http://weblogs.sqlteam.com/jeffs/archive/2007/04/27/SQL-2005-Regular-Expression-Replace.aspx" target="_blank">Jeff&#8217;s SQL Server Blog</a> for the initial Regular Expression Replace code.</p> ]]></content:encoded> <wfw:commentRss>http://www.ideaexcursion.com/2009/08/18/sql-server-regular-expression-clr-udf/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item><title>HOWTO: Connect to MySQL in SSIS</title><link>http://www.ideaexcursion.com/2009/06/04/howto-connect-to-mysql-in-ssis/</link> <comments>http://www.ideaexcursion.com/2009/06/04/howto-connect-to-mysql-in-ssis/#comments</comments> <pubDate>Thu, 04 Jun 2009 14:58:43 +0000</pubDate> <dc:creator>Taylor Gerring</dc:creator> <category><![CDATA[Google]]></category> <category><![CDATA[SSIS]]></category> <category><![CDATA[MySQL]]></category> <category><![CDATA[SQL Server]]></category><guid isPermaLink="false">http://www.ideaexcursion.com/?p=793</guid> <description><![CDATA[Using the MySQL ADO.NET provider, SQL Server Integration Services can natively query MySQL databases, providing an easy method to transfer data between systems.]]></description> <content:encoded><![CDATA[<p>While Microsoft provided <a title="SSIS Team Blog New connectivity options in 2008" href="http://blogs.msdn.com/mattm/archive/2008/03/10/new-connectivity-options-in-2008.aspx" target="_blank">connectors for Oracle, Teradata, and SAP BI</a> for <abbr title="SQL Server Integration Services">SSIS</abbr> 2008, there are many other database systems left out of the mix. Fortunately, <abbr title="SQL Server Integration Services">SSIS</abbr> is exceptionally flexible in connecting to various data sources and allows other vendors to provide native support. The MySQL team did just that with <a title="MySQL :: Download Connector/Net 6.0" href="http://dev.mysql.com/downloads/connector/net/6.0.html" target="_blank">Connector/NET 6.0</a>, their ADO.NET provider. This tool allows us to use the the ADO.NET connections in SQL Server Integration Services to easily connect to MySQL. This is a walk through on how to connect to MySQL with <abbr title="SQL Server Integration Services">SSIS</abbr> 2005 utilizing the Connector/NET 6.0 ADO.NET provider.<br /> <span id="more-793"></span></p><ol><li>Download and install MySQL <a title="MySQL :: Download Connector/Net 6.0" href="http://dev.mysql.com/downloads/connector/net/6.0.html" target="_blank">Connector/NET 6.0</a></li><li>Start a new Integration Services project in <acronym title="Business Intelligence Development Studio">BIDS</acronym></li><li>Right-click in Connection Managers and create a new ADO.NET Connection<p><a rel="attachment wp-att-808" href="http://www.ideaexcursion.com/2009/06/04/howto-connect-to-mysql-in-ssis/new-ado-net-connection/"><img class="size-medium wp-image-808" title="New ADO.NET Connection" src="http://static.ideaexcursion.com/wp-content/uploads/2009/05/new-ado-net-connection-250x300.png" alt="New ADO.NET Connection" width="250" height="300" /></a></li><li>In the Provider dropdown, expand .Net Providers and select MySQL Data Provider. Press &quot;OK&quot;<p><a rel="attachment wp-att-807" href="http://www.ideaexcursion.com/2009/06/04/howto-connect-to-mysql-in-ssis/mysql-data-provider/"><img class="size-medium wp-image-807" title="MySQL Data Provider" src="http://static.ideaexcursion.com/wp-content/uploads/2009/05/mysql-data-provider-300x201.png" alt="MySQL Data Provider" width="300" height="201" /></a></li><li>Fill out the Server name, User name, Password and select the database name for the target MySQL server. Be sure to test the connection and press &#8220;OK&#8221;<p><a rel="attachment wp-att-799" href="http://www.ideaexcursion.com/2009/06/04/howto-connect-to-mysql-in-ssis/connection-manager-connection-info/"><img class="size-medium wp-image-799" title="Connection Manager Connection Info" src="http://static.ideaexcursion.com/wp-content/uploads/2009/05/connection-manager-connection-info-294x300.png" alt="Connection Manager Connection Info" width="294" height="300" /></a></li><li>Rename the connection to &#8220;MySQLDB&#8221;<p><a rel="attachment wp-att-800" href="http://www.ideaexcursion.com/2009/06/04/howto-connect-to-mysql-in-ssis/connection-managers-mysqldb/"><img class="size-full wp-image-800" title="Connection Managers MySQLDB" src="http://static.ideaexcursion.com/wp-content/uploads/2009/05/connection-managers-mysqldb.png" alt="Connection Managers MySQLDB" width="143" height="50" /></a></li><li>Open up the Toolbox and drag a Data Flow Task from the toolbox onto the Control Flow surface<p><div id="attachment_795" class="wp-caption alignnone" style="width: 310px"><a rel="attachment wp-att-795" href="http://www.ideaexcursion.com/2009/06/04/howto-connect-to-mysql-in-ssis/add-dataflowtask/"><img class="size-medium wp-image-795" title="Add Dataflow Task" src="http://static.ideaexcursion.com/wp-content/uploads/2009/05/add-dataflowtask-300x76.png" alt="Add Dataflow Task" width="300" height="76" /></a><p class="wp-caption-text">Add Dataflow Task</p></div></li><li>Double-click the Data Flow Task to switch to the Data Flow view</li><li>Create a new variable, &#8220;MySQLResult&#8221; with the Data Type of Object. We will be using this as the final destination for the data, so we don&#8217;t need to connect to a file or database to store the data from this test<p><div id="attachment_812" class="wp-caption alignnone" style="width: 310px"><a rel="attachment wp-att-812" href="http://www.ideaexcursion.com/2009/06/04/howto-connect-to-mysql-in-ssis/variables-mysqlresult/"><img class="size-medium wp-image-812" title="MySQLResult Variable" src="http://static.ideaexcursion.com/wp-content/uploads/2009/05/variables-mysqlresult-300x66.png" alt="MySQLResult Variable" width="300" height="66" /></a><p class="wp-caption-text">MySQLResult Variable</p></div></li><li>Drag a new DataReader Source component onto the Data Flow surface<p><div id="attachment_796" class="wp-caption alignnone" style="width: 310px"><a rel="attachment wp-att-796" href="http://www.ideaexcursion.com/2009/06/04/howto-connect-to-mysql-in-ssis/add-datareader-source/"><img class="size-medium wp-image-796" title="Add DataReader Source" src="http://static.ideaexcursion.com/wp-content/uploads/2009/05/add-datareader-source-300x78.png" alt="Add DataReader Source" width="300" height="78" /></a><p class="wp-caption-text">Add DataReader Source</p></div></li><li>Double-click the DataReader Source to open the Advanced Editor. On the Connection Managers tab, select the previously-created MySQLDB connection<p><div id="attachment_805" class="wp-caption alignnone" style="width: 310px"><a rel="attachment wp-att-805" href="http://www.ideaexcursion.com/2009/06/04/howto-connect-to-mysql-in-ssis/datareader-source-connection-managers/"><img class="size-medium wp-image-805" title="DataReader Source Connection Managers" src="http://static.ideaexcursion.com/wp-content/uploads/2009/05/datareader-source-connection-managers-300x290.png" alt="DataReader Source Connection Managers" width="300" height="290" /></a><p class="wp-caption-text">DataReader Source Connection Managers</p></div></li><li>Switch to the Component Properties tab and enter the SQL query in the SqlCommand property. Note that the query must be compatible with MySQL syntax, not SQL Server.<div id="attachment_804" class="wp-caption alignnone" style="width: 310px"><a rel="attachment wp-att-804" href="http://www.ideaexcursion.com/2009/06/04/howto-connect-to-mysql-in-ssis/datareader-source-component-properties/"><img class="size-medium wp-image-804" title="DataReader Source Component Properties" src="http://static.ideaexcursion.com/wp-content/uploads/2009/05/datareader-source-component-properties-300x269.png" alt="DataReader Source Component Properties" width="300" height="269" /></a><p class="wp-caption-text">DataReader Source Component Properties</p></div></li><li>Switch to the Column Mappings tab to verify that the query is successful and the all the columns were pulled from the database. When done, press &#8220;OK&#8221;.<p><div id="attachment_803" class="wp-caption alignnone" style="width: 310px"><a rel="attachment wp-att-803" href="http://www.ideaexcursion.com/2009/06/04/howto-connect-to-mysql-in-ssis/datareader-soruce-column-mappings/"><img class="size-medium wp-image-803" title="DataReader Source Column Mappings" src="http://static.ideaexcursion.com/wp-content/uploads/2009/05/datareader-soruce-column-mappings-300x269.png" alt="DataReader Source Column Mappings" width="300" height="269" /></a><p class="wp-caption-text">DataReader Source Column Mappings</p></div></li><li>Create a new Recordset Destination by dragging it from the toolbox to the Data Flow surface<p><div id="attachment_797" class="wp-caption alignnone" style="width: 310px"><a rel="attachment wp-att-797" href="http://www.ideaexcursion.com/2009/06/04/howto-connect-to-mysql-in-ssis/add-recordset-destination/"><img class="size-medium wp-image-797" title="Add Recordset Destination" src="http://static.ideaexcursion.com/wp-content/uploads/2009/05/add-recordset-destination-300x102.png" alt="Add Recordset Destination" width="300" height="102" /></a><p class="wp-caption-text">Add Recordset Destination</p></div></li><li>Drag the green Data Flow Path from DataReader Source to Recordset Destination, so they connect<p><div id="attachment_801" class="wp-caption alignnone" style="width: 170px"><a rel="attachment wp-att-801" href="http://www.ideaexcursion.com/2009/06/04/howto-connect-to-mysql-in-ssis/connect-source-destination/"><img class="size-full wp-image-801" title="Connect Source to Destination" src="http://static.ideaexcursion.com/wp-content/uploads/2009/05/connect-source-destination.png" alt="Connect Source to Destination" width="160" height="145" /></a><p class="wp-caption-text">Connect Source to Destination</p></div></li><li>Double-click the Recordset Destination to open its Advanced Editor</li><li>Under Custom Properties, select the dropdown for VariableName and select the variable we created before, User::MySQLResult<p><div id="attachment_810" class="wp-caption alignnone" style="width: 310px"><a rel="attachment wp-att-810" href="http://www.ideaexcursion.com/2009/06/04/howto-connect-to-mysql-in-ssis/recordset-destination-component-properties/"><img class="size-medium wp-image-810" title="Recordset Destination Component Properties" src="http://static.ideaexcursion.com/wp-content/uploads/2009/05/recordset-destination-component-properties-300x269.png" alt="Recordset Destination Component Properties" width="300" height="269" /></a><p class="wp-caption-text">Recordset Destination Component Properties</p></div></li><li>Switch to the Input Columns tab and select those columns that you want stored in the Recordset Destination. When complete, click &#8220;OK&#8221;<div id="attachment_811" class="wp-caption alignnone" style="width: 308px"><a rel="attachment wp-att-811" href="http://www.ideaexcursion.com/2009/06/04/howto-connect-to-mysql-in-ssis/recordset-destination-input-columns/"><img class="size-medium wp-image-811" title="Recordset Destination Input Columns" src="http://static.ideaexcursion.com/wp-content/uploads/2009/05/recordset-destination-input-columns-298x300.png" alt="Recordset Destination Input Columns" width="298" height="300" /></a><p class="wp-caption-text">Recordset Destination Input Columns</p></div></li><li>Right-click the green Data Flow Path and choose &#8220;Data Viewers&#8230;&#8221;<p><div id="attachment_813" class="wp-caption alignnone" style="width: 264px"><a rel="attachment wp-att-813" href="http://www.ideaexcursion.com/2009/06/04/howto-connect-to-mysql-in-ssis/data-flow-path-data-viewers/"><img class="size-medium wp-image-813" title="Data Flow Path Data Viewers" src="http://static.ideaexcursion.com/wp-content/uploads/2009/05/data-flow-path-data-viewers-254x300.png" alt="Data Flow Path Data Viewers" width="254" height="300" /></a><p class="wp-caption-text">Data Flow Path Data Viewers</p></div></li><li>Select &#8220;Data Viewers&#8221; from the left pane and click the &#8220;Add&#8230;&#8221; button<p><div id="attachment_802" class="wp-caption alignnone" style="width: 310px"><a rel="attachment wp-att-802" href="http://www.ideaexcursion.com/2009/06/04/howto-connect-to-mysql-in-ssis/data-flow-path-editor/"><img class="size-medium wp-image-802" title="Data Flow Path Editor" src="http://static.ideaexcursion.com/wp-content/uploads/2009/05/data-flow-path-editor-300x253.png" alt="Data Flow Path Editor" width="300" height="253" /></a><p class="wp-caption-text">Data Flow Path Editor</p></div></li><li>Under the General tab, select Grid and press &#8220;OK&#8221;<p><div id="attachment_798" class="wp-caption alignnone" style="width: 310px"><a rel="attachment wp-att-798" href="http://www.ideaexcursion.com/2009/06/04/howto-connect-to-mysql-in-ssis/configure-data-viewer/"><img class="size-medium wp-image-798" title="Configure Data Viewer" src="http://static.ideaexcursion.com/wp-content/uploads/2009/05/configure-data-viewer-300x229.png" alt="Configure Data Viewer" width="300" height="229" /></a><p class="wp-caption-text">Configure Data Viewer</p></div></li><li>Run the package</li><li>If you&#8217;ve done everything correctly, you should see a Data Reader Output Data Viewer window pop up with the contents of the query we specified earlier.<p><div id="attachment_806" class="wp-caption alignnone" style="width: 310px"><a rel="attachment wp-att-806" href="http://www.ideaexcursion.com/2009/06/04/howto-connect-to-mysql-in-ssis/data-viewer-output/"><img class="size-medium wp-image-806" title="Data Viewer Output" src="http://static.ideaexcursion.com/wp-content/uploads/2009/05/data-viewer-output-300x165.png" alt="Data Viewer Output" width="300" height="165" /></a><p class="wp-caption-text">Data Viewer Output</p></div></li></ol><p>SQL Server Integration Services makes connecting to other systems very easy. The MySQL ADO.NET provider works well, but requires more configuration than a native Source component.</p> ]]></content:encoded> <wfw:commentRss>http://www.ideaexcursion.com/2009/06/04/howto-connect-to-mysql-in-ssis/feed/</wfw:commentRss> <slash:comments>8</slash:comments> </item> <item><title>Flatten Heirarchies in SQL Server with Common Table Expressions</title><link>http://www.ideaexcursion.com/2009/05/12/flatten-heirarchies-in-sql-server-with-common-table-expressions/</link> <comments>http://www.ideaexcursion.com/2009/05/12/flatten-heirarchies-in-sql-server-with-common-table-expressions/#comments</comments> <pubDate>Tue, 12 May 2009 14:41:57 +0000</pubDate> <dc:creator>Taylor Gerring</dc:creator> <category><![CDATA[SQL Server]]></category> <category><![CDATA[SQL]]></category> <category><![CDATA[SQL Server 2005]]></category> <category><![CDATA[SQL Server 2008]]></category> <category><![CDATA[T-SQL]]></category><guid isPermaLink="false">http://www.ideaexcursion.com/?p=731</guid> <description><![CDATA[Use a CTE to recursively roll up all descendant records into separate groups, regardless of level. Example data and Common Table Expression code included.]]></description> <content:encoded><![CDATA[<p>Common Table Expressions were a new feature added to SQL Server 2005 and provide an efficient way to recursively query relationships stored in a normalized table. We&#8217;re going to build on that essential functionality to flatten a typical corporate structure so that all children, grand children, great grand children, etc. roll up into a single, flattened parent, regardless of depth. To graphically visualize this, take a look at the actual relationship we&#8217;ll be querying against:<br /> <span id="more-731"></span><br /><div id="attachment_772" class="wp-caption alignnone" style="width: 310px"><a href="http://www.ideaexcursion.com/2009/05/12/flatten-heirarchies-in-sql-server-with-common-table-expressions/actual-relationship/" rel="attachment wp-att-772"><img src="http://static.ideaexcursion.com/wp-content/uploads/2009/05/actual-relationship-300x211.png" alt="Actual employee structure to be flattened" title="Actual Relationship" width="300" height="211" class="size-medium wp-image-772" /></a><p class="wp-caption-text">Actual employee structure to be flattened</p></div></p><p>This structure will act as our example for the Development Department. The Department Head (Employee 0) has asked for a report of all employees within each team of the development group. Notice that different groups have varying levels of depth. The Database team only has a Manager with two direct reports. The <abbr title="User Interface">UI</abbr> team is a single person. The Middle Tier group is much larger, with a Manager having two direct reports, and one of those employees having several employees beneath him. The requested report should group all employees, flattening the relationship to a single Manager, but excluding himself (since that would mean every employee would simply roll up to him). This &#8220;Desired Relationship&#8221; is represented below:</p><div id="attachment_771" class="wp-caption alignnone" style="width: 310px"><a href="http://www.ideaexcursion.com/2009/05/12/flatten-heirarchies-in-sql-server-with-common-table-expressions/desired-relationship/" rel="attachment wp-att-771"><img src="http://static.ideaexcursion.com/wp-content/uploads/2009/05/desired-relationship-300x80.png" alt="The desired result of the query" title="Desired Relationship" width="300" height="80" class="size-medium wp-image-771" /></a><p class="wp-caption-text">The desired result of the query</p></div><p>To recreate this scenario, I&#8217;m providing some code for both the setup and query. If you&#8217;re not familiar with Common Table Expressions, I would suggest that you familiarize yourself with the <a href="http://msdn.microsoft.com/en-us/library/ms186243.aspx" title="SQL Server Books Online">SQL Server <abbr title="Books Online">BOL</abbr> entry</a>. The main concept you should understand to be able to adapt this to your own data is that for a <abbr title="Common Table Expression">CTE</abbr> to act recursively, you need both an anchor and recursive query. I&#8217;ll explain their parts later.</p><p>First, let&#8217;s instantiate the tables and populate them with data:</p><div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
</pre></td><td class="code"><pre class="tsql" style="font-family:monospace;"><span style="color: #0000FF;">use</span> tempdb
go
&nbsp;
<span style="color: #0000FF;">if</span> <span style="color: #FF00FF;">object_id</span><span style="color: #808080;">&#40;</span><span style="color: #FF0000;">'dbo.Employees'</span><span style="color: #808080;">&#41;</span> <span style="color: #0000FF;">is</span> not null
	<span style="color: #0000FF;">drop</span> <span style="color: #0000FF;">table</span> dbo.<span style="color: #202020;">Employees</span>
go
&nbsp;
<span style="color: #0000FF;">create</span> <span style="color: #0000FF;">table</span> dbo.<span style="color: #202020;">Employees</span>
<span style="color: #808080;">&#40;</span>
	  EmployeeID <span style="color: #0000FF;">int</span> <span style="color: #0000FF;">primary</span> <span style="color: #0000FF;">key</span>
	, EmployeeName <span style="color: #0000FF;">varchar</span><span style="color: #808080;">&#40;</span><span style="color: #000;">50</span><span style="color: #808080;">&#41;</span> not null
	, ManagerEmployeeID <span style="color: #0000FF;">int</span> null
<span style="color: #808080;">&#41;</span>
go
&nbsp;
<span style="color: #0000FF;">insert</span> <span style="color: #0000FF;">into</span> dbo.<span style="color: #202020;">Employees</span> <span style="color: #808080;">&#40;</span>EmployeeID, EmployeeName, ManagerEmployeeID<span style="color: #808080;">&#41;</span>
<span style="color: #0000FF;">select</span> <span style="color: #000;">0</span>, <span style="color: #FF0000;">'Employee 0'</span>, null
<span style="color: #0000FF;">union</span> all
<span style="color: #0000FF;">select</span> <span style="color: #000;">1</span>, <span style="color: #FF0000;">'Employee 1'</span>, <span style="color: #000;">0</span>
<span style="color: #0000FF;">union</span> all
<span style="color: #0000FF;">select</span> <span style="color: #000;">2</span>, <span style="color: #FF0000;">'Employee 2'</span>, <span style="color: #000;">0</span>
<span style="color: #0000FF;">union</span> all
<span style="color: #0000FF;">select</span> <span style="color: #000;">3</span>, <span style="color: #FF0000;">'Employee 3'</span>, <span style="color: #000;">0</span>
<span style="color: #0000FF;">union</span> all
<span style="color: #0000FF;">select</span> <span style="color: #000;">4</span>, <span style="color: #FF0000;">'Employee 1.1'</span>, <span style="color: #000;">1</span>
<span style="color: #0000FF;">union</span> all
<span style="color: #0000FF;">select</span> <span style="color: #000;">5</span>, <span style="color: #FF0000;">'Employee 1.2'</span>, <span style="color: #000;">1</span>
<span style="color: #0000FF;">union</span> all
<span style="color: #0000FF;">select</span> <span style="color: #000;">6</span>, <span style="color: #FF0000;">'Employee 3.1'</span>, <span style="color: #000;">3</span>
<span style="color: #0000FF;">union</span> all
<span style="color: #0000FF;">select</span> <span style="color: #000;">7</span>, <span style="color: #FF0000;">'Employee 3.2'</span>, <span style="color: #000;">3</span>
<span style="color: #0000FF;">union</span> all
<span style="color: #0000FF;">select</span> <span style="color: #000;">8</span>, <span style="color: #FF0000;">'Employee 3.1.1'</span>, <span style="color: #000;">6</span>
<span style="color: #0000FF;">union</span> all
<span style="color: #0000FF;">select</span> <span style="color: #000;">9</span>, <span style="color: #FF0000;">'Employee 3.1.1.1'</span>, <span style="color: #000;">8</span>
go</pre></td></tr></table></div><p>This script will simply create the example employees table and populate it with data to relationally represent what is displayed in the first picture. Here are what the various lines accomplish:</p><ul><li>1-2: Switch the database context to tempdb. Since the contents of tempdb are cleared upon service restart, we&#8217;re simply ensuring that this will be cleaned up eventually.</li><li>4-6: Check if dbo.Employees exists. If so, drop it.</li><li>8-14: Create a table to hold our example data.</li><li>16-36: Manually populate the example data. Note that we&#8217;re using a UNION ALL between the selects, so only a single INSERT occurs.</li></ul><p>As for the query to manipulate this data, here is the actual <abbr title="Common Table Expression">CTE</abbr>:</p><div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
</pre></td><td class="code"><pre class="tsql" style="font-family:monospace;"><span style="color: #008080;">-- Use a CTE to flatten an organization structure to each department</span>
<span style="color: #0000FF;">with</span> AllMyChildren <span style="color: #808080;">&#40;</span>ManagerEmployeeID, EmployeeID, GroupManagerID, EmployeeName, <span style="color: #0000FF;">Depth</span><span style="color: #808080;">&#41;</span>
<span style="color: #0000FF;">as</span>
<span style="color: #808080;">&#40;</span>
	<span style="color: #008080;">-- The anchor statement. This selects each department head</span>
	<span style="color: #008080;">-- The important part is to alias the original EmployeeID as the GroupManagerID</span>
	<span style="color: #0000FF;">select</span> ManagerEmployeeID, EmployeeID, EmployeeID <span style="color: #808080;">&#91;</span>GroupManagerID<span style="color: #808080;">&#93;</span>, EmployeeName, <span style="color: #000;">0</span> <span style="color: #808080;">&#91;</span><span style="color: #0000FF;">Depth</span><span style="color: #808080;">&#93;</span>
	<span style="color: #0000FF;">from</span> dbo.<span style="color: #202020;">Employees</span> <span style="color: #0000FF;">where</span> EmployeeID in
		<span style="color: #008080;">-- We want all employees 1 level below below a certain level, so we'll utilize a subquery to find them</span>
		<span style="color: #808080;">&#40;</span>
		<span style="color: #008080;">-- Get EmployeeIDs that are children to their common parent</span>
		<span style="color: #0000FF;">select</span> EmployeeID <span style="color: #0000FF;">from</span> dbo.<span style="color: #202020;">Employees</span> <span style="color: #0000FF;">where</span> ManagerEmployeeID <span style="color: #808080;">=</span> <span style="color: #000;">0</span>
		<span style="color: #808080;">&#41;</span>
	<span style="color: #0000FF;">UNION</span> <span style="color: #808080;">ALL</span>
	<span style="color: #008080;">-- The recursive statement which finds all employees under each department</span>
	<span style="color: #0000FF;">select</span> pc.<span style="color: #202020;">ManagerEmployeeID</span>, pc.<span style="color: #202020;">EmployeeID</span>, amc.<span style="color: #202020;">GroupManagerID</span>, pc.<span style="color: #202020;">EmployeeName</span>, <span style="color: #0000FF;">Depth</span> <span style="color: #808080;">+</span> <span style="color: #000;">1</span>
	<span style="color: #0000FF;">from</span> dbo.<span style="color: #202020;">Employees</span> pc
	<span style="color: #0000FF;">inner</span> join AllMyChildren amc <span style="color: #0000FF;">on</span> pc.<span style="color: #202020;">ManagerEmployeeID</span> <span style="color: #808080;">=</span> amc.<span style="color: #202020;">EmployeeID</span>
<span style="color: #808080;">&#41;</span>
<span style="color: #008080;">-- The CTE is primed, but we still need to execute it with the statement below</span>
<span style="color: #0000FF;">select</span> EmployeeName, GroupManagerID, <span style="color: #0000FF;">Depth</span> 
<span style="color: #0000FF;">from</span> AllMyChildren
<span style="color: #0000FF;">order</span> <span style="color: #0000FF;">by</span> GroupManagerID, <span style="color: #0000FF;">Depth</span>
<span style="color: #008080;">-- Use the MAXRECURSION query hint to avoid default recursion limit of 100</span>
<span style="color: #0000FF;">OPTION</span> <span style="color: #808080;">&#40;</span>MAXRECURSION <span style="color: #000;">0</span><span style="color: #808080;">&#41;</span></pre></td></tr></table></div><p>I&#8217;ve commented liberally, but I&#8217;ll go through this line-by-line to explain it in more detail.</p><ul><li>2-4: Define the <abbr title="Common Table Expression">CTE</abbr> (AllMyChildren) and the columns that it will output (ManagerEmployeeID, EmployeeID, GroupManagerID, EmployeeName, Depth)</li><li>7-8: This is the anchor statement. SELECT the fields we eventually want outputted. The key to this is that EmployeeID is being included a second time, but aliased as GroupManagerID.</li><li>10-13: This is a subquery in the WHERE condition of the anchor statement. Because we want to discard some levels (only the topmost level in our example), we need to tell the anchor to fetch all children whose parent EmployeeID is 0. Adjust this sub-query to affect at what level the teams should roll up to.</li><li>14-18: UNION ALL the anchor to the recursive portion of the <abbr title="Common Table Expression">CTE</abbr>. The recursive part of the query is similar to the anchor in that it must SELECT similar columns. Notice that it again selects from dbo.Employees, but it also performs an INNER JOIN against the <abbr title="Common Table Expression">CTE</abbr>, linking the EmployeeID and ManagerEmployeeID. This is what causes to recursion to occur. Additionally, we increment Depth by 1. The inclusion of the level is completely optional in the <abbr title="Common Table Expression">CTE</abbr>, but may be helpful for reporting.</li><li>19: Close the <abbr title="Common Table Expression">CTE</abbr> block. At this point, it is defined, but we have yet to execute it.</li><li>21-23: Perform a SELECT against the <abbr title="Common Table Expression">CTE</abbr> to start it.</li><li>25: By default, recursion in Common Table Expressions is limited to 100 iterations. If you anticipate having more than 100 records, you&#8217;ll need to specify MAXRECURSION 0. When testing, you can limit it to any number up to 32,767 to prevent wild-running queries. Here &#8211; since this is tested and working &#8211; we specify &#8220;0&#8243; for unlimited iterations.</li></ul><p>And this is the result of the above query:<br /><div id="attachment_766" class="wp-caption alignnone" style="width: 283px"><a href="http://www.ideaexcursion.com/2009/05/12/flatten-heirarchies-in-sql-server-with-common-table-expressions/query-result/" rel="attachment wp-att-766"><img src="http://static.ideaexcursion.com/wp-content/uploads/2009/05/query-result.png" alt="The tabular results of the query" title="Query Result" width="273" height="191" class="size-full wp-image-766" /></a><p class="wp-caption-text">The tabular results of the query</p></div><br /> Notice that the GroupManagerID is the same for each team, regardless of depth. Also, the Depth corresponds to how far down the tree each employee exist. I&#8217;ve conveniently coded the EmployeeName in dot-notation to make the comparison much simpler.</p> ]]></content:encoded> <wfw:commentRss>http://www.ideaexcursion.com/2009/05/12/flatten-heirarchies-in-sql-server-with-common-table-expressions/feed/</wfw:commentRss> <slash:comments>1</slash:comments> </item> </channel> </rss>
<!-- Served from: www.ideaexcursion.com @ 2012-02-04 08:32:40 by W3 Total Cache -->
