<?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/"
	>

<channel>
	<title>BI: Beer Intelligence? &#187; T-SQL</title>
	<atom:link href="http://blog.hoegaerden.be/tag/t-sql/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.hoegaerden.be</link>
	<description>Serving you my best brews, one pint at a time!</description>
	<lastBuildDate>Tue, 14 May 2013 17:04:18 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>SSRS Deployment: Generate The Batch Script Through SQL!</title>
		<link>http://blog.hoegaerden.be/2013/02/16/ssrs-deployment-generate-the-batch-script-through-sql/</link>
		<comments>http://blog.hoegaerden.be/2013/02/16/ssrs-deployment-generate-the-batch-script-through-sql/#comments</comments>
		<pubDate>Sat, 16 Feb 2013 13:33:57 +0000</pubDate>
		<dc:creator>Valentino Vranken</dc:creator>
				<category><![CDATA[Reporting Services]]></category>
		<category><![CDATA[Presentation]]></category>
		<category><![CDATA[Reporting Services 2012]]></category>
		<category><![CDATA[SSRS]]></category>
		<category><![CDATA[T-SQL]]></category>

		<guid isPermaLink="false">http://blog.hoegaerden.be/2013/02/16/ssrs-deployment-generate-the-batch-script-through-sql/</guid>
		<description><![CDATA[This post is a follow-up to my presentation on Automating SSRS Deployments Using Built-in SQL Server Tools. During that presentation, I demonstrated that you can use the Reporting Services web service in a custom application to get a list of your deployed objects from the server.  Using that functionality you’d be able to generate the [...]]]></description>
			<content:encoded><![CDATA[<p><img style="background-image: none; margin: 0px 0px 5px 10px; padding-left: 0px; padding-right: 0px; display: inline; float: right; padding-top: 0px; border: 0px;" title="Source: http://commons.wikimedia.org/wiki/File:Estrangela.jpg" src="http://blog.hoegaerden.be/wp-content/uploads/image564.png" border="0" alt="Source: http://commons.wikimedia.org/wiki/File:Estrangela.jpg" width="118" height="187" align="right" />This post is a follow-up to my presentation on <a title="Automating SSRS Deployments Using Built-in SQL Server Tools" href="http://blog.hoegaerden.be/2013/01/21/sqlug-automating-ssrs-deployments/" target="_blank">Automating SSRS Deployments Using Built-in SQL Server Tools</a>.</p>
<p>During that presentation, I demonstrated that you can use the Reporting Services web service in a custom application to get a list of your deployed objects from the server.  Using that functionality you’d be able to generate the batch script containing the RS commands which in their turn use the RSS scripts which I’ve demonstrated.</p>
<p><em>For those who missed the presentation but ended up here nevertheless, you can </em><a title="Automating SSRS Deployment: Download" href="http://blog.hoegaerden.be/2013/01/30/automating-ssrs-deployment-download/" target="_blank"><em>download the slides and demo code through my other follow-up post</em></a><em>.</em></p>
<p>However, that’s not the only option!</p>
<p>In this post I’ll be using a technique which is one of my favorite methods of saving work.  Instead of typing a lot of code yourself, type just a bit of code to generate the code that you actually want.  Or, in other words and more concrete: today I’ll be using T-SQL to generate the RS commands for the batch file!</p>
<p>I will be querying some SSRS system tables, so take into account that the code may need changes for future versions of SQL Server, but I can confirm that it should work with both SQL Server 2008 and 2012.  SQL Server 2005 probably too but I don’t have that running anymore so can’t test it out.  Don’t hesitate to leave a comment if you can confirm that this will run on 2005.</p>
<p>The database on which you need to run these queries is called <strong>ReportServer</strong>, or <strong>ReportServer$YourInstance</strong> if you didn’t install it with the default instance name.</p>
<h2>Figuring It All Out</h2>
<p>One of the tougher parts of writing these queries was figuring out how the system tables can be linked with each other.  I studied some of the stored procedures and combined that knowledge with what I actually saw as content on my test system.  I’ll first share a couple of generic queries which may prove useful in case you just want to query the tables.  Then I’ll share the queries that will produce the RS statements.</p>
<p>Usually you’re only interested in the objects that belong to your project.  So each of the following queries has a filter to only show the content of my project folder, called <strong>Awesome Reports</strong>.</p>
<h3>List Of Objects Dependent On Data Source</h3>
<p>The following query shows a list of all objects that have a data source dependency.</p>
<pre class="code"><span style="color: green;">--list of objects dependent on data sources
</span><span style="color: blue;">select
    case </span><span style="color: teal;">C</span><span style="color: gray;">.</span><span style="color: teal;">[Type]
        </span><span style="color: blue;">when </span>1 <span style="color: blue;">then </span><span style="color: red;">'Folder'
        </span><span style="color: blue;">when </span>2 <span style="color: blue;">then </span><span style="color: red;">'Report'
        </span><span style="color: blue;">when </span>3 <span style="color: blue;">then </span><span style="color: red;">'Resource'
        </span><span style="color: blue;">when </span>4 <span style="color: blue;">then </span><span style="color: red;">'Linked Report'
        </span><span style="color: blue;">when </span>5 <span style="color: blue;">then </span><span style="color: red;">'Data Source'
        </span><span style="color: blue;">when </span>6 <span style="color: blue;">then </span><span style="color: red;">'Report Model'
        </span><span style="color: blue;">when </span>7 <span style="color: blue;">then </span><span style="color: red;">'???' </span><span style="color: green;">--post comment if you know what 7 means
        </span><span style="color: blue;">when </span>8 <span style="color: blue;">then </span><span style="color: red;">'Shared Dataset'
        </span><span style="color: blue;">when </span>9 <span style="color: blue;">then </span><span style="color: red;">'Report Part'
    </span><span style="color: blue;">end </span><span style="color: teal;">ObjectType
    </span><span style="color: gray;">, </span><span style="color: teal;">C</span><span style="color: gray;">.</span><span style="color: teal;">Name
    </span><span style="color: gray;">, </span><span style="color: teal;">DS</span><span style="color: gray;">.</span><span style="color: teal;">Name DataSourceName
    </span><span style="color: gray;">, </span><span style="color: teal;">DSET_SRC</span><span style="color: gray;">.</span><span style="color: teal;">Name DatasetExternalDataSourceName
    </span><span style="color: gray;">, </span><span style="color: teal;">PARENT</span><span style="color: gray;">.</span><span style="color: blue;">Path
from </span><span style="color: teal;">dbo</span><span style="color: gray;">.</span><span style="color: blue;">Catalog </span><span style="color: teal;">C
</span><span style="color: gray;">inner join </span><span style="color: teal;">dbo</span><span style="color: gray;">.</span><span style="color: blue;">Catalog </span><span style="color: teal;">PARENT </span><span style="color: blue;">on </span><span style="color: teal;">PARENT</span><span style="color: gray;">.</span><span style="color: teal;">ItemID </span><span style="color: gray;">= </span><span style="color: teal;">C</span><span style="color: gray;">.</span><span style="color: teal;">ParentID
</span><span style="color: gray;">inner join </span><span style="color: teal;">dbo</span><span style="color: gray;">.</span><span style="color: teal;">DataSource DS </span><span style="color: blue;">on </span><span style="color: teal;">C</span><span style="color: gray;">.</span><span style="color: teal;">ItemID </span><span style="color: gray;">= </span><span style="color: teal;">DS</span><span style="color: gray;">.</span><span style="color: teal;">ItemID
</span><span style="color: gray;">left outer join </span><span style="color: teal;">dbo</span><span style="color: gray;">.</span><span style="color: teal;">DataSets DSET </span><span style="color: blue;">on </span><span style="color: teal;">DSET</span><span style="color: gray;">.</span><span style="color: teal;">LinkID </span><span style="color: gray;">= </span><span style="color: teal;">C</span><span style="color: gray;">.</span><span style="color: teal;">ItemID
</span><span style="color: gray;">left outer join </span><span style="color: teal;">dbo</span><span style="color: gray;">.</span><span style="color: teal;">DataSource DSET_SRC </span><span style="color: blue;">on </span><span style="color: teal;">DSET_SRC</span><span style="color: gray;">.</span><span style="color: teal;">ItemID </span><span style="color: gray;">= </span><span style="color: teal;">DSET</span><span style="color: gray;">.</span><span style="color: teal;">ItemID
</span><span style="color: blue;">where </span><span style="color: teal;">C</span><span style="color: gray;">.</span><span style="color: blue;">Type </span><span style="color: gray;">&lt;&gt; </span>5 <span style="color: green;">--no data sources
    </span><span style="color: gray;">and </span><span style="color: teal;">C</span><span style="color: gray;">.</span><span style="color: blue;">Path </span><span style="color: gray;">like </span><span style="color: red;">'/Awesome Reports/%'
</span><span style="color: blue;">order by </span><span style="color: teal;">c</span><span style="color: gray;">.</span><span style="color: blue;">Type</span><span style="color: gray;">, </span><span style="color: teal;">C</span><span style="color: gray;">.</span><span style="color: teal;">Name
</span></pre>
<p>So what does this return on my test system?</p>
<p><img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="List of objects with data source dependency" src="http://blog.hoegaerden.be/wp-content/uploads/image565.png" border="0" alt="List of objects with data source dependency" width="693" height="58" /></p>
<p>In the case of a shared dataset, the <em>DataSetDataSource</em> name is what the data source is called inside the shared dataset.  To get to the real name of the data source, we need to join with the dbo.DataSource table through the dbo.DataSets table while using the right key fields.</p>
<h3>List Of Datasets With Linked Data Source</h3>
<p>The query below can be interesting to generate a list of your datasets with their data source dependency, including their location.</p>
<pre class="code"><span style="color: green;">--list of datasets with linked data source
</span><span style="color: blue;">select </span><span style="color: teal;">DSET</span><span style="color: gray;">.</span><span style="color: teal;">Name DatasetName
    </span><span style="color: gray;">, </span><span style="color: teal;">DSET_PARENT</span><span style="color: gray;">.</span><span style="color: blue;">Path </span><span style="color: teal;">DatasetLocation
    </span><span style="color: gray;">, </span><span style="color: teal;">DS</span><span style="color: gray;">.</span><span style="color: teal;">Name DataSourceName
    </span><span style="color: gray;">, </span><span style="color: teal;">DS_PARENT</span><span style="color: gray;">.</span><span style="color: blue;">Path </span><span style="color: teal;">DataSourceLocation
</span><span style="color: blue;">from </span><span style="color: teal;">dbo</span><span style="color: gray;">.</span><span style="color: teal;">DataSets DSET
</span><span style="color: gray;">inner join </span><span style="color: teal;">dbo</span><span style="color: gray;">.</span><span style="color: blue;">Catalog </span><span style="color: teal;">DSET_C </span><span style="color: blue;">on </span><span style="color: teal;">DSET_C</span><span style="color: gray;">.</span><span style="color: teal;">ItemID </span><span style="color: gray;">= </span><span style="color: teal;">DSET</span><span style="color: gray;">.</span><span style="color: teal;">LinkID
</span><span style="color: gray;">inner join </span><span style="color: teal;">dbo</span><span style="color: gray;">.</span><span style="color: blue;">Catalog </span><span style="color: teal;">DSET_PARENT </span><span style="color: blue;">on </span><span style="color: teal;">DSET_PARENT</span><span style="color: gray;">.</span><span style="color: teal;">ItemID </span><span style="color: gray;">= </span><span style="color: teal;">DSET_C</span><span style="color: gray;">.</span><span style="color: teal;">ParentID
</span><span style="color: gray;">inner join </span><span style="color: teal;">dbo</span><span style="color: gray;">.</span><span style="color: teal;">DataSource DS </span><span style="color: blue;">on </span><span style="color: teal;">DS</span><span style="color: gray;">.</span><span style="color: teal;">ItemID </span><span style="color: gray;">= </span><span style="color: teal;">DSET</span><span style="color: gray;">.</span><span style="color: teal;">ItemID
</span><span style="color: gray;">inner join </span><span style="color: teal;">dbo</span><span style="color: gray;">.</span><span style="color: blue;">Catalog </span><span style="color: teal;">DS_C </span><span style="color: blue;">on </span><span style="color: teal;">DS_C</span><span style="color: gray;">.</span><span style="color: teal;">ItemID </span><span style="color: gray;">= </span><span style="color: teal;">DS</span><span style="color: gray;">.</span><span style="color: teal;">Link
</span><span style="color: gray;">inner join </span><span style="color: teal;">dbo</span><span style="color: gray;">.</span><span style="color: blue;">Catalog </span><span style="color: teal;">DS_PARENT </span><span style="color: blue;">on </span><span style="color: teal;">DS_PARENT</span><span style="color: gray;">.</span><span style="color: teal;">ItemID </span><span style="color: gray;">= </span><span style="color: teal;">DS_C</span><span style="color: gray;">.</span><span style="color: teal;">ParentID
</span><span style="color: blue;">where </span><span style="color: teal;">DSET_C</span><span style="color: gray;">.</span><span style="color: blue;">Path </span><span style="color: gray;">like </span><span style="color: red;">'/Awesome Reports/%'
</span></pre>
<p>On my system I get the following result:</p>
<p><img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="List of datasets with their data source dependency" src="http://blog.hoegaerden.be/wp-content/uploads/image566.png" border="0" alt="List of datasets with their data source dependency" width="592" height="43" /></p>
<h2>Generating The RS Commands</h2>
<p>Using the knowledge we got by examining above queries, we can now build some queries that generate the RS commands.  And here they are:</p>
<pre class="code"><span style="color: green;">--generate DeployDataset commands
</span><span style="color: blue;">select </span><span style="color: red;">'%RS% -i "%SCRIPTLOCATION%\DeployDataset.rss" -s %REPORTSERVER% -l %TIMEOUT% -e %ENDPOINT% -v dataset="'
     </span><span style="color: gray;">+ </span><span style="color: teal;">DSET_C</span><span style="color: gray;">.</span><span style="color: teal;">Name </span><span style="color: gray;">+ </span><span style="color: red;">'" -v datasetLocation="' </span><span style="color: gray;">+ </span><span style="color: teal;">DSET_PARENT</span><span style="color: gray;">.</span><span style="color: blue;">Path
     </span><span style="color: gray;">+ </span><span style="color: red;">'" -v dataSourceName="' </span><span style="color: gray;">+ </span><span style="color: teal;">DS</span><span style="color: gray;">.</span><span style="color: teal;">Name
     </span><span style="color: gray;">+ </span><span style="color: red;">'" -v dataSourceLocation="' </span><span style="color: gray;">+ </span><span style="color: teal;">DS_PARENT</span><span style="color: gray;">.</span><span style="color: blue;">Path </span><span style="color: gray;">+ </span><span style="color: red;">'"'
</span><span style="color: blue;">from </span><span style="color: teal;">dbo</span><span style="color: gray;">.</span><span style="color: teal;">DataSets DSET
</span><span style="color: gray;">inner join </span><span style="color: teal;">dbo</span><span style="color: gray;">.</span><span style="color: blue;">Catalog </span><span style="color: teal;">DSET_C </span><span style="color: blue;">on </span><span style="color: teal;">DSET_C</span><span style="color: gray;">.</span><span style="color: teal;">ItemID </span><span style="color: gray;">= </span><span style="color: teal;">DSET</span><span style="color: gray;">.</span><span style="color: teal;">LinkID
</span><span style="color: gray;">inner join </span><span style="color: teal;">dbo</span><span style="color: gray;">.</span><span style="color: blue;">Catalog </span><span style="color: teal;">DSET_PARENT </span><span style="color: blue;">on </span><span style="color: teal;">DSET_PARENT</span><span style="color: gray;">.</span><span style="color: teal;">ItemID </span><span style="color: gray;">= </span><span style="color: teal;">DSET_C</span><span style="color: gray;">.</span><span style="color: teal;">ParentID
</span><span style="color: gray;">inner join </span><span style="color: teal;">dbo</span><span style="color: gray;">.</span><span style="color: teal;">DataSource DS </span><span style="color: blue;">on </span><span style="color: teal;">DS</span><span style="color: gray;">.</span><span style="color: teal;">ItemID </span><span style="color: gray;">= </span><span style="color: teal;">DSET</span><span style="color: gray;">.</span><span style="color: teal;">ItemID
</span><span style="color: gray;">inner join </span><span style="color: teal;">dbo</span><span style="color: gray;">.</span><span style="color: blue;">Catalog </span><span style="color: teal;">DS_C </span><span style="color: blue;">on </span><span style="color: teal;">DS_C</span><span style="color: gray;">.</span><span style="color: teal;">ItemID </span><span style="color: gray;">= </span><span style="color: teal;">DS</span><span style="color: gray;">.</span><span style="color: teal;">Link
</span><span style="color: gray;">inner join </span><span style="color: teal;">dbo</span><span style="color: gray;">.</span><span style="color: blue;">Catalog </span><span style="color: teal;">DS_PARENT </span><span style="color: blue;">on </span><span style="color: teal;">DS_PARENT</span><span style="color: gray;">.</span><span style="color: teal;">ItemID </span><span style="color: gray;">= </span><span style="color: teal;">DS_C</span><span style="color: gray;">.</span><span style="color: teal;">ParentID
</span><span style="color: blue;">where </span><span style="color: teal;">DSET_C</span><span style="color: gray;">.</span><span style="color: blue;">Path </span><span style="color: gray;">like </span><span style="color: red;">'/Awesome Reports/%'

</span><span style="color: green;">--generate DeployReport commands
</span><span style="color: blue;">select </span><span style="color: red;">'%RS% -i "%SCRIPTLOCATION%\DeployReport.rss" -s %REPORTSERVER% -l %TIMEOUT% -e %ENDPOINT% -v name="'
    </span><span style="color: gray;">+ </span><span style="color: teal;">C</span><span style="color: gray;">.</span><span style="color: teal;">Name </span><span style="color: gray;">+ </span><span style="color: red;">'" -v folder="' </span><span style="color: gray;">+ </span><span style="color: teal;">PARENT</span><span style="color: gray;">.</span><span style="color: blue;">Path </span><span style="color: gray;">+ </span><span style="color: red;">'"'
</span><span style="color: blue;">FROM </span><span style="color: teal;">dbo</span><span style="color: gray;">.</span><span style="color: blue;">Catalog AS </span><span style="color: teal;">C
</span><span style="color: gray;">inner join </span><span style="color: teal;">dbo</span><span style="color: gray;">.</span><span style="color: blue;">Catalog </span><span style="color: teal;">PARENT </span><span style="color: blue;">on </span><span style="color: teal;">PARENT</span><span style="color: gray;">.</span><span style="color: teal;">ItemID </span><span style="color: gray;">= </span><span style="color: teal;">C</span><span style="color: gray;">.</span><span style="color: teal;">ParentID
</span><span style="color: blue;">where </span><span style="color: teal;">C</span><span style="color: gray;">.</span><span style="color: blue;">Type </span><span style="color: gray;">= </span>2 <span style="color: green;">--report
    </span><span style="color: gray;">and </span><span style="color: teal;">C</span><span style="color: gray;">.</span><span style="color: blue;">Path </span><span style="color: gray;">like </span><span style="color: red;">'/Awesome Reports/%'

</span><span style="color: green;">--generate LinkReportToDataSource commands
</span><span style="color: blue;">select </span><span style="color: red;">'%RS% -i "%SCRIPTLOCATION%\LinkReportToDataSource.rss" -s %REPORTSERVER% -l %TIMEOUT% -e %ENDPOINT% -v reportName="'
    </span><span style="color: gray;">+ </span><span style="color: teal;">REP</span><span style="color: gray;">.</span><span style="color: teal;">Name </span><span style="color: gray;">+ </span><span style="color: red;">'" -v parent="' </span><span style="color: gray;">+ </span><span style="color: teal;">REP_PARENT</span><span style="color: gray;">.</span><span style="color: blue;">Path </span><span style="color: gray;">+ </span><span style="color: red;">'" -v dataSource="'
    </span><span style="color: gray;">+ </span><span style="color: teal;">DS</span><span style="color: gray;">.</span><span style="color: teal;">Name </span><span style="color: gray;">+ </span><span style="color: red;">'" -v dataSourceLocation="' </span><span style="color: gray;">+ </span><span style="color: teal;">DS_PARENT</span><span style="color: gray;">.</span><span style="color: blue;">Path </span><span style="color: gray;">+ </span><span style="color: red;">'"'
</span><span style="color: blue;">from </span><span style="color: teal;">dbo</span><span style="color: gray;">.</span><span style="color: blue;">Catalog AS </span><span style="color: teal;">REP
</span><span style="color: gray;">inner join </span><span style="color: teal;">dbo</span><span style="color: gray;">.</span><span style="color: blue;">Catalog </span><span style="color: teal;">REP_PARENT </span><span style="color: blue;">on </span><span style="color: teal;">REP_PARENT</span><span style="color: gray;">.</span><span style="color: teal;">ItemID </span><span style="color: gray;">= </span><span style="color: teal;">REP</span><span style="color: gray;">.</span><span style="color: teal;">ParentID
</span><span style="color: gray;">inner join </span><span style="color: teal;">dbo</span><span style="color: gray;">.</span><span style="color: teal;">DataSource DS </span><span style="color: blue;">on </span><span style="color: teal;">REP</span><span style="color: gray;">.</span><span style="color: teal;">ItemID </span><span style="color: gray;">= </span><span style="color: teal;">DS</span><span style="color: gray;">.</span><span style="color: teal;">ItemID
</span><span style="color: gray;">inner join </span><span style="color: teal;">dbo</span><span style="color: gray;">.</span><span style="color: blue;">Catalog </span><span style="color: teal;">DS_C </span><span style="color: blue;">on </span><span style="color: teal;">DS_C</span><span style="color: gray;">.</span><span style="color: teal;">ItemID </span><span style="color: gray;">= </span><span style="color: teal;">DS</span><span style="color: gray;">.</span><span style="color: teal;">Link
</span><span style="color: gray;">inner join </span><span style="color: teal;">dbo</span><span style="color: gray;">.</span><span style="color: blue;">Catalog </span><span style="color: teal;">DS_PARENT </span><span style="color: blue;">on </span><span style="color: teal;">DS_PARENT</span><span style="color: gray;">.</span><span style="color: teal;">ItemID </span><span style="color: gray;">= </span><span style="color: teal;">DS_C</span><span style="color: gray;">.</span><span style="color: teal;">ParentID
</span><span style="color: blue;">where </span><span style="color: teal;">REP</span><span style="color: gray;">.</span><span style="color: blue;">Type </span><span style="color: gray;">= </span>2 <span style="color: green;">--report
    </span><span style="color: gray;">and </span><span style="color: teal;">REP</span><span style="color: gray;">.</span><span style="color: blue;">Path </span><span style="color: gray;">like </span><span style="color: red;">'/Awesome Reports/%'
</span><span style="color: blue;">order by </span><span style="color: teal;">REP</span><span style="color: gray;">.</span><span style="color: teal;">Name </span><span style="color: blue;">asc</span><span style="color: gray;">, </span><span style="color: teal;">DS</span><span style="color: gray;">.</span><span style="color: teal;">Name </span><span style="color: blue;">asc

</span><span style="color: green;">--generate LinkReportToDataset commands
</span><span style="color: blue;">SELECT </span><span style="color: red;">'%RS% -i "%SCRIPTLOCATION%\LinkReportToDataset.rss" -s %REPORTSERVER% -l %TIMEOUT% -e %ENDPOINT% -v reportName="'
    </span><span style="color: gray;">+ </span><span style="color: teal;">REP</span><span style="color: gray;">.</span><span style="color: teal;">Name </span><span style="color: gray;">+ </span><span style="color: red;">'" -v reportLocation="' </span><span style="color: gray;">+ </span><span style="color: teal;">REP_PARENT</span><span style="color: gray;">.</span><span style="color: blue;">Path </span><span style="color: gray;">+ </span><span style="color: red;">'" -v datasetName="'
    </span><span style="color: gray;">+ </span><span style="color: teal;">DSET</span><span style="color: gray;">.</span><span style="color: teal;">Name </span><span style="color: gray;">+ </span><span style="color: red;">'" -v datasetLocation="' </span><span style="color: gray;">+ </span><span style="color: teal;">DSET_PARENT</span><span style="color: gray;">.</span><span style="color: blue;">Path </span><span style="color: gray;">+ </span><span style="color: red;">'"'
</span><span style="color: blue;">from </span><span style="color: teal;">dbo</span><span style="color: gray;">.</span><span style="color: blue;">Catalog AS </span><span style="color: teal;">REP
</span><span style="color: gray;">inner join </span><span style="color: teal;">dbo</span><span style="color: gray;">.</span><span style="color: blue;">Catalog </span><span style="color: teal;">REP_PARENT </span><span style="color: blue;">on </span><span style="color: teal;">REP_PARENT</span><span style="color: gray;">.</span><span style="color: teal;">ItemID </span><span style="color: gray;">= </span><span style="color: teal;">REP</span><span style="color: gray;">.</span><span style="color: teal;">ParentID
</span><span style="color: gray;">inner join </span><span style="color: teal;">dbo</span><span style="color: gray;">.</span><span style="color: teal;">DataSets </span><span style="color: blue;">AS </span><span style="color: teal;">DSET </span><span style="color: blue;">ON </span><span style="color: teal;">REP</span><span style="color: gray;">.</span><span style="color: teal;">ItemID </span><span style="color: gray;">= </span><span style="color: teal;">DSET</span><span style="color: gray;">.</span><span style="color: teal;">ItemID
</span><span style="color: gray;">inner join </span><span style="color: teal;">dbo</span><span style="color: gray;">.</span><span style="color: blue;">Catalog </span><span style="color: teal;">DSET_C </span><span style="color: blue;">on </span><span style="color: teal;">DSET_C</span><span style="color: gray;">.</span><span style="color: teal;">ItemID </span><span style="color: gray;">= </span><span style="color: teal;">DSET</span><span style="color: gray;">.</span><span style="color: teal;">LinkID
</span><span style="color: gray;">inner join </span><span style="color: teal;">dbo</span><span style="color: gray;">.</span><span style="color: blue;">Catalog </span><span style="color: teal;">DSET_PARENT </span><span style="color: blue;">on </span><span style="color: teal;">DSET_PARENT</span><span style="color: gray;">.</span><span style="color: teal;">ItemID </span><span style="color: gray;">= </span><span style="color: teal;">DSET_C</span><span style="color: gray;">.</span><span style="color: teal;">ParentID
</span><span style="color: blue;">where </span><span style="color: teal;">REP</span><span style="color: gray;">.</span><span style="color: blue;">Path </span><span style="color: gray;">like </span><span style="color: red;">'/Awesome Reports/%'
</span></pre>
<p>And here’s the output:</p>
<p><a href="http://blog.hoegaerden.be/wp-content/uploads/image567.png"><img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="Generating the RS commands" src="http://blog.hoegaerden.be/wp-content/uploads/image_thumb111.png" border="0" alt="Generating the RS commands" width="700" height="120" /></a></p>
<h2>Conclusion</h2>
<p>It may not be a recommended practice to use the Reporting Services system tables, but as long as you’re only reading them and you keep in mind that your code may break in a next version of SQL Server, they can surely be useful!</p>
<p>Have fun!</p>
<p>Valentino.</p>
<p><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fblog.hoegaerden.be%2F2013%2F02%2F16%2Fssrs-deployment-generate-the-batch-script-through-sql%2F&amp;title=SSRS%20Deployment%3A%20Generate%20The%20Batch%20Script%20Through%20SQL%21" id="wpa2a_2"><img src="http://blog.hoegaerden.be/wp-content/plugins/add-to-any/share_save_171_16.png" width="171" height="16" alt="Share"/></a></p>]]></content:encoded>
			<wfw:commentRss>http://blog.hoegaerden.be/2013/02/16/ssrs-deployment-generate-the-batch-script-through-sql/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Enhanced Duplicate Key Error Message</title>
		<link>http://blog.hoegaerden.be/2011/11/27/enhanced-duplicate-key-error-message/</link>
		<comments>http://blog.hoegaerden.be/2011/11/27/enhanced-duplicate-key-error-message/#comments</comments>
		<pubDate>Sun, 27 Nov 2011 15:30:09 +0000</pubDate>
		<dc:creator>Valentino Vranken</dc:creator>
				<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[T-SQL]]></category>
		<category><![CDATA[SQL Server 2008 R2]]></category>

		<guid isPermaLink="false">http://blog.hoegaerden.be/2011/11/27/enhanced-duplicate-key-error-message/</guid>
		<description><![CDATA[I came across an interesting little enhancement which I’d like to share with you.  I’m sure you’re all familiar with the following error message: Msg 2601, Level 14, State 1, Line 4 Cannot insert duplicate key row in object &#8216;dbo.#t&#8217; with unique index &#8216;PK_Unique&#8217;. That’s right, the “hey, you’re inserting junk, stop that right now!” [...]]]></description>
			<content:encoded><![CDATA[<p>I came across an interesting little enhancement which I’d like to share with you.  I’m sure you’re all familiar with the following error message:</p>
<blockquote><p>Msg 2601, Level 14, State 1, Line 4<br />
Cannot insert duplicate key row in object &#8216;dbo.#t&#8217; with unique index &#8216;PK_Unique&#8217;.</p></blockquote>
<p>That’s right, the “hey, you’re inserting junk, stop that right now!” message.</p>
<p>However, earlier this week I encountered a variation of that error in our ETL logs:</p>
<blockquote><p>Msg 2601, Level 14, State 1, Line 3<br />
Cannot insert duplicate key row in object &#8216;dbo.#t&#8217; with unique index &#8216;PK_Unique&#8217;. <strong>The duplicate key value is (1, 2).</strong></p></blockquote>
<p>I’m sure I don’t need to explain how useful that extra sentence can be, right?  So, then I started digging because obviously I want that error to always include the offending values.</p>
<p>Recently our database back-end servers have been replaced with new machines running SQL Server 2008 R2 SP1.  And indeed, as of R2 SP1, this error message has gotten an upgrade!</p>
<p>If you want to check for yourself, the following code snippet can be used to generate the error:</p>
<pre class="code"><span style="color: blue;">create table </span>#t <span style="color: gray;">(</span>PK1 <span style="color: blue;">int</span><span style="color: gray;">, </span>PK2 <span style="color: blue;">int</span><span style="color: gray;">);
</span><span style="color: blue;">create unique index </span>PK_Unique <span style="color: blue;">on </span>#t<span style="color: gray;">(</span>PK1<span style="color: gray;">, </span>PK2<span style="color: gray;">);
</span><span style="color: blue;">insert into </span>#t <span style="color: blue;">values </span><span style="color: gray;">(</span>1<span style="color: gray;">, </span>2<span style="color: gray;">),(</span>1<span style="color: gray;">, </span>2<span style="color: gray;">);</span></pre>
<p>Now, message 2601 is not the only one complaining about duplicate keys.  Another example can be generated using the following query:</p>
<pre class="code"><span style="color: blue;">create table </span>#t <span style="color: gray;">(</span>PK1 <span style="color: blue;">int unique</span><span style="color: gray;">, </span>PK2 <span style="color: blue;">int unique</span><span style="color: gray;">);
</span><span style="color: blue;">insert into </span>#t <span style="color: blue;">values </span><span style="color: gray;">(</span>1<span style="color: gray;">, </span>2<span style="color: gray;">),(</span>1<span style="color: gray;">, </span>2<span style="color: gray;">);</span></pre>
<p>Executing that snippet on SQL Server 2008 R2 SP1 results in this error:</p>
<blockquote><p>Msg 2627, Level 14, State 1, Line 1</p>
<p>Violation of UNIQUE KEY constraint &#8216;UQ__#t________C5776555123EB7A3&#8242;. Cannot insert duplicate key in object &#8216;dbo.#t&#8217;. <strong>The duplicate key value is (2).</strong></p></blockquote>
<p>Do you notice the difference?  The message did indeed get an upgrade, but is not able to support a simultaneous violation on multiple columns, while msg 2601 can.  Hopefully they can get that fixed by SQL Server 2012 RTM!</p>
<p>Out of curiosity I decided to get a closer look at other possible variations on this error and dug into the <strong><em>sys.messages</em></strong> table using this query:</p>
<pre class="code"><span style="color: blue;">select </span><span style="color: gray;">* </span><span style="color: blue;">from </span><span style="color: green;">sys</span><span style="color: gray;">.</span><span style="color: green;">messages
</span><span style="color: blue;">where text </span><span style="color: gray;">like (</span><span style="color: red;">'%duplicate key%'</span><span style="color: gray;">);</span></pre>
<p>Results of that query:</p>
<p><a href="http://blog.hoegaerden.be/wp-content/uploads/image429.png"><img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="All error messages containing &quot;duplicate key&quot;" src="http://blog.hoegaerden.be/wp-content/uploads/image_thumb103.png" border="0" alt="All error messages containing &quot;duplicate key&quot;" width="700" height="70" /></a></p>
<p>So, there’s one more to investigate: 1505.  To get that error generated is not that complicated.  We actually already have all the statements, just need to use them in the right order, like so:</p>
<pre class="code"><span style="color: blue;">create table </span>#t <span style="color: gray;">(</span>PK1 <span style="color: blue;">int</span><span style="color: gray;">, </span>PK2 <span style="color: blue;">int</span><span style="color: gray;">);
</span><span style="color: blue;">insert into </span>#t <span style="color: blue;">values </span><span style="color: gray;">(</span>1<span style="color: gray;">, </span>2<span style="color: gray;">),(</span>1<span style="color: gray;">, </span>2<span style="color: gray;">);
</span><span style="color: blue;">create unique index </span>PK_Unique <span style="color: blue;">on </span>#t<span style="color: gray;">(</span>PK1<span style="color: gray;">, </span>PK2<span style="color: gray;">);</span></pre>
<p>And what do we get?</p>
<blockquote><p>Msg 1505, Level 16, State 1, Line 1</p>
<p>The CREATE UNIQUE INDEX statement terminated because a duplicate key was found for the object name &#8216;dbo.#t__________________________________________________________________________________________________________________00000000001D&#8217; and the index name &#8216;PK_Unique&#8217;. <strong>The duplicate key value is (1, 2).</strong></p></blockquote>
<p>Nice, message 1505 also supports multiple columns, that makes two out of three!</p>
<p>Ow, don’t forget to clean up (yes, I even do that for temporary tables).</p>
<pre class="code"><span style="color: blue;">drop table </span>#t<span style="color: gray;">;</span></pre>
<h2>Additional Info</h2>
<p>Some further digging around on the internet brought me to the following suggestion on Microsoft Connect: <a title="Duplicate Key Values" href="http://connect.microsoft.com/SQLServer/feedback/details/470542/duplicate-key-values" target="_blank">Duplicate Key Values by Anton Plotnikov</a>.  Looks like our enhanced statements are the result of that request.</p>
<p>There’s another suggestion as well: <a title="Tweak To Duplicate Key Message" href="http://connect.microsoft.com/SQLServer/feedback/details/679366/tweak-to-duplicate-key-message" target="_blank">Tweak To Duplicate Key Message by Louis Davidson</a>.  Louis suggests to have all offending values added to the message as well, but in a different situation than when dealing with multiple unique columns.  You can create one insert statement that inserts more than one record with offending values, and that’s the situation he’s referring to.  If you feel that’s important, get over to Connect and cast vote!  I do think that the number of values would need to get limited in that case, we might end up with really long error messages otherwise.</p>
<p>One last link, also by Louis Davidson.  He also wrote a <a title="Denali Enhancement–Duplicate Key Error Message" href="http://sqlblog.com/blogs/louis_davidson/archive/2011/07/14/denali-enhancement-duplicate-key-error-message.aspx" target="_blank">blog post about the error message</a> when he found out about it in Denali.</p>
<p>Have fun!</p>
<p>Valentino.</p>
<p><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fblog.hoegaerden.be%2F2011%2F11%2F27%2Fenhanced-duplicate-key-error-message%2F&amp;title=Enhanced%20Duplicate%20Key%20Error%20Message" id="wpa2a_4"><img src="http://blog.hoegaerden.be/wp-content/plugins/add-to-any/share_save_171_16.png" width="171" height="16" alt="Share"/></a></p>]]></content:encoded>
			<wfw:commentRss>http://blog.hoegaerden.be/2011/11/27/enhanced-duplicate-key-error-message/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Record Count For All Tables In Database</title>
		<link>http://blog.hoegaerden.be/2011/05/21/record-count-for-all-tables-in-database/</link>
		<comments>http://blog.hoegaerden.be/2011/05/21/record-count-for-all-tables-in-database/#comments</comments>
		<pubDate>Sat, 21 May 2011 08:46:33 +0000</pubDate>
		<dc:creator>Valentino Vranken</dc:creator>
				<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[T-SQL]]></category>
		<category><![CDATA[Script]]></category>

		<guid isPermaLink="false">http://blog.hoegaerden.be/2011/05/21/record-count-for-all-tables-in-database/</guid>
		<description><![CDATA[In this post I’m actually just going to repeat what I answered to a question on Experts Exchange. The OP was having an issue getting a record count of all tables in his database.  It was working through the Management Studio, using the undocumented but well-known sp_MSforeachtable stored procedure. exec sp_MSforeachtable 'select ''?'' TableName, count(*) [...]]]></description>
			<content:encoded><![CDATA[<p>In this post I’m actually just going to repeat what I answered to <a title="EE Q about getting a row count of each table in database" href="http://www.experts-exchange.com/Microsoft/Development/MS-SQL-Server/SQL_Server_2008/Q_27038565.html" target="_blank">a question on Experts Exchange</a>.</p>
<p>The OP was having an issue getting a record count of all tables in his database.  It was working through the Management Studio, using the undocumented but well-known sp_MSforeachtable stored procedure.</p>
<pre class="code"><span style="color: blue;">exec </span><span style="color: maroon;">sp_MSforeachtable </span><span style="color: red;">'select ''?'' TableName, count(*) Cnt from ?'</span><span style="color: gray;">;</span></pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>However, the OP needed the list of record counts inside an Integration Services package.  And this didn’t work out as expected, because sp_MSforeachtable uses a temporary table, something that SSIS does not like.</p>
<p>So I proposed to use a script similar to the following, possibly in a stored procedure.</p>
<p><a href="http://11011.net/software/vspaste"></a></p>
<pre class="code"><span style="color: green;">/*
DESCRIPTION: Returns a list of record counts for each table in the database
AUTHOR:         Valentino Vranken
CREATED:     2011/05/21
VERSION:     1.0
COPIED FROM: http://blog.hoegaerden.be
*/
</span><span style="color: blue;">declare </span>@tempTable <span style="color: blue;">table
</span><span style="color: gray;">(
    </span>TableSchema <span style="color: blue;">nvarchar</span><span style="color: gray;">(</span>256<span style="color: gray;">),
    </span>TableName <span style="color: blue;">nvarchar</span><span style="color: gray;">(</span>256<span style="color: gray;">),
    </span>Cnt <span style="color: blue;">bigint
</span><span style="color: gray;">);

</span><span style="color: blue;">declare </span>@sql <span style="color: blue;">nvarchar</span><span style="color: gray;">(</span>4000<span style="color: gray;">);
</span><span style="color: blue;">declare </span>@tableSchema <span style="color: blue;">nvarchar</span><span style="color: gray;">(</span>256<span style="color: gray;">);
</span><span style="color: blue;">declare </span>@tableName <span style="color: blue;">nvarchar</span><span style="color: gray;">(</span>256<span style="color: gray;">);
</span><span style="color: blue;">declare </span>@columnName <span style="color: blue;">sysname</span><span style="color: gray;">;
</span><span style="color: blue;">declare </span>@cnt <span style="color: blue;">bigint</span><span style="color: gray;">;

</span><span style="color: blue;">declare </span>tableCursor <span style="color: blue;">cursor for
    select </span>TABLE_SCHEMA<span style="color: gray;">, </span>TABLE_NAME <span style="color: blue;">from </span><span style="color: green;">INFORMATION_SCHEMA</span><span style="color: gray;">.</span><span style="color: green;">TABLES
    </span><span style="color: blue;">where </span>TABLE_TYPE <span style="color: gray;">= </span><span style="color: red;">'BASE TABLE'</span><span style="color: gray;">;

</span><span style="color: blue;">open </span>tableCursor<span style="color: gray;">;

</span><span style="color: blue;">fetch next from </span>tableCursor <span style="color: blue;">into </span>@tableSchema<span style="color: gray;">, </span>@tableName<span style="color: gray;">;

</span><span style="color: blue;">while </span><span style="color: magenta;">@@FETCH_STATUS </span><span style="color: gray;">= </span>0
<span style="color: blue;">begin
    set </span>@sql <span style="color: gray;">= </span><span style="color: red;">'select @cnt = COUNT(*) from [' </span><span style="color: gray;">+ </span>@tableSchema <span style="color: gray;">+ </span><span style="color: red;">'].[' </span><span style="color: gray;">+ </span>@tableName <span style="color: gray;">+ </span><span style="color: red;">']'</span><span style="color: gray;">;

    </span><span style="color: blue;">exec </span><span style="color: maroon;">sp_executesql </span>@sql<span style="color: gray;">, </span><span style="color: red;">N'@cnt bigint output'</span><span style="color: gray;">, </span>@cnt <span style="color: gray;">= </span>@cnt <span style="color: blue;">output</span><span style="color: gray;">;

    </span><span style="color: blue;">insert into </span>@tempTable <span style="color: blue;">select </span>@tableSchema<span style="color: gray;">, </span>@tableName<span style="color: gray;">, </span>@cnt<span style="color: gray;">;

    </span><span style="color: blue;">fetch next from </span>tableCursor <span style="color: blue;">into </span>@tableSchema<span style="color: gray;">, </span>@tableName<span style="color: gray;">;
</span><span style="color: blue;">end</span><span style="color: gray;">;

</span><span style="color: blue;">close </span>tableCursor<span style="color: gray;">;
</span><span style="color: blue;">deallocate </span>tableCursor<span style="color: gray;">;

</span><span style="color: blue;">select </span><span style="color: gray;">* </span><span style="color: blue;">from </span>@tempTable<span style="color: gray;">;</span></pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>Yeah, I know, cursors are bad… But hey, at least it works!  Just don’t schedule it for execution every five seconds.</p>
<p><em>I seem to be repeating myself.  I wrote something similar when I posted <a title="Script: Find All Empty Columns In Database" href="http://blog.hoegaerden.be/2009/02/15/script-find-all-empty-columns-in-database/" target="_blank">the script about finding empty columns</a> many months ago.</em></p>
<p>The most important part of the script is the following:</p>
<p><a href="http://11011.net/software/vspaste"></a></p>
<pre class="code"><span style="color: blue;">select </span>TABLE_SCHEMA<span style="color: gray;">, </span>TABLE_NAME <span style="color: blue;">from </span><span style="color: green;">INFORMATION_SCHEMA</span><span style="color: gray;">.</span><span style="color: green;">TABLES
</span><span style="color: blue;">where </span>TABLE_TYPE <span style="color: gray;">= </span><span style="color: red;">'BASE TABLE'</span><span style="color: gray;">;</span></pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>This is using one of the information schema views to retrieve a list of tables in the database.  The INFORMATION_SCHEMA.TABLES view contains a list of tables and views, so the where clause is needed to filter out the views.  INFORMATION_SCHEMA.TABLES is just one of <a title="Information Schema Views (Transact-SQL)" href="http://msdn.microsoft.com/en-us/library/ms186778.aspx" target="_blank">many interesting views available in SQL Server</a>, and should always be used in favor of the sys tables.</p>
<p>What does the output look like?  Here’s part of the output when executed on the AdventureWorks2008R2 database:</p>
<p><img style="display: inline; border: 0px;" title="Record count of all tables in the AdventureWorks2008R2 database (although not all are shown, you get the idea)" src="http://blog.hoegaerden.be/wp-content/uploads/image415.png" border="0" alt="Record count of all tables in the AdventureWorks2008R2 database (although not all are shown, you get the idea)" width="376" height="183" /></p>
<p>Have fun!</p>
<p>Valentino.</p>
<p><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fblog.hoegaerden.be%2F2011%2F05%2F21%2Frecord-count-for-all-tables-in-database%2F&amp;title=Record%20Count%20For%20All%20Tables%20In%20Database" id="wpa2a_6"><img src="http://blog.hoegaerden.be/wp-content/plugins/add-to-any/share_save_171_16.png" width="171" height="16" alt="Share"/></a></p>]]></content:encoded>
			<wfw:commentRss>http://blog.hoegaerden.be/2011/05/21/record-count-for-all-tables-in-database/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Where&#8217;s The (Windows) User?</title>
		<link>http://blog.hoegaerden.be/2011/01/14/wheres-the-windows-user/</link>
		<comments>http://blog.hoegaerden.be/2011/01/14/wheres-the-windows-user/#comments</comments>
		<pubDate>Fri, 14 Jan 2011 17:48:46 +0000</pubDate>
		<dc:creator>Valentino Vranken</dc:creator>
				<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[data]]></category>
		<category><![CDATA[T-SQL]]></category>

		<guid isPermaLink="false">http://blog.hoegaerden.be/2011/01/14/wheres-the-windows-user/</guid>
		<description><![CDATA[Have you ever needed to write a script that would store the Windows login of the user that executes the script in a table, without hard-coding it?  And you couldn’t find your way through the user jungle of T-SQL?  Then read on! A normal approach would be to rely on the IntelliSense feature of SSMS [...]]]></description>
			<content:encoded><![CDATA[<p>Have you ever needed to write a script that would store the Windows login of the user that executes the script in a table, without hard-coding it?  And you couldn’t find your way through the user jungle of T-SQL?  Then read on!</p>
<p>A normal approach would be to rely on the IntelliSense feature of SSMS and start typing something like:</p>
<pre class="code"><span style="color: blue;">select use</span></pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>Which gives you a dropdown like:</p>
<p><img style="display: inline; border-width: 0px;" title="image" src="http://blog.hoegaerden.be/wp-content/uploads/image363.png" border="0" alt="image" width="540" height="209" /></p>
<p>Cool you think, there are four system functions to return something user-related, one of them would be the Windows user, right?  Well, wrong.</p>
<p>Let’s find out what these functions actually return, and locate a method that does return what we require.</p>
<h2>The Different User-Related System Functions</h2>
<h3>USER</h3>
<p>According to the BOL:</p>
<blockquote><p>Allows a system-supplied value for the database user name of the current user to be inserted into a table when no default value is specified.</p></blockquote>
<p>So, how many times did you read that?  And do you now know what you’ll get?  Me neither.  As far as I understand it, you’ll get the database user name of the connection context.  Not what we need.</p>
<h3>USER_ID</h3>
<p>According to BOL:</p>
<blockquote><p>Returns the identification number for a database user.</p></blockquote>
<p>The function accepts one argument that accepts a string representing a username.  When no argument is provided, it will return the user ID of the execution context.</p>
<p>But that’s not important because the BOL also mentions that it will be removed in a future version and that you thus shouldn’t use it anymore.  No problem if you need it though, its replacement is called DATABASE_PRINCIPAL_ID.</p>
<h3>USER_NAME</h3>
<p>According to BOL:</p>
<blockquote><p>Returns a database user name from a specified identification number.</p></blockquote>
<p>This is the inverse of USER_ID.  It accepts one argument representing the user ID.  When omitted, the user of the current execution context will be returned.  Which is actually the equivalent of the USER function.</p>
<p>Still no Windows user though, so let’s move on to the next one.</p>
<h3>USER_SID</h3>
<p>Not documented in the BOL.</p>
<p>It seems to represent the <strong>sid </strong>column found through following query:</p>
<p><a href="http://11011.net/software/vspaste"></a></p>
<pre class="code"><span style="color: blue;">select </span><span style="color: gray;">* </span><span style="color: blue;">from </span><span style="color: green;">sys</span><span style="color: gray;">.</span><span style="color: green;">sysusers</span></pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>And the sys.sysusers view is documented.  Apparently “sid” stands for “security identifier” and it’s a varbinary.  But not only is the view documented, it is also deprecated.  You should use the following view instead:</p>
<pre class="code"><span style="color: blue;">select </span><span style="color: gray;">* </span><span style="color: blue;">from </span><span style="color: green;">sys</span><span style="color: gray;">.</span><span style="color: green;">database_principals</span></pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>That query returns a list of principals in the current database, and one of the fields is indeed our dear sid.  However, we can now conclude that sid is definitely not what we’re looking for in this little quest, it doesn’t even closely resemble a Windows user name.  So let’s move on again!</p>
<h3>CURRENT_USER</h3>
<p>Hmm, “current user”, will this finally be the Windows user with which I’m running my queries?</p>
<p>According to BOL:</p>
<blockquote><p>Returns the name of the current user. This function is equivalent to USER_NAME().</p></blockquote>
<p>Equivalent to USER_NAME, so still not what we’re looking for.</p>
<h3>SESSION_USER</h3>
<p>According to BOL:</p>
<blockquote><p>SESSION_USER returns the user name of the current context in the current database.</p></blockquote>
<p>You can probably guess by now that again this is not what we’re searching for.  Next.</p>
<h3>SYSTEM_USER</h3>
<p>According to BOL:</p>
<blockquote><p>Allows a system-supplied value for the current login to be inserted into a table when no default value is specified.</p></blockquote>
<p>Ah, finally a description that contains the word “login”!  And indeed, here’s what it returns:</p>
<blockquote><p>ORDINA\VaVr</p></blockquote>
<p>That is indeed my Windows user.  Mission accomplished!</p>
<p><strong><span style="text-decoration: underline;">Please note:</span></strong> if you’re running this through a window connected via a SQL Server login, you will get that login as opposed to your Windows login.  And if you get ‘sa’ while you’re connected to a production server you should look into making some security changes. <img src='http://blog.hoegaerden.be/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
<h3>SUSER_NAME and SUSER_SNAME</h3>
<p>Two more similar functions exist: SUSER_NAME and SUSER_SNAME.</p>
<p>Both return a login name as well, and both accept a parameter.  When executed without parameter, they return the login name of the current user.  Their only difference is the parameter that they accept.  SUSER_NAME accepts the “login identification number of the user”, which is an <strong>int</strong>, and SUSER_SNAME accepts the “login security identification number”, a <strong>varbinary(85)</strong> (remember sid from above?).</p>
<h3>Quick Query</h3>
<p>The following query shows how to use all the functions, with the default values for any parameters.</p>
<pre class="code"><span style="color: blue;">select </span><span style="color: magenta;">USER </span><span style="color: blue;">as </span>cUSER<span style="color: gray;">, </span><span style="color: magenta;">USER_ID</span><span style="color: gray;">() </span><span style="color: blue;">as </span>cUSER_ID<span style="color: gray;">, </span><span style="color: magenta;">USER_NAME</span><span style="color: gray;">() </span><span style="color: blue;">as </span><span style="color: magenta;">USER_NAME</span><span style="color: gray;">,
    </span><span style="color: magenta;">USER_SID</span><span style="color: gray;">() </span><span style="color: blue;">as </span>cUSER_SID<span style="color: gray;">, </span><span style="color: magenta;">CURRENT_USER </span><span style="color: blue;">as </span>cCURRENT_USER<span style="color: gray;">,
    </span><span style="color: magenta;">SESSION_USER </span><span style="color: blue;">as </span>cSESSION_USER<span style="color: gray;">, </span><span style="color: magenta;">SYSTEM_USER </span><span style="color: blue;">as </span>cSYSTEM_USER<span style="color: gray;">,
    </span><span style="color: magenta;">SUSER_NAME</span><span style="color: gray;">() </span><span style="color: blue;">as </span>cSUSER_NAME<span style="color: gray;">, </span><span style="color: magenta;">SUSER_SNAME</span><span style="color: gray;">() </span><span style="color: blue;">as </span>cSUSER_SNAME</pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>Have fun!</p>
<p><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fblog.hoegaerden.be%2F2011%2F01%2F14%2Fwheres-the-windows-user%2F&amp;title=Where%E2%80%99s%20The%20%28Windows%29%20User%3F" id="wpa2a_8"><img src="http://blog.hoegaerden.be/wp-content/plugins/add-to-any/share_save_171_16.png" width="171" height="16" alt="Share"/></a></p>]]></content:encoded>
			<wfw:commentRss>http://blog.hoegaerden.be/2011/01/14/wheres-the-windows-user/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Calculating LastXMonths Aggregations Using T-SQL and SSIS</title>
		<link>http://blog.hoegaerden.be/2010/08/26/calculating-lastxmonths-aggregations-using-t-sql-and-ssis/</link>
		<comments>http://blog.hoegaerden.be/2010/08/26/calculating-lastxmonths-aggregations-using-t-sql-and-ssis/#comments</comments>
		<pubDate>Thu, 26 Aug 2010 20:25:19 +0000</pubDate>
		<dc:creator>Valentino Vranken</dc:creator>
				<category><![CDATA[Integration Services]]></category>
		<category><![CDATA[SQLServerPedia Syndication]]></category>
		<category><![CDATA[T-SQL]]></category>
		<category><![CDATA[data]]></category>
		<category><![CDATA[SSIS]]></category>

		<guid isPermaLink="false">http://blog.hoegaerden.be/2010/08/26/calculating-lastxmonths-aggregations-using-t-sql-and-ssis/</guid>
		<description><![CDATA[With the holidays I haven’t been able to write much.  So I’ll make up for it with this +3000 words article.  If you’re reading this early in the morning, you’d better get a double espresso first In this article I will demonstrate a method that can be used to calculate aggregations over a certain period [...]]]></description>
			<content:encoded><![CDATA[<p>With the holidays I haven’t been able to write much.  So I’ll make up for it with this +3000 words article.  If you’re reading this early in the morning, you’d better get a double espresso first <img src='http://blog.hoegaerden.be/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' /> </p>
<p>In this article I will demonstrate a method that can be used to calculate aggregations over a certain period of time in the past, or LastXMonths aggregations as I’m calling them throughout the article.  I’ll be using T-SQL, SQL Server Integration Services and a relational database as source.  More specifically I will be using the Merge Join data transformation in SSIS, and Common Table Expressions in T-SQL.</p>
<p>Version-wise I’m using SQL Server 2008 R2, but this method should work as of SQL Server 2005.  Furthermore I’m using the Contoso DWH, <a title="Microsoft Contoso BI Demo Dataset for Retail Industry" href="http://www.microsoft.com/downloads/details.aspx?FamilyID=868662DC-187A-4A85-B611-B7DF7DC909FC" target="_blank">available for download at the Microsoft Download Center</a>.  (In case you’re wondering, it’s the .BAK file.)</p>
<p>You can <a title="MergeJoin.dtsx" href="http://cid-81c8b064cbbe1698.office.live.com/self.aspx/.Public/blog/MergeJoin.dtsx" target="_blank">download the finished SSIS package from my Skydrive</a>.  (The file is called MergeJoin.dtsx.)</p>
<h2>The Scenario</h2>
<p>Let’s say we’ve got a relational database containing some sales figures.  Management has asked for sales-related data to be available somewhere for easy analysis.  Ideally a cube would be built for that purpose but as budgets are currently tight, a temporary solution needs to be provided meanwhile.  So it’s been decided that an additional table will be created, populated with the exact data as required by management.  This table should contain all details (number of items and amount of the sale) about products sold, grouped by the date of the sale, the zip code of the place where the sale occurred and the category of the product.</p>
<p>Furthermore, each record should contain the sum of all sales of the last month for the zip code and product category of each particular record.  Two additional aggregations should calculate the sales for the last three months and last six months.</p>
<h3>A Simple Example</h3>
<p>To make sure we’re all on the same track on the requirements, here’s a small example to illustrate the expected outcome.</p>
<p><a href="http://blog.hoegaerden.be/wp-content/uploads/image330.png"><img style="display: inline; border-width: 0px;" title="Small example displaying the expected outcome of the process" src="http://blog.hoegaerden.be/wp-content/uploads/image_thumb73.png" border="0" alt="Small example displaying the expected outcome of the process" width="676" height="79" /></a></p>
<p>I’ve omitted the SalesAmount numbers for readability reasons.  The records are ordered chronologically, with the oldest first.  As you can see, the bottom record shows 16 as value for Last6MSalesQuantity.  This is the result of the SalesQuantity of the current record and the SalesQuantity of the previous record, which happens to fall within the timespan of the lowest record’s SaleDate going back six months.  The two other records do not fall within the six months timespan and are thus not included in the sum for the Last6MSalesQuantity of that bottom record.</p>
<h2>Fetching The Data Into A Table</h2>
<p>Our scenario requires that the sales figures are calculated and put into a new table.  Let’s first start with creating the queries to fetch the data.</p>
<h3>Step 1: The Daily Numbers</h3>
<p>The easiest part are the daily sales numbers.  These can be retrieved fairly easy from the Contoso data warehouse, just by using a GROUP BY clause as shown in the following query.</p>
<p><a href="http://11011.net/software/vspaste"></a></p>
<pre class="code"><span style="color: green;">--daily sales
</span><span style="color: blue;">select </span>DD<span style="color: gray;">.</span>Datekey<span style="color: gray;">, </span>DS<span style="color: gray;">.</span>ZipCode<span style="color: gray;">, </span>DPC<span style="color: gray;">.</span>ProductCategoryName<span style="color: gray;">,
    </span><span style="color: magenta;">SUM</span><span style="color: gray;">(</span>FS<span style="color: gray;">.</span>SalesAmount<span style="color: gray;">) </span>SalesAmount_SUM<span style="color: gray;">,
    </span><span style="color: magenta;">SUM</span><span style="color: gray;">(</span>FS<span style="color: gray;">.</span>SalesQuantity<span style="color: gray;">) </span>SalesQuantity_SUM
<span style="color: blue;">from </span>dbo<span style="color: gray;">.</span>FactSales FS
    <span style="color: gray;">inner join </span>dbo<span style="color: gray;">.</span>DimStore DS <span style="color: blue;">on </span>DS<span style="color: gray;">.</span>StoreKey <span style="color: gray;">= </span>FS<span style="color: gray;">.</span>StoreKey
    <span style="color: gray;">inner join </span>dbo<span style="color: gray;">.</span>DimProduct DP <span style="color: blue;">on </span>DP<span style="color: gray;">.</span>ProductKey <span style="color: gray;">= </span>FS<span style="color: gray;">.</span>ProductKey
    <span style="color: gray;">inner join </span>dbo<span style="color: gray;">.</span>DimProductSubcategory DPS
        <span style="color: blue;">on </span>DPS<span style="color: gray;">.</span>ProductSubcategoryKey <span style="color: gray;">= </span>DP<span style="color: gray;">.</span>ProductSubcategoryKey
    <span style="color: gray;">inner join </span>dbo<span style="color: gray;">.</span>DimProductCategory DPC
        <span style="color: blue;">on </span>DPC<span style="color: gray;">.</span>ProductCategoryKey <span style="color: gray;">= </span>DPS<span style="color: gray;">.</span>ProductSubcategoryKey
    <span style="color: gray;">inner join </span>dbo<span style="color: gray;">.</span>DimDate DD <span style="color: blue;">on </span>DD<span style="color: gray;">.</span>Datekey <span style="color: gray;">= </span>FS<span style="color: gray;">.</span>DateKey
<span style="color: blue;">group by </span>DD<span style="color: gray;">.</span>Datekey<span style="color: gray;">, </span>DS<span style="color: gray;">.</span>ZipCode<span style="color: gray;">, </span>DPC<span style="color: gray;">.</span>ProductCategoryName
<span style="color: blue;">order by </span>DD<span style="color: gray;">.</span>Datekey <span style="color: blue;">asc</span><span style="color: gray;">, </span>DS<span style="color: gray;">.</span>ZipCode <span style="color: blue;">asc</span><span style="color: gray;">, </span>DPC<span style="color: gray;">.</span>ProductCategoryName <span style="color: blue;">asc</span><span style="color: gray;">;
</span></pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>Part of the result of that query looks like this:</p>
<p><img style="display: inline; border-width: 0px;" title="Result of the daily sales query" src="http://blog.hoegaerden.be/wp-content/uploads/image331.png" border="0" alt="Result of the daily sales query" width="610" height="218" /></p>
<p>Nothing special to mention so far so let’s continue to the next step.</p>
<h3>Step 2: The Monthly Numbers</h3>
<p>In this step, we’ll use the query from step 1 as base for the full query.  I’ll first show you the query and then provide you with some explanation of what’s going on.</p>
<pre class="code"><span style="color: green;">--LastMonth
</span><span style="color: blue;">declare </span>@numberOfMonths <span style="color: blue;">tinyint </span><span style="color: gray;">= </span>1<span style="color: gray;">;
</span><span style="color: blue;">with </span>DailySalesData <span style="color: blue;">as
</span><span style="color: gray;">(
    </span><span style="color: blue;">select </span>DD<span style="color: gray;">.</span>Datekey<span style="color: gray;">, </span>DS<span style="color: gray;">.</span>ZipCode<span style="color: gray;">, </span>DPC<span style="color: gray;">.</span>ProductCategoryName<span style="color: gray;">,
        </span><span style="color: magenta;">SUM</span><span style="color: gray;">(</span>FS<span style="color: gray;">.</span>SalesAmount<span style="color: gray;">) </span>SalesAmount_SUM<span style="color: gray;">,
        </span><span style="color: magenta;">SUM</span><span style="color: gray;">(</span>FS<span style="color: gray;">.</span>SalesQuantity<span style="color: gray;">) </span>SalesQuantity_SUM
    <span style="color: blue;">from </span>dbo<span style="color: gray;">.</span>FactSales FS
        <span style="color: gray;">inner join </span>dbo<span style="color: gray;">.</span>DimStore DS <span style="color: blue;">on </span>DS<span style="color: gray;">.</span>StoreKey <span style="color: gray;">= </span>FS<span style="color: gray;">.</span>StoreKey
        <span style="color: gray;">inner join </span>dbo<span style="color: gray;">.</span>DimProduct DP <span style="color: blue;">on </span>DP<span style="color: gray;">.</span>ProductKey <span style="color: gray;">= </span>FS<span style="color: gray;">.</span>ProductKey
        <span style="color: gray;">inner join </span>dbo<span style="color: gray;">.</span>DimProductSubcategory DPS
            <span style="color: blue;">on </span>DPS<span style="color: gray;">.</span>ProductSubcategoryKey <span style="color: gray;">= </span>DP<span style="color: gray;">.</span>ProductSubcategoryKey
        <span style="color: gray;">inner join </span>dbo<span style="color: gray;">.</span>DimProductCategory DPC
            <span style="color: blue;">on </span>DPC<span style="color: gray;">.</span>ProductCategoryKey <span style="color: gray;">= </span>DPS<span style="color: gray;">.</span>ProductSubcategoryKey
        <span style="color: gray;">inner join </span>dbo<span style="color: gray;">.</span>DimDate DD <span style="color: blue;">on </span>DD<span style="color: gray;">.</span>Datekey <span style="color: gray;">= </span>FS<span style="color: gray;">.</span>DateKey
    <span style="color: blue;">group by </span>DD<span style="color: gray;">.</span>Datekey<span style="color: gray;">, </span>DS<span style="color: gray;">.</span>ZipCode<span style="color: gray;">, </span>DPC<span style="color: gray;">.</span>ProductCategoryName
<span style="color: gray;">),
</span>UniqueRecordsPerDay <span style="color: blue;">as
</span><span style="color: gray;">(
    </span><span style="color: blue;">select </span>Datekey<span style="color: gray;">, </span>ZipCode<span style="color: gray;">, </span>ProductCategoryName
    <span style="color: blue;">from </span>DailySalesData
    <span style="color: blue;">group by </span>Datekey<span style="color: gray;">, </span>ZipCode<span style="color: gray;">, </span>ProductCategoryName
<span style="color: gray;">)
</span><span style="color: blue;">select </span>UR<span style="color: gray;">.</span>Datekey<span style="color: gray;">, </span>DSD<span style="color: gray;">.</span>ZipCode<span style="color: gray;">, </span>DSD<span style="color: gray;">.</span>ProductCategoryName<span style="color: gray;">,
    </span><span style="color: magenta;">SUM</span><span style="color: gray;">(</span>DSD<span style="color: gray;">.</span>SalesAmount_SUM<span style="color: gray;">) </span>SalesAmount_SUM<span style="color: gray;">,
    </span><span style="color: magenta;">SUM</span><span style="color: gray;">(</span>DSD<span style="color: gray;">.</span>SalesQuantity_SUM<span style="color: gray;">) </span>SalesQuantity_SUM
<span style="color: blue;">from </span>DailySalesData DSD
    <span style="color: gray;">inner join </span>UniqueRecordsPerDay UR
            <span style="color: blue;">on </span>UR<span style="color: gray;">.</span>ProductCategoryName <span style="color: gray;">= </span>DSD<span style="color: gray;">.</span>ProductCategoryName
        <span style="color: gray;">and </span>UR<span style="color: gray;">.</span>ZipCode <span style="color: gray;">= </span>DSD<span style="color: gray;">.</span>ZipCode
        <span style="color: gray;">and </span>DSD<span style="color: gray;">.</span>Datekey
            <span style="color: gray;">between </span><span style="color: magenta;">DATEADD</span><span style="color: gray;">(</span><span style="color: magenta;">month</span><span style="color: gray;">, -</span>@numberOfMonths<span style="color: gray;">, </span>UR<span style="color: gray;">.</span>Datekey <span style="color: gray;">+ </span>1<span style="color: gray;">)
            and </span>UR<span style="color: gray;">.</span>Datekey
<span style="color: blue;">group by </span>UR<span style="color: gray;">.</span>Datekey<span style="color: gray;">, </span>DSD<span style="color: gray;">.</span>ZipCode<span style="color: gray;">, </span>DSD<span style="color: gray;">.</span>ProductCategoryName
<span style="color: blue;">order by </span>UR<span style="color: gray;">.</span>Datekey <span style="color: blue;">asc</span><span style="color: gray;">, </span>DSD<span style="color: gray;">.</span>ZipCode <span style="color: blue;">asc</span><span style="color: gray;">, </span>DSD<span style="color: gray;">.</span>ProductCategoryName <span style="color: blue;">asc</span><span style="color: gray;">;</span></pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>The query uses a variable called @numberOfMonths.  This will allow us to use the same query for the totals of last month, as well as for the Last3M and the Last6M numbers.  All that’s needed is changing the variable to 3 or 6.</p>
<p>But how does the query get to the results?  To start, it uses two CTEs (Common Table Expressions).  The first one is called DailySalesData.  And the query for that CTE should look familiar to you by now: it’s the one from step 1, without the ORDER BY clause.</p>
<p>The second CTE is called UniqueRecordsPerDay and gives us one record for each unique date, zip code and product category as found in the Contoso data.  The DateKey, ZipCode and ProductCategoryName fields are our key grouping fields.  And this CTE is actually the key to calculating the monthly aggregated data, as I’ll explain next.</p>
<p>What the main query does is the following.  It selects the data from the DailySalesData CTE and joins that with the unique records per day recordset.  All grouping key fields need to be included in the join.  However, as you can see, to add the DateKey into the join I’m not just using the equals operator but the BETWEEN keyword instead.  I’ve also used the DATEADD function to subtract the number of months as specified through the @numberOfMonths variable.  That statement is saying: “give me all records starting from DateKey, going back @numberOfMonths”.  The query again groups by the key fields to be able to sum the records up.</p>
<p>This construction ensures that the SalesAmount_SUM and SalesQuantity_SUM fields represent the sum for the record’s zip code and product category and for the period as indicated by the @numberOfMonths variable.</p>
<h3>Step 3: Merging It All Together Into One Table</h3>
<p>Now that we know how to retrieve the data, we still need to get it into a table.  One option would be to use the INSERT statement on the daily records, followed by UPDATE statements to populate the monthly (1, 3, 6) aggregated columns.  However, I’m a BI guy so let’s use an SSIS package to get to the result (plus it allows me to illustrate the Merge Join data flow transformation <img src='http://blog.hoegaerden.be/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> ).</p>
<p>So open up the BIDS and create a new package.  Drop a Data Flow Task into the Control Flow and add a Connection Manager connecting to your Contoso DWH.  Then switch to the Data Flow page.</p>
<p>Nothing special so far I believe.  Next we need to set up four Data Flow Sources: one for the daily figures, one for the monthly, one for the 3M and one for the 6M data.</p>
<h4>Setting Up The Data Sources</h4>
<p>Throw in an OLE DB Source component, configure it to use your connection manager and copy/paste the first query above into the command textbox.  Again nothing special, right?</p>
<p>However, the Merge Join component expects its incoming data to be sorted.  That’s why I’ve included the ORDER BY clause in the queries above.  But that’s not all.  Connecting our data source to a Merge Join transformation without any additional change will result in an error such as the following:</p>
<blockquote><p>Validation error. Data Flow Task Merge Join [457]: The input is not sorted. The &#8220;input &#8220;Merge Join Left Input&#8221; (458)&#8221; must be sorted.</p></blockquote>
<p>To avoid this error, we need to explicitly inform our data flow that the data is actually ordered, and we need to give it all the details: on what fields has the data been ordered and in what order!  And that needs to be done through the Advanced Editor.</p>
<p>So, right-click the OLE DB Source and select <em>Show Advanced Editor</em>.</p>
<p><img style="display: inline; border-width: 0px;" title="Right-click OLE DB Source to open up the Advanced Editor" src="http://blog.hoegaerden.be/wp-content/uploads/image332.png" border="0" alt="Right-click OLE DB Source to open up the Advanced Editor" width="374" height="415" /></p>
<p>In the <strong>Advanced Editor</strong>, navigate to the last tab called <em>Input and Output Properties</em> and select the “OLE DB Source Output” node in the tree structure on the left.  Doing that will show the properties for the selected output and one of those properties is called <strong>IsSorted</strong>.  By default it is set to False.  Set it to True.</p>
<p><span style="text-decoration: underline;"><strong>Tip:</strong></span> double-clicking the label of the property will swap its value to the other value.  This can be useful in cases when you need to change several options but even here is saves a couple of clicks.  It’s all about optimization. <img src='http://blog.hoegaerden.be/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
<p><a href="http://blog.hoegaerden.be/wp-content/uploads/image333.png"><img style="display: inline; border-width: 0px;" title="Advanced Editor on OLE DB Source: the IsSorted property" src="http://blog.hoegaerden.be/wp-content/uploads/image_thumb74.png" border="0" alt="Advanced Editor on OLE DB Source: the IsSorted property" width="700" height="632" /></a></p>
<p>At this moment the component knows that the incoming data is sorted, but it still doesn’t know on what fields.  To specify that, open up the OLE DB Source Output node, followed by the Output Columns node.  You’ll now see the list of fields.  As specified in the query, the data is ordered firstly on DateKey, secondly on ZipCode and thirdly on ProductCategoryName.</p>
<p>Select DateKey to see its properties.</p>
<p><a href="http://blog.hoegaerden.be/wp-content/uploads/image334.png"><img style="display: inline; border-width: 0px;" title="Advanced Editor of OLE DB Source showing the SortKeyPosition property" src="http://blog.hoegaerden.be/wp-content/uploads/image_thumb75.png" border="0" alt="Advanced Editor of OLE DB Source showing the SortKeyPosition property" width="700" height="678" /></a></p>
<p>The property in which we’re interested here is called <strong>SortKeyPosition</strong>.  By default it is set to zero.  When the incoming data is sorted,  this property should reflect in what order the data is sorted, starting with one for the first field.  So in our case here the value should be set to 1.</p>
<p>Set the SortKeyPosition property for ZipCode to 2 and for ProductCategoryName to 3.</p>
<p>That’s one of the four OLE DB sources set up.  The other three will be easier as we can start from the first one.  So, copy and paste the source component, open it up by double-clicking it and replace the query with our second query from earlier, the one returning the monthly figures.  Ow, and give it a decent name but I’m sure you knew that.</p>
<p>Create the third source component in the same way, but change the value for the @numberOfMonths variable to 3.  And again the same process for source number four, changing the variable’s value to 6.</p>
<p>Here’s what we have so far:</p>
<p><img style="display: inline; border-width: 0px;" title="Four OLE DB sources set up - waiting to be merged" src="http://blog.hoegaerden.be/wp-content/uploads/image335.png" border="0" alt="Four OLE DB sources set up - waiting to be merged" width="635" height="61" /></p>
<h4>Merging The Sources Into One Flow</h4>
<p>Next up is merging the incoming flows.  Drag a<strong> Merge Join </strong>data flow transformation under the Daily Sales source and connect the source to the Merge Join.  That will open the following <strong>Input Output Selection </strong>screen.</p>
<p><img style="display: inline; border-width: 0px;" title="Input Output Selection window" src="http://blog.hoegaerden.be/wp-content/uploads/image336.png" border="0" alt="Input Output Selection window" width="560" height="303" /></p>
<p>A Merge Join expects two inputs: one is called the <em>Left Input</em> and the other is called the <em>Right Input</em>.  Select <em>Merge Join Left Input </em>as value for the <strong>Input </strong>dropdown.</p>
<p>Close the popup window and connect the second source (with the monthly data) as well to the Merge Join.  There’s only one input remaining so this one is automatically the right input – no popup window is shown.</p>
<p>Next we need to configure the Merge Join so that it merges the data as expected.  Open the <strong>Merge Join Transformation Editor</strong> by double-clicking the component.</p>
<p><a href="http://blog.hoegaerden.be/wp-content/uploads/image337.png"><img style="display: inline; border-width: 0px;" title="Merge Join Transformation Editor" src="http://blog.hoegaerden.be/wp-content/uploads/image_thumb76.png" border="0" alt="Merge Join Transformation Editor" width="700" height="659" /></a></p>
<p>By default the <strong>Join type</strong> dropdown is set to <em>Inner join</em>.  In our situation that’s good enough.  In the case that only one record exists for a certain zip code and product category on a given day, the monthly data for this record will be the sum of just that one record but in any case: there’s always at least one record for each incoming flow to be combined with each other.</p>
<p>As you can see, because both incoming flows are ordered in the same way, it automatically knows on which fields to put the join.</p>
<p>By default, no output fields are created as the white bottom half of the screenshot indicates.</p>
<p>Now I’ll show you a screenshot of the expected setup:</p>
<p><img style="display: inline; border-width: 0px;" title="Merge Join Transformation Editor set up as expected" src="http://blog.hoegaerden.be/wp-content/uploads/image338.png" border="0" alt="Merge Join Transformation Editor set up as expected" width="700" height="659" /></p>
<p>There are several ways to specify the output fields.  The first method is by using the dropdown in the <strong>Input</strong> column.  Selecting a value there will populate a dropdown in the column called <strong>Input Column</strong> (djeez, that was one column too much).  Here’s what that method looks like:</p>
<p><img style="display: inline; border-width: 0px;" title="Specifying the output fields by using the dropdowns" src="http://blog.hoegaerden.be/wp-content/uploads/image339.png" border="0" alt="Specifying the output fields by using the dropdowns" width="340" height="106" /></p>
<p>Selecting a value in the second column will then give you a default value for the <strong>Output Alias</strong>.  This default can be freely modified.  As you may have guessed, this is not my preferred method – way too many comboboxes.</p>
<p>Another method of specifying the output fields is by using the checkboxes in front of the fields in the top part of the window.  I believe the larger screenshot above says it all.  Just check the fields that you need and then change their default Output Alias to whatever suits you.   In my example here I only needed to modify the alias for the last two records.</p>
<p>With our first Merge Join set up, only two are remaining.  So drag in a second Merge Join from the Toolbox, connect the output of the first join as Left Input on the second join and add the output of the third OLE DB source as Right Input.</p>
<p>Interesting to note here is that the output of the Merge Join is sorted in the same manner as its inputs.  One way of verifying this is by right-clicking the connector between the two joins and choosing <em>Edit</em>.</p>
<p><img style="display: inline; border-width: 0px;" title="Right-click data flow connector and select Edit to open up Data Flow Path Editor" src="http://blog.hoegaerden.be/wp-content/uploads/image340.png" border="0" alt="Right-click data flow connector and select Edit to open up Data Flow Path Editor" width="415" height="360" /></p>
<p>That opens up the <strong>Data Flow Path Editor</strong>.</p>
<p><strong><span style="text-decoration: underline;">Tip:</span></strong> double-clicking the connector will also open the editor!</p>
<p><a href="http://blog.hoegaerden.be/wp-content/uploads/image341.png"><img style="display: inline; border-width: 0px;" title="Examine the Metadata of the Data Flow Path to verify the sort order" src="http://blog.hoegaerden.be/wp-content/uploads/image_thumb77.png" border="0" alt="Examine the Metadata of the Data Flow Path to verify the sort order" width="700" height="472" /></a></p>
<p>As you can see in the above screenshot, the metadata page shows a list of the available fields with some properties, such as the Sort Key Position.  Now if that doesn’t look familiar?! <img src='http://blog.hoegaerden.be/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
<p>So far, the second Merge Join has been added and connected but it hasn’t been configured yet.  The process is very similar to the way we’ve set up the first join.  Just select all fields from the left input by checking all the checkboxes and select the two SUM fields from the right input.  Don’t forget to give those SUM fields a clear name.</p>
<p>Two joins done, one remaining.  Just drag one in and connect it with the second join plus the last remaining OLE DB source.  I won’t go into further details here, it’s exactly the same as I just explained for the second join.</p>
<p>Here’s what the Data Flow should look like:</p>
<p><img style="display: inline; border-width: 0px;" title="The Data Flow with all the Merge Joins connected" src="http://blog.hoegaerden.be/wp-content/uploads/image342.png" border="0" alt="The Data Flow with all the Merge Joins connected" width="628" height="295" /></p>
<p>And here’s what the third Merge Join should look like:<a href="http://blog.hoegaerden.be/wp-content/uploads/image343.png"><img style="display: inline; border-width: 0px;" title="The third Merge Join as set up for the example" src="http://blog.hoegaerden.be/wp-content/uploads/image_thumb78.png" border="0" alt="The third Merge Join as set up for the example" width="700" height="703" /></a></p>
<h4>An Error That You May Encounter</h4>
<p>When using sorted data flows and the Merge Join component, you may encounter the following error message:</p>
<p><img style="display: inline; border-width: 0px;" title="An error that you may encounter while using the Merge Join component" src="http://blog.hoegaerden.be/wp-content/uploads/image344.png" border="0" alt="An error that you may encounter while using the Merge Join component" width="700" height="219" /></p>
<p>And now in words for the search engines:</p>
<blockquote><p>The component has detected potential metadata corruption during validation.</p>
<p>Error at Data Flow Task [SSIS.Pipeline]: The IsSorted property of output “Merge Join Output” (91) is set to TRUE, but the absolute values of the non-zero output column SortKeyPositions do not form a monotonically increasing sequence, starting at one.</p></blockquote>
<p>Yeah right, you had to read that twice, didn’t you?  And the best is yet to come:</p>
<blockquote><p>Due to limitations of the Advanced Editor dialog box, this component cannot be edited using this dialog box.</p></blockquote>
<p>So there’s a problem with your Merge Join but you cannot use the Advanced Editor to fix it, hmm, and you call that the ADVANCED editor?  Is there anything more advanced perhaps?  Well, actually, there is.  It’s called the Properties pane.  With the Merge Join selected, one of the properties there is called <strong>NumKeyColumns</strong>.  That property reflects on how many columns the incoming data is sorted.  And currently it contains the wrong value.  Changing its value to the correct number of columns will remove the error.</p>
<p><img style="display: inline; border-width: 0px;" title="Properties pane displaying the Merge Join's properties, including NumKeyColumns" src="http://blog.hoegaerden.be/wp-content/uploads/image345.png" border="0" alt="Properties pane displaying the Merge Join's properties, including NumKeyColumns" width="311" height="368" /></p>
<p>In case you’re wondering when you might encounter this particular problem, here’s how you can simulate it.  (Don’t forget to make a copy of the package before messing around with it.)</p>
<p>With the package as it currently is, remove the ZipCode field from the first two sources by unchecking it in the Columns page of the OLE DB Source Editor.</p>
<p>The sources are now complaining so open up their Advanced Editor and correct the SortKeyPosition of the ProductCategoryName field: it should become 2 instead of 3 because ZipCode was 2 and has been removed.</p>
<p>Now try to open the first Merge Join.  The first time it will complain about invalid references so delete those.  With the references deleted, if you now try to open the Merge Join editor, you’ll see the error we’re discussing here.  To fix it, change the NumKeyColumns property of the Merge Join to 2 instead of 3.</p>
<h4>Adding The Destination Table</h4>
<p>Now there’s only one step remaining: adding a destination for our merged data.  So, throw in an <strong>OLE DB Destination </strong>and connect it with the output of the last Merge Join:</p>
<p><img style="display: inline; border-width: 0px;" title="An OLE DB Destination connected to the join that merges it all together" src="http://blog.hoegaerden.be/wp-content/uploads/image346.png" border="0" alt="An OLE DB Destination connected to the join that merges it all together" width="153" height="171" /></p>
<p>I’ll just use a quick and dirty way of creating a new table in the database.  Open up the OLE DB Destination Editor by double-clicking it and select a Connection Manager in the dropdown.  Now click the <strong>New</strong> button next to the <em>Name of the table or the view</em> dropdown.</p>
<p>That opens up the Create Table window, with a CREATE TABLE query pre-generated for you for free.  Isn’t that nice?  Change the name of the table to something nice (at least remove those spaces, yuk!!) and click OK.</p>
<p><img style="display: inline; border-width: 0px;" title="The Create Table window" src="http://blog.hoegaerden.be/wp-content/uploads/image347.png" border="0" alt="The Create Table window" width="512" height="446" /></p>
<p>The new table is created at the moment that the OK button gets clicked.</p>
<p>Right, so are we there?  Well, almost.  As you can see now in the next screenshot, the BIDS does not want us to click the OK button just yet.</p>
<p><a href="http://blog.hoegaerden.be/wp-content/uploads/image348.png"><img style="display: inline; border-width: 0px;" title="The OLE DB Destination Editor with the Mappings still missing" src="http://blog.hoegaerden.be/wp-content/uploads/image_thumb79.png" border="0" alt="The OLE DB Destination Editor with the Mappings still missing" width="700" height="654" /></a></p>
<p>To resolve that warning, just open the Mappings page.  As the names of the input columns are matching exactly with the names of the fields in the destination table, everything will be automagically configured at this moment.  So now you can close the window with the OK button.</p>
<p>And that’s it!  Everything is set up to populate the new table with the aggregated figures, as requested by management.  To give it a run, right-click your package in the Solution Explorer and guess what… select Execute Package!  If everything has been configured as expected, you should get some green boxes soon.  And some data in the table, like this:</p>
<p><a href="http://blog.hoegaerden.be/wp-content/uploads/image349.png"><img style="display: inline; border-width: 0px;" title="The final result: sales figures aggregated over different periods in time" src="http://blog.hoegaerden.be/wp-content/uploads/image_thumb80.png" border="0" alt="The final result: sales figures aggregated over different periods in time" width="700" height="82" /></a></p>
<h2>Conclusion</h2>
<p>In this article I’ve demonstrated a way to aggregate data over different periods in time, using T-SQL and Integration Services.  Obviously this method does not replace the flexibility that one gets when analyzing data stored in an OLAP cube, but it can be a practical method when you quickly need to provide aggregated data for management.</p>
<p>Have fun!</p>
<p>Valentino.</p>
<p><strong>References</strong></p>
<p><a title="Merge Join Transformation" href="http://msdn.microsoft.com/en-us/library/ms141775.aspx" target="_blank">Merge Join Data Flow Transformation</a></p>
<p><a title="Using Common Table Expressions" href="http://msdn.microsoft.com/en-us/library/ms190766.aspx" target="_blank">Common Table Expressions (CTEs)</a></p>
<p><a title="DATEADD (Transact-SQL)" href="http://msdn.microsoft.com/en-us/library/ms186819.aspx" target="_blank">DATEADD() function</a></p>
<p><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fblog.hoegaerden.be%2F2010%2F08%2F26%2Fcalculating-lastxmonths-aggregations-using-t-sql-and-ssis%2F&amp;title=Calculating%20LastXMonths%20Aggregations%20Using%20T-SQL%20and%20SSIS" id="wpa2a_10"><img src="http://blog.hoegaerden.be/wp-content/plugins/add-to-any/share_save_171_16.png" width="171" height="16" alt="Share"/></a></p>]]></content:encoded>
			<wfw:commentRss>http://blog.hoegaerden.be/2010/08/26/calculating-lastxmonths-aggregations-using-t-sql-and-ssis/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Aggregating Data With The OVER Clause</title>
		<link>http://blog.hoegaerden.be/2010/06/01/aggregating-data-with-the-over-clause/</link>
		<comments>http://blog.hoegaerden.be/2010/06/01/aggregating-data-with-the-over-clause/#comments</comments>
		<pubDate>Tue, 01 Jun 2010 21:12:01 +0000</pubDate>
		<dc:creator>Valentino Vranken</dc:creator>
				<category><![CDATA[T-SQL]]></category>
		<category><![CDATA[data]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[Tutorial]]></category>

		<guid isPermaLink="false">http://blog.hoegaerden.be/2010/06/01/aggregating-data-with-the-over-clause/</guid>
		<description><![CDATA[In this article I will show you a couple of different T-SQL queries to fetch aggregated data.  The main purpose is to illustrate how the OVER clause can be used to aggregate data. For the examples I will use data from the AdventureWorks2008R2 database, available at CodePlex. The Data The AdventureWorks 2008 R2 database contains [...]]]></description>
			<content:encoded><![CDATA[<p>In this article I will show you a couple of different T-SQL queries to fetch aggregated data.  The main purpose is to illustrate how the OVER clause can be used to aggregate data.</p>
<p>For the examples I will use data from the AdventureWorks2008R2 database, <a title="AdventureWorks 2008R2 RTM" href="http://msftdbprodsamples.codeplex.com/releases/view/45907" target="_blank">available at CodePlex</a>.</p>
<h2>The Data</h2>
<p>The AdventureWorks 2008 R2 database contains a view called <em>Sales.vSalesPerson</em>.  This is the data with which I’ll be working in the examples below.  Here’s what it looks like:</p>
<p><a href="http://blog.hoegaerden.be/wp-content/uploads/image286.png"><img style="display: inline; border-width: 0px;" title="My Working Data" src="http://blog.hoegaerden.be/wp-content/uploads/image_thumb68.png" border="0" alt="My Working Data" width="700" height="184" /></a></p>
<p>I’ve hidden some fields so that all the relevant ones are in view.</p>
<h2>The Scenario<a href="http://11011.net/software/vspaste"></a></h2>
<p>Your manager has asked you to create one query, to be executed on the <em>Sales.vSalesPerson</em> table, that returns a list of:</p>
<ul>
<li>all employees (FirstName, LastName, JobTitle, CountryRegionName, StateProvinceName, City),</li>
<li>their sales of last year (SalesLastYear),</li>
<li>the sum of the sales of last year for their country,</li>
<li>the average of the sales of last year compared to all employees with the same type of phone (PhoneNumberType)</li>
<li>the overall average and sum of the sales of last year.</li>
</ul>
<h3>Using Derived Tables</h3>
<p>No problem you say, coming right up.  So you start building your query, retrieving all fields as requested.</p>
<p>After quite some typing, here’s what your query looks like:</p>
<p><a href="http://11011.net/software/vspaste"></a><a href="http://11011.net/software/vspaste"></a><a href="http://11011.net/software/vspaste"></a><a href="http://11011.net/software/vspaste"></a><a href="http://11011.net/software/vspaste"></a></p>
<pre class="code"><span style="color: blue;">select </span>S<span style="color: gray;">.</span>FirstName<span style="color: gray;">, </span>S<span style="color: gray;">.</span>LastName<span style="color: gray;">, </span>S<span style="color: gray;">.</span>JobTitle<span style="color: gray;">, </span>S<span style="color: gray;">.</span>PhoneNumberType<span style="color: gray;">, </span>S<span style="color: gray;">.</span>CountryRegionName<span style="color: gray;">,
    </span>S<span style="color: gray;">.</span>StateProvinceName<span style="color: gray;">, </span>S<span style="color: gray;">.</span>City<span style="color: gray;">, </span>S<span style="color: gray;">.</span>SalesLastYear<span style="color: gray;">,
    </span>GeographicSales<span style="color: gray;">.</span>SalesLastYearGeographic_SUM<span style="color: gray;">,
    </span>SalesByPhoneType<span style="color: gray;">.</span>SalesLastYearByPhoneNumberType_AVG<span style="color: gray;">,
    </span>SalesSUM<span style="color: gray;">.</span>SalesLastYear_AVG<span style="color: gray;">, </span>SalesSUM<span style="color: gray;">.</span>SalesLastYear_SUM
<span style="color: blue;">from </span>Sales<span style="color: gray;">.</span>vSalesPerson S
<span style="color: green;">--Derived Table 1: the overall aggregates
</span><span style="color: gray;">cross join (
    </span><span style="color: blue;">select </span><span style="color: magenta;">SUM</span><span style="color: gray;">(</span>SalesLastYear<span style="color: gray;">) </span>SalesLastYear_SUM<span style="color: gray;">, </span><span style="color: magenta;">AVG</span><span style="color: gray;">(</span>SalesLastYear<span style="color: gray;">) </span>SalesLastYear_AVG
    <span style="color: blue;">from </span>Sales<span style="color: gray;">.</span>vSalesPerson
<span style="color: gray;">) </span>SalesSUM
<span style="color: green;">--Derived Table 2: the aggregate on Country level
</span><span style="color: gray;">inner join (
    </span><span style="color: blue;">select </span>CountryRegionName<span style="color: gray;">, </span><span style="color: magenta;">SUM</span><span style="color: gray;">(</span>SalesLastYear<span style="color: gray;">) </span>SalesLastYearGeographic_SUM
    <span style="color: blue;">from </span>Sales<span style="color: gray;">.</span>vSalesPerson
    <span style="color: blue;">group by </span>CountryRegionName
<span style="color: gray;">) </span>GeographicSales <span style="color: blue;">on </span>GeographicSales<span style="color: gray;">.</span>CountryRegionName <span style="color: gray;">= </span>S<span style="color: gray;">.</span>CountryRegionName
<span style="color: green;">--Derived Table 3: the aggregate on phone type
</span><span style="color: gray;">inner join (
    </span><span style="color: blue;">select </span>PhoneNumberType<span style="color: gray;">, </span><span style="color: magenta;">AVG</span><span style="color: gray;">(</span>SalesLastYear<span style="color: gray;">) </span>SalesLastYearByPhoneNumberType_AVG
    <span style="color: blue;">from </span>Sales<span style="color: gray;">.</span>vSalesPerson
    <span style="color: blue;">group by </span>PhoneNumberType
<span style="color: gray;">) </span>SalesByPhoneType <span style="color: blue;">on </span>SalesByPhoneType<span style="color: gray;">.</span>PhoneNumberType<span style="color: gray;">= </span>S<span style="color: gray;">.</span>PhoneNumberType<span style="color: gray;">;
</span></pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>The main query is retrieving all fields as requested.  Further down there are three derived table queries, each one retrieving aggregates on a different level.</p>
<p>The first derived table is retrieving the overall aggregates.  These are cross-joined with every record in our main query so for each record the totals will be the same, which is what we want.</p>
<p>The second derived table retrieves the aggregates on Country level, including the CountryRegionName.  This is done using the conventional GROUP BY method.  The CountryRegionName is the key on which the derived table is joined to the main table.</p>
<p>The third derived table uses a similar system, this time for the aggregate on phone type.</p>
<p>And here’s the query’s output:</p>
<p><a href="http://blog.hoegaerden.be/wp-content/uploads/image287.png"><img style="display: inline; border-width: 0px;" title="Output of the query using subqueries" src="http://blog.hoegaerden.be/wp-content/uploads/image_thumb69.png" border="0" alt="Output of the query using subqueries" width="700" height="174" /></a></p>
<p>Happy with this result, you go up to the cafeteria to finally have lunch with your colleagues (who left 15 minutes earlier but you wanted to get your query finished first).</p>
<h3>Using The OVER Clause</h3>
<p>During lunch you explain to your peers what kind of funny request you got from management and told them how you solved it.</p>
<p>Then one of them speaks up and says: “Want to know how you can avoid all that typing?  Use the OVER clause!  I’ll show you when we are back at our desks.”</p>
<p>After lunch, here’s what your colleague helps to produce:</p>
<p><a href="http://11011.net/software/vspaste"></a><a href="http://11011.net/software/vspaste"></a><a href="http://11011.net/software/vspaste"></a><a href="http://11011.net/software/vspaste"></a></p>
<pre class="code"><span style="color: blue;">select </span>S<span style="color: gray;">.</span>FirstName<span style="color: gray;">, </span>S<span style="color: gray;">.</span>LastName<span style="color: gray;">, </span>S<span style="color: gray;">.</span>JobTitle<span style="color: gray;">, </span>S<span style="color: gray;">.</span>PhoneNumberType<span style="color: gray;">, </span>S<span style="color: gray;">.</span>CountryRegionName<span style="color: gray;">,
    </span>S<span style="color: gray;">.</span>StateProvinceName<span style="color: gray;">, </span>S<span style="color: gray;">.</span>City<span style="color: gray;">, </span>S<span style="color: gray;">.</span>SalesLastYear<span style="color: gray;">,
    </span><span style="color: magenta;">SUM</span><span style="color: gray;">(</span>SalesLastYear<span style="color: gray;">) </span><span style="color: blue;">OVER </span><span style="color: gray;">(</span><span style="color: blue;">PARTITION BY </span>CountryRegionName<span style="color: gray;">)
        </span>SalesLastYearGeographic_SUM<span style="color: gray;">,
    </span><span style="color: magenta;">AVG</span><span style="color: gray;">(</span>SalesLastYear<span style="color: gray;">) </span><span style="color: blue;">OVER </span><span style="color: gray;">(</span><span style="color: blue;">PARTITION BY </span>PhoneNumberType<span style="color: gray;">)
        </span>SalesLastYearByPhoneNumberType_AVG<span style="color: gray;">,
    </span>SalesSUM<span style="color: gray;">.</span>SalesLastYear_AVG<span style="color: gray;">, </span>SalesSUM<span style="color: gray;">.</span>SalesLastYear_SUM
<span style="color: blue;">from </span>Sales<span style="color: gray;">.</span>vSalesPerson S
<span style="color: green;">--Derived Table 1: the overall aggregates
</span><span style="color: gray;">cross join (
    </span><span style="color: blue;">select </span><span style="color: magenta;">SUM</span><span style="color: gray;">(</span>SalesLastYear<span style="color: gray;">) </span>SalesLastYear_SUM<span style="color: gray;">, </span><span style="color: magenta;">AVG</span><span style="color: gray;">(</span>SalesLastYear<span style="color: gray;">) </span>SalesLastYear_AVG
    <span style="color: blue;">from </span>Sales<span style="color: gray;">.</span>vSalesPerson
<span style="color: gray;">) </span>SalesSUM<span style="color: gray;">;
</span></pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>As you can see, derived tables 2 and 3 are gone.  They have been replaced with <a title="OVER Clause (Transact-SQL)" href="http://msdn.microsoft.com/en-us/library/ms189461.aspx" target="_blank">the OVER clause</a>, in combination with PARTITION BY.  What you say with the OVER clause is: “partition the dataset by the fields specified in the PARTITION BY and apply the aggregation on those partitions”.  Another word for this is <strong>aggregate window function</strong>.</p>
<p>As you like the approach, you ask your co-worker how you can get rid of that cross join.  He doesn’t really know but then another colleague who overheard your conversation says: “On this blog the other day I read that you can use the OVER clause and partition by anything you want.  As long as it’s a constant, it will work!”.</p>
<p>So you give that a try and you end up with the following final query:</p>
<p><a href="http://11011.net/software/vspaste"></a><a href="http://11011.net/software/vspaste"></a></p>
<pre class="code"><span style="color: blue;">select </span>FirstName<span style="color: gray;">, </span>LastName<span style="color: gray;">, </span>JobTitle<span style="color: gray;">, </span>PhoneNumberType<span style="color: gray;">, </span>CountryRegionName<span style="color: gray;">,
    </span>StateProvinceName<span style="color: gray;">, </span>City<span style="color: gray;">, </span>SalesLastYear<span style="color: gray;">,
    </span><span style="color: magenta;">SUM</span><span style="color: gray;">(</span>SalesLastYear<span style="color: gray;">) </span><span style="color: blue;">OVER </span><span style="color: gray;">(</span><span style="color: blue;">PARTITION BY </span>CountryRegionName<span style="color: gray;">)
        </span>SalesLastYearGeographic_SUM<span style="color: gray;">,
    </span><span style="color: magenta;">AVG</span><span style="color: gray;">(</span>SalesLastYear<span style="color: gray;">) </span><span style="color: blue;">OVER </span><span style="color: gray;">(</span><span style="color: blue;">PARTITION BY </span>PhoneNumberType<span style="color: gray;">)
        </span>SalesLastYearByPhoneNumberType_AVG<span style="color: gray;">,
    </span><span style="color: magenta;">AVG</span><span style="color: gray;">(</span>SalesLastYear<span style="color: gray;">) </span><span style="color: blue;">OVER </span><span style="color: gray;">(</span><span style="color: blue;">PARTITION BY </span><span style="color: red;">'duh'</span><span style="color: gray;">) </span>SalesLastYear_AVG<span style="color: gray;">,
    </span><span style="color: magenta;">SUM</span><span style="color: gray;">(</span>SalesLastYear<span style="color: gray;">) </span><span style="color: blue;">OVER </span><span style="color: gray;">(</span><span style="color: blue;">PARTITION BY </span>1<span style="color: gray;">) </span>SalesLastYear_SUM
<span style="color: blue;">from </span>Sales<span style="color: gray;">.</span>vSalesPerson<span style="color: gray;">;
</span></pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>As illustrated in the example, you can use any constant value to calculate overall aggregates over the whole dataset using the OVER clause.</p>
<p>You happily thank your colleagues and tell them that next time you’ll be able to join them for lunch on time.</p>
<p>About a week later you’re explaining to one of your friends how you’ve gotten to know the OVER clause. After hearing how you use it to aggregate over the whole dataset, he smiles and says: “I know how you can simplify it even more! Don’t partition at all!”.</p>
<p>Taking a closer look it turns out that the PARTITION BY is actually optional:</p>
<pre>Ranking Window Functions
&lt; OVER_CLAUSE &gt; :: =     OVER ( [ PARTITION BY value_expression, ... [ n ] ]            &lt;ORDER BY_Clause&gt; )Aggregate Window Functions
&lt; OVER_CLAUSE &gt; :: =     OVER ( [ PARTITION BY value_expression, ... [ n ] ] )</pre>
<p>See those square brackets? Means it’s optional.</p>
<p>So here is the real final query:</p>
<pre class="code"><span style="color: blue;">select </span>FirstName<span style="color: gray;">, </span>LastName<span style="color: gray;">, </span>JobTitle<span style="color: gray;">, </span>PhoneNumberType<span style="color: gray;">, </span>CountryRegionName<span style="color: gray;">,
    </span>StateProvinceName<span style="color: gray;">, </span>City<span style="color: gray;">, </span>SalesLastYear<span style="color: gray;">,
    </span><span style="color: magenta;">SUM</span><span style="color: gray;">(</span>SalesLastYear<span style="color: gray;">) </span><span style="color: blue;">OVER </span><span style="color: gray;">(</span><span style="color: blue;">PARTITION BY </span>CountryRegionName<span style="color: gray;">)
        </span>SalesLastYearGeographic_SUM<span style="color: gray;">,
    </span><span style="color: magenta;">AVG</span><span style="color: gray;">(</span>SalesLastYear<span style="color: gray;">) </span><span style="color: blue;">OVER </span><span style="color: gray;">(</span><span style="color: blue;">PARTITION BY </span>PhoneNumberType<span style="color: gray;">)
        </span>SalesLastYearByPhoneNumberType_AVG<span style="color: gray;">,
    </span><span style="color: magenta;">AVG</span><span style="color: gray;">(</span>SalesLastYear<span style="color: gray;">) </span><span style="color: blue;">OVER </span><span style="color: gray;">() </span>SalesLastYear_AVG<span style="color: gray;">,
    </span><span style="color: magenta;">SUM</span><span style="color: gray;">(</span>SalesLastYear<span style="color: gray;">) </span><span style="color: blue;">OVER </span><span style="color: gray;">() </span>SalesLastYear_SUM
<span style="color: blue;">from </span>Sales<span style="color: gray;">.</span>vSalesPerson<span style="color: gray;">;
</span></pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<h2>Conclusion</h2>
<p>When you compare the final query with the first one, tell me, which one would you prefer to maintain?  Do you prefer to have lunch with your peers or to arrive late and miss all the fun?</p>
<p>Have fun!</p>
<p>Valentino.</p>
<p><strong>References</strong></p>
<p><a title="The OVER Clause (Transact-SQL)" href="http://msdn.microsoft.com/en-us/library/ms189461.aspx" target="_blank">OVER Clause (Transact-SQL)</a></p>
<p><a title="Join Fundamentals" href="http://msdn.microsoft.com/en-us/library/ms191517.aspx" target="_blank">Join Fundamentals</a></p>
<p><a title="SELECT (Transact-SQL)" href="http://msdn.microsoft.com/en-us/library/ms189499.aspx" target="_blank">SELECT (Transact-SQL)</a></p>
<p><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fblog.hoegaerden.be%2F2010%2F06%2F01%2Faggregating-data-with-the-over-clause%2F&amp;title=Aggregating%20Data%20With%20The%20OVER%20Clause" id="wpa2a_12"><img src="http://blog.hoegaerden.be/wp-content/plugins/add-to-any/share_save_171_16.png" width="171" height="16" alt="Share"/></a></p>]]></content:encoded>
			<wfw:commentRss>http://blog.hoegaerden.be/2010/06/01/aggregating-data-with-the-over-clause/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Retrieving Data From Excel</title>
		<link>http://blog.hoegaerden.be/2010/03/29/retrieving-data-from-excel/</link>
		<comments>http://blog.hoegaerden.be/2010/03/29/retrieving-data-from-excel/#comments</comments>
		<pubDate>Mon, 29 Mar 2010 20:16:53 +0000</pubDate>
		<dc:creator>Valentino Vranken</dc:creator>
				<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[SQLServerPedia Syndication]]></category>
		<category><![CDATA[data]]></category>
		<category><![CDATA[Excel]]></category>
		<category><![CDATA[T-SQL]]></category>

		<guid isPermaLink="false">http://blog.hoegaerden.be/2010/03/29/retrieving-data-from-excel/</guid>
		<description><![CDATA[The purpose of this article is to demonstrate how to retrieve data from an Excel sheet and put it in a table in a SQL Server database. Introduction Anyone who&#8217;s ever used a computer for a significant amount of time has probably come into contact with Excel, the spreadsheet application part of the Microsoft Office [...]]]></description>
			<content:encoded><![CDATA[<p>The purpose of this article is to demonstrate how to retrieve data from an Excel sheet and put it in a table in a SQL Server database.</p>
<h2>Introduction</h2>
<p>Anyone who&#8217;s ever used a computer for a significant amount of time has probably come into contact with <a title="Excel 2007" href="http://office.microsoft.com/en-us/excel/default.aspx" target="_blank">Excel</a>, the spreadsheet application part of the Microsoft Office suite. Its main purposes are to perform calculations and create charts and pivot tables for analysis.</p>
<p>But people have great imagination and invent new uses for it every day.  I&#8217;ve even seen it used as a picture album.  (Sorry dad, but I know you won’t be reading this anyway. <img src='http://blog.hoegaerden.be/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> )  Ever since he had this specific YACI, or “Yet Another Computer Issue”, because his PC wasn’t powerful enough to open his 45 MB Excel file, uh, “picture collection”, he took some evening classes.  He’s now putting his Photoshopped pictures in PowerPoint…  Anyway, let’s get back on track now.</p>
<p>Another use, and the one that&#8217;s the subject of this article, is when Excel has been used as a database.  Come on, you know what I&#8217;m talking about, with the first row containing the column headers followed by possibly thousands of data rows.  The following screenshot contains an example, and is also the file that I will be using in this article.  I took all records from the <strong>Production.Product</strong> table in the <a title="AdventureWorks 2008R2 November CTP" href="http://msftdbprodsamples.codeplex.com/releases/view/24854" target="_blank">AdventureWorks 2008R2</a> database and dumped them in Excel.</p>
<p><a href="http://blog.hoegaerden.be/wp-content/uploads/image206.png"><img style="display: inline; border-width: 0px;" title="An Excel sheet used as a data store" src="http://blog.hoegaerden.be/wp-content/uploads/image_thumb54.png" border="0" alt="An Excel sheet used as a data store" width="681" height="224" /></a></p>
<p>At some point people will realize, either because someone told them or because they lost some data due to inattentiveness, that it wasn&#8217;t a really good idea to keep all that data in an Excel sheet.  And they&#8217;ll ask you to put it in a real database such as SQL Server.</p>
<p>That&#8217;s what I&#8217;m going to show you in the next paragraphs: how to import data from Excel into SQL Server.</p>
<h2>Using OPENROWSET() To Query Excel Files</h2>
<p>There are actually several different ways to achieve this.  In this article I will use the <a title="BOL2008: OPENROWSET (Transact-SQL)" href="http://msdn.microsoft.com/en-us/library/ms190312.aspx" target="_blank">OPENROWSET()</a> function.  This is a T-SQL function that can be used to access any OLE DB data source.  All you need is the right OLE DB driver.  The oldest version which I could confirm that contains this function is SQL Server 7.0, good enough to say that any version supports it.</p>
<p>My sample Excel files are located in <em>C:\temp\</em>.  This folder contains two files:<em> Products.xls</em> and <em>Products.xlsx</em>.  The first file is saved in the old format, Excel 97-2003, while the second file was saved from Excel 2010.  Both files contain the same data.  The sheet containing the list of products is called <em>ProductList</em>.</p>
<p>And here are the queries:</p>
<p><a href="http://11011.net/software/vspaste"></a><a href="http://11011.net/software/vspaste"></a></p>
<pre class="code"><span style="color: green;">--Excel 2007-2010
</span><span style="color: blue;">SELECT </span><span style="color: gray;">* </span><span style="color: green;">--INTO #productlist
</span><span style="color: blue;">FROM OPENROWSET</span><span style="color: gray;">(</span><span style="color: red;">'Microsoft.ACE.OLEDB.12.0'</span><span style="color: gray;">,
    </span><span style="color: red;">'Excel 12.0 Xml;HDR=YES;Database=C:\temp\Products.xlsx'</span><span style="color: gray;">,
    </span><span style="color: red;">'SELECT * FROM [ProductList$]'</span><span style="color: gray;">);

</span><span style="color: green;">--Excel 97-2003
</span><span style="color: blue;">SELECT </span><span style="color: gray;">* </span><span style="color: green;">--INTO #productlist
</span><span style="color: blue;">FROM OPENROWSET</span><span style="color: gray;">(</span><span style="color: red;">'Microsoft.Jet.OLEDB.4.0'</span><span style="color: gray;">,
    </span><span style="color: red;">'Excel 8.0;HDR=YES;Database=C:\temp\Products.xls'</span><span style="color: gray;">,
    </span><span style="color: red;">'select * from [ProductList$]'</span><span style="color: gray;">);</span></pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>These queries are just returning the data from the Excel file into the Results window, when executed using the Management Studio.  To insert the data into a table, uncomment the <a title="BOL2008: INTO Clause (Transact-SQL)" href="http://msdn.microsoft.com/en-us/library/ms188029.aspx" target="_blank">INTO</a> clause.  When uncommented, the statement retrieves the data from the Excel sheet and puts it into a newly-created <a title="BOL2008: Special Table Types" href="http://msdn.microsoft.com/en-us/library/ms186986.aspx" target="_blank">local temporary table</a> called <em>#productlist</em>.</p>
<p>Furthermore, the query assumes that the first row contains the header.  If that’s not the case, replace HDR=YES with HDR=NO.</p>
<p><strong>Note:</strong> if you get an error message when running the query, look further down in this article.  I’ve covered a couple of them.</p>
<p>With the INTO clause uncommented and the query executed, the temporary table can now be queried just like any other table:</p>
<pre class="code"><span style="color: blue;">SELECT </span><span style="color: gray;">* </span><span style="color: blue;">FROM </span>#productlist</pre>
<h3>What Type Is Your Data?</h3>
<p>Let’s have a look if this method of using a SELECT INTO in combination with OPENROWSET and a temporary table is smart enough to interpret the correct data types of the data coming in.  Use the following command to describe the metadata of the temporary table:</p>
<pre class="code"><span style="color: blue;">USE </span>tempdb<span style="color: gray;">;
</span><span style="color: blue;">GO
</span><span style="color: maroon;">sp_help </span><span style="color: red;">'#productlist'</span><span style="color: gray;">;</span></pre>
<p><a href="http://11011.net/software/vspaste"></a>Because a temporary table is stored in the <em>tempdb</em>, the <a title="BOL2008: sp_helptext (Transact-SQL)" href="http://msdn.microsoft.com/en-us/library/ms176112.aspx" target="_blank">sp_help</a> command should be issued against that database.</p>
<p>Here’s the part of the output in which we’re interested:</p>
<p><img style="display: inline; border-width: 0px;" title="The data types used when combining OPENROWSET with SELECT INTO" src="http://blog.hoegaerden.be/wp-content/uploads/image207.png" border="0" alt="The data types used when combining OPENROWSET with SELECT INTO" width="377" height="442" /></p>
<p>As you can see, anything that looks like text will be put in a field of type<em> nvarchar(510)</em> and anything that looks like a number (integers, floating-point numbers, datetime values, …) is put into a <em>float(53)</em>.  Not a lot of intelligence there.  This is the result when no formatting was put on the cells in Excel.</p>
<p>As an experiment I’ve changed the format of some fields in the Excel file and then retried the SELECT INTO statement.  What did I change?  I identified <em>ProductID</em> as being a number without any decimals, changed <em>StandardCost</em> and <em>ListPrice</em> to a currency with four decimal digits and I changed <em>SellStartDate</em> and <em>SellEndDate</em> to a custom date/time format showing both date and time.</p>
<p>The effect on the table creation was not completely as I would have expected:</p>
<p><img style="display: inline; border-width: 0px;" title="SELECT INTO with some field types changed" src="http://blog.hoegaerden.be/wp-content/uploads/image208.png" border="0" alt="SELECT INTO with some field types changed" width="382" height="446" /></p>
<p><em>ProductID</em> is still being stored into a float field, even though in Excel it’s defined as having no decimals.  And the datetime values are not recognized either.  Okay, I used a custom format there, so maybe it’s due to that.</p>
<p>It’s up to you of course how you use this method of importing the data.  You can put your records into a temporary table to process further, or you can create a table with the expected data types upfront and import the data directly into that one.</p>
<h2>Some Possible Issues</h2>
<p>Let’s cover some issues related to this method.</p>
<h3>Enable ‘AD Hoc Distributed Queries’</h3>
<p>The OPENROWSET() function expects that the <a title="BOL2008: ad hoc distributed queries Option" href="http://msdn.microsoft.com/en-us/library/ms187569.aspx" target="_blank">‘Ad Hoc Distributed Queries’ option</a> is enabled on the server.  When that’s not the case you’ll see the following message:</p>
<blockquote><p><span style="color: #ff0000;">Msg 15281, Level 16, State 1, Line 1</span></p>
<p><span style="color: #ff0000;">SQL Server blocked access to STATEMENT &#8216;OpenRowset/OpenDatasource&#8217; of component &#8216;Ad Hoc Distributed Queries&#8217; because this component is turned off as part of the security configuration for this server. A system administrator can enable the use of &#8216;Ad Hoc Distributed Queries&#8217; by using sp_configure. For more information about enabling &#8216;Ad Hoc Distributed Queries&#8217;, see &#8220;Surface Area Configuration&#8221; in SQL Server Books Online.</span></p></blockquote>
<p>This is one of the advanced options.  To enable it you can use the following command:</p>
<pre class="code"><span style="color: maroon;">sp_configure </span><span style="color: red;">'show advanced options'</span><span style="color: gray;">, </span>1<span style="color: gray;">;
</span><span style="color: blue;">GO
RECONFIGURE</span><span style="color: gray;">;
</span><span style="color: blue;">GO

</span><span style="color: maroon;">sp_configure </span><span style="color: red;">'Ad Hoc Distributed Queries'</span><span style="color: gray;">, </span>1<span style="color: gray;">;
</span><span style="color: blue;">GO
RECONFIGURE</span><span style="color: gray;">;
</span><span style="color: blue;">GO</span></pre>
<p>To get a good look at all the different settings, just run the <a title="BOL2008: sp_configure (Transact-SQL)" href="http://msdn.microsoft.com/en-us/library/ms188787.aspx" target="_blank">sp_configure</a> procedure without any parameters.</p>
<p><strong>Note:</strong> if you’re not the administrator of the server, you should talk to the DBA who’s responsible before attempting this.</p>
<h3>The File Needs To Be Closed</h3>
<p>When the Excel file is not closed, you’ll end up with the following error:</p>
<blockquote><p><span style="color: #ff0000;">Msg 7399, Level 16, State 1, Line 1</span></p>
<p><span style="color: #ff0000;">The OLE DB provider &#8220;Microsoft.Jet.OLEDB.4.0&#8243; for linked server &#8220;(null)&#8221; reported an error. The provider did not give any information about the error.</span></p>
<p><span style="color: #ff0000;">Msg 7303, Level 16, State 1, Line 1 </span></p>
<p><span style="color: #ff0000;">Cannot initialize the data source object of OLE DB provider &#8220;Microsoft.Jet.OLEDB.4.0&#8243; for linked server &#8220;(null)&#8221;.</span></p></blockquote>
<p>So close the file and try the query again.</p>
<h3>OLE DB Driver Not Installed</h3>
<p>The OPENROWSET() function uses OLE DB, so it needs a driver for your data source, in this case for Excel.  If the right driver is not installed, you’ll see the following error (or similar, depends on the version used).</p>
<blockquote><p><span style="color: #ff0000;">Msg 7302, Level 16, State 1, Line 1</span></p>
<p><span style="color: #ff0000;">Cannot create an instance of OLE DB provider &#8220;Microsoft.ACE.OLEDB.12.0&#8243; for linked server &#8220;(null)&#8221;.</span></p></blockquote>
<p>To solve the issue, install the right driver and try again.</p>
<p>How can you tell what drivers are installed?  Open up the <strong>ODBC Data Source Administrator</strong> window (Start &gt; Run &gt; type ODBCAD32.EXE and enter) and have a look in the <em>Drivers</em> tab.  The following screenshot (taken on a Dutch Windows XP) shows both the JET 4.0 driver for Excel 97-2003 and the fairly-new ACE driver for Excel 2007.</p>
<p><img style="display: inline; border-width: 0px;" title="odbcad32.exe - ODBC Data Source Administrator" src="http://blog.hoegaerden.be/wp-content/uploads/image209.png" border="0" alt="odbcad32.exe - ODBC Data Source Administrator" width="461" height="415" /></p>
<p>The drivers can be downloaded from the following pages on the Microsoft site:</p>
<p><a title="How to obtain the latest service pack for the Microsoft Jet 4.0 Database Engine" href="http://support.microsoft.com/kb/239114/" target="_blank">Excel 97-2003 Jet 4.0 driver</a></p>
<p><a title="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">Excel 2007 ACE driver &#8211; 12.00.6423.1000</a></p>
<p><a title="2010 Office System Driver Beta: Data Connectivity Components" href="http://www.microsoft.com/downloads/details.aspx?FamilyID=C06B8369-60DD-4B64-A44B-84B371EDE16D&amp;displaylang=en" target="_blank">Excel 2010 ACE driver (beta) &#8211; 14.00.4732.1000</a></p>
<p><strong>Sidenote:</strong> the Excel 2010 driver is not supported on Windows XP, but I was able to query the 2010 Excel sheet using the 2007 driver.  I guess that this is the result of the <a title="Wikipedia: Office Open XML" href="http://en.wikipedia.org/wiki/Office_Open_XML" target="_blank">Office Open XML standard</a> which was introduced in Office 2007.</p>
<p><strong>Driver backward-compatibility</strong></p>
<p>The ACE drivers are backwards-compatible.  So the following queries are working perfectly:</p>
<pre class="code"><span style="color: green;">--old Excel with new ACE driver - working query 1
</span><span style="color: blue;">SELECT </span><span style="color: gray;">* </span><span style="color: green;">--INTO #productlist
</span><span style="color: blue;">FROM OPENROWSET</span><span style="color: gray;">(</span><span style="color: red;">'Microsoft.ACE.OLEDB.12.0'</span><span style="color: gray;">,
    </span><span style="color: red;">'Excel 8.0;HDR=YES;Database=C:\temp\Products.xls'</span><span style="color: gray;">,
    </span><span style="color: red;">'SELECT * FROM [ProductList$]'</span><span style="color: gray;">);

</span><span style="color: green;">--old Excel with new ACE driver - working query 2
</span><span style="color: blue;">SELECT </span><span style="color: gray;">* </span><span style="color: green;">--INTO #productlist
</span><span style="color: blue;">FROM OPENROWSET</span><span style="color: gray;">(</span><span style="color: red;">'Microsoft.ACE.OLEDB.12.0'</span><span style="color: gray;">,
    </span><span style="color: red;">'Excel 12.0;HDR=YES;Database=C:\temp\Products.xls'</span><span style="color: gray;">,
    </span><span style="color: red;">'SELECT * FROM [ProductList$]'</span><span style="color: gray;">);</span></pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>In other words, you won’t be needing that first link for the Jet driver.  For the full story have a look at <a title="How to get a x64 version of Jet?" href="http://blogs.msdn.com/psssql/archive/2010/01/21/how-to-get-a-x64-version-of-jet.aspx" target="_blank">this blog post by Adam Saxton</a> of the CSS SQL Server Escalation Services team.</p>
<h3>The 64-bit Story</h3>
<p>So, what if you’re running a 64-bit OS?  I’ll start by saying that I had quite some issues getting OPENROWSET to work, but finally I managed it.  Following is a list of my attempts, each time with the resulting message.  And finally I’ll show you how I got it to work.  The problem was something really unexpected…</p>
<p><strong>ACE 14 64-bit through SSMS</strong></p>
<p>My main laptop is running Windows 7 64-bit, Office 2010 64-bit and SQL Server 2008 R2 64-bit.  So I installed the 64-bit version of the ACE 14 driver, which happens to be the first OLE DB driver for Excel that ships in 64-bit.  But when I execute my query I’m getting the following message:</p>
<blockquote><p><span style="color: #ff0000;">Msg 7403, Level 16, State 1, Line 1</span></p>
<p><span style="color: #ff0000;">The OLE DB provider &#8220;Microsoft.ACE.OLEDB.14.0&#8243; has not been registered.</span></p></blockquote>
<p>Is this because SSMS ships only in 32-bit?  Maybe, but I’m not able to install the 32-bit driver.  It doesn’t allow me to because I’ve got Office in 64-bit installed.  The installer throws me the following error:</p>
<p><img style="display: inline; border-width: 0px;" title="Microsoft Access database engine 2010 (beta) - You cannot install the 32-bit version of Access Database engine for Microsoft Office 2010 because you currently have 64-bit Office products installed..." src="http://blog.hoegaerden.be/wp-content/uploads/image210.png" border="0" alt="Microsoft Access database engine 2010 (beta) - You cannot install the 32-bit version of Access Database engine for Microsoft Office 2010 because you currently have 64-bit Office products installed..." width="416" height="218" /></p>
<p><strong>ACE 12 32-bit on a 64-bit machine</strong></p>
<p>When I check the installed drivers using the 32-bit version of the ODBC Data Source Administrator (located in C:\Windows\SysWOW64), I notice that the ACE 12 driver is installed.  However, trying to use that one from the Management Studio gives me this:</p>
<blockquote><p><span style="color: #ff0000;">Msg 7399, Level 16, State 1, Line 1</span></p>
<p><span style="color: #ff0000;">The OLE DB provider &#8220;Microsoft.ACE.OLEDB.12.0&#8243; for linked server &#8220;(null)&#8221; reported an error. The provider did not give any information about the error.</span></p>
<p><span style="color: #ff0000;">Msg 7330, Level 16, State 2, Line 1 </span></p>
<p><span style="color: #ff0000;">Cannot fetch a row from OLE DB provider &#8220;Microsoft.ACE.OLEDB.12.0&#8243; for linked server &#8220;(null)&#8221;.</span></p></blockquote>
<p>The Results pane shows all the columns with the right column names, retrieved from Excel.  But the driver seems to have a problem retrieving the actual data.</p>
<p>This issue with error 7330 is mentioned <a title="OPENROWSET with Excel 2007 -- 7330 Cannot fetch a row from OLE DB provider &quot;Microsoft.ACE.OLEDB.12.0&quot; for linked server &quot;(null)&quot;" href="http://social.msdn.microsoft.com/Forums/en-US/sqldataaccess/thread/8514b4bb-945a-423b-98fe-a4ec4d7366ea" target="_blank">in the following thread</a> on the SQL Server MSDN forum, but unfortunately the proposed solution didn’t solve the problem in my case.</p>
<p><strong>64-bit SQLCMD using ACE 14 driver</strong></p>
<p>I also tried using the 64-bit version of <a title="BOL2008: sqlcmd Utility" href="http://msdn.microsoft.com/en-us/library/ms162773.aspx" target="_blank">sqlcmd.exe</a>, but strangely enough that throws the same error.</p>
<p><a href="http://blog.hoegaerden.be/wp-content/uploads/image211.png"><img style="display: inline; border-width: 0px;" title="Using sqlcmd 64-bit to query Excel" src="http://blog.hoegaerden.be/wp-content/uploads/image_thumb55.png" border="0" alt="Using sqlcmd 64-bit to query Excel" width="688" height="377" /></a></p>
<p>I actually expected this last method to work, after all, everything is now running in 64-bit.  But alas, it didn’t…</p>
<p><strong>One more go…</strong></p>
<p>After some more trial and error, I have actually found a way to get the query to work.  I don’t have a logical explanation on why it’s behaving the way it is, but, well, it is working…</p>
<p>This query is running fine:</p>
<pre class="code"><span style="color: blue;">SELECT </span><span style="color: gray;">* </span><span style="color: green;">--INTO #productlist
</span><span style="color: blue;">FROM OPENROWSET</span><span style="color: gray;">(</span><span style="color: red;">'Microsoft.ACE.OLEDB.12.0'</span><span style="color: gray;">,
    </span><span style="color: red;">'Excel 12.0 Xml;HDR=YES;Database=C:\temp\Products.xlsx'</span><span style="color: gray;">,
    </span><span style="color: red;">'SELECT * FROM [ProductList$]'</span><span style="color: gray;">);</span></pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>But this one isn’t:</p>
<pre class="code"><span style="color: green;">--Excel 2007-2010
</span><span style="color: blue;">SELECT </span><span style="color: gray;">* </span><span style="color: green;">--INTO #productlist
</span><span style="color: blue;">FROM OPENROWSET</span><span style="color: gray;">(</span><span style="color: red;">'Microsoft.ACE.OLEDB.12.0'</span><span style="color: gray;">,
    </span><span style="color: red;">'Excel 12.0 Xml;HDR=YES;Database=C:\temp\Products.xlsx'</span><span style="color: gray;">,
    </span><span style="color: red;">'SELECT * FROM [ProductList$]'</span><span style="color: gray;">);</span></pre>
<p>It’s exactly the same query, only difference is the comment line at the start.  And even weirder, if I add a space after the double-dash, the query works fine as well!</p>
<p>Then I decided to remove the commented INTO clause.  This made the weird behavior disappear.  So for some reason SQL Server doesn’t like the OPENROWSET function combined with comments inside the query.  The strange behavior also disappears when a space is added between the double-dash and the INTO keyword.</p>
<p>Uh, computers can be so much fun, right? <img src='http://blog.hoegaerden.be/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
<p>If anyone has got an explanation on this strange behavior: please do post a comment!  For now my conclusion is: don’t use comments when creating an OPENROWSET query.</p>
<p><strong><span style="text-decoration: underline;">IMPORTANT UPDATE (April 11, 2010)</span>: it seems that the current installer for the ACE 14 driver contains a bug and registers it as being “Microsoft.ACE.OLEDB.12.0” instead of “Microsoft.ACE.OLEDB.14.0” .  This explains some of the issues shown above.  Some evidence on the issue:</strong></p>
<p><strong><a title="Access Database Engine 2010 installation issue to use with ADO access technology to access data from Jet database (.mdb files)" href="https://connect.microsoft.com/data/feedback/details/541090/access-database-engine-2010-installation-issue-to-use-with-ado-access-technology-to-access-data-from-jet-database-mdb-files?wa=wsignin1.0" target="_blank">Microsoft Connect: Access Database Engine 2010 installation issue to use with ADO access technology to access data from Jet database (.mdb files)</a></strong></p>
<p><strong><a title="The 'Microsoft.ACE.OLEDB.14.0' provider is not registered ....." href="http://forums.devarticles.com/microsoft-access-development-49/the-microsoft-ace-oledb-14-0-provider-is-not-registered-238601.html" target="_blank">The &#8216;Microsoft.ACE.OLEDB.14.0&#8242; provider is not registered &#8230;.. (see last comment)</a></strong></p>
<p><strong><a title="Excel Services, ODC and Microsoft.ACE.OLEDB.14.0" href="http://blog.sharepointalist.com/2010/03/odc-microsoft-ace-oledb-14-0.html" target="_blank">Excel Services, ODC and Microsoft.ACE.OLEDB.14.0</a></strong></p>
<h2>Conclusion</h2>
<p>The above has shown that OPENROWSET() can be a useful function, given the right circumstances.  But in the wrong setting it can be quite cumbersome to get to work.</p>
<p>I would recommend this method only for one-off quick imports, such as when you as a developer are given a bunch of data in a spreadsheet and need to get it into the database, one way or another.  I would not use it for an automated import process.  For that we’ve got a more interesting alternative which I’ll cover <a title="Importing Excel Data Using Integration Services" href="http://blog.hoegaerden.be/2010/04/20/importing-excel-data-using-integration-services/">in an upcoming article</a>.</p>
<p>Have fun!</p>
<p>Valentino.</p>
<p><strong>References</strong></p>
<p><a title="BOL 2008: Special Table Types" href="http://msdn.microsoft.com/en-us/library/ms186986.aspx" target="_blank">BOL 2008: Special Table Types (incl. temporary tables)</a></p>
<p><a title="BOL 2008: OPENROWSET() function" href="http://msdn.microsoft.com/en-us/library/ms190312.aspx" target="_blank">BOL 2008: OPENROWSET() function</a></p>
<p><a title="BOL 2008: the INTO clause" href="http://msdn.microsoft.com/en-us/library/ms188029.aspx" target="_blank">BOL 2008: the INTO clause</a></p>
<p><a title="How to get a x64 version of Jet?" href="http://blogs.msdn.com/psssql/archive/2010/01/21/how-to-get-a-x64-version-of-jet.aspx" target="_blank">CSS SQL Server Engineers: How to get a x64 version of Jet?</a></p>
<p><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fblog.hoegaerden.be%2F2010%2F03%2F29%2Fretrieving-data-from-excel%2F&amp;title=Retrieving%20Data%20From%20Excel" id="wpa2a_14"><img src="http://blog.hoegaerden.be/wp-content/plugins/add-to-any/share_save_171_16.png" width="171" height="16" alt="Share"/></a></p>]]></content:encoded>
			<wfw:commentRss>http://blog.hoegaerden.be/2010/03/29/retrieving-data-from-excel/feed/</wfw:commentRss>
		<slash:comments>55</slash:comments>
		</item>
		<item>
		<title>Recursively Delete SSIS Folder</title>
		<link>http://blog.hoegaerden.be/2010/02/28/recursively-delete-ssis-folder/</link>
		<comments>http://blog.hoegaerden.be/2010/02/28/recursively-delete-ssis-folder/#comments</comments>
		<pubDate>Sun, 28 Feb 2010 21:12:51 +0000</pubDate>
		<dc:creator>Valentino Vranken</dc:creator>
				<category><![CDATA[Integration Services]]></category>
		<category><![CDATA[SQLServerPedia Syndication]]></category>
		<category><![CDATA[T-SQL]]></category>
		<category><![CDATA[data]]></category>
		<category><![CDATA[Script]]></category>
		<category><![CDATA[SSIS]]></category>

		<guid isPermaLink="false">http://blog.hoegaerden.be/2010/02/28/recursively-delete-ssis-folder/</guid>
		<description><![CDATA[A while ago I posted a query to create a list of all the Integration Services packages deployed to the MSDB.  I am now using that query to take it a step further. If you’ve been using SSIS for a while you’ve probably noticed that the Management Studio doesn’t like to delete Integration Services folders [...]]]></description>
			<content:encoded><![CDATA[<p>A while ago I posted <a title="List All SSIS Packages Deployed On Your Integration Server" href="http://blog.hoegaerden.be/2010/01/10/list-all-ssis-packages-deployed-on-your-integration-server/" target="_blank">a query to create a list of all the Integration Services packages deployed to the MSDB</a>.  I am now using that query to take it a step further.</p>
<p>If you’ve been using SSIS for a while you’ve probably noticed that the Management Studio doesn’t like to delete Integration Services folders that are not empty.  In fact, it will politely ask you if you’re sure that you want to delete the folder on which you’ve just selected the “Delete” option through the right-click menu.</p>
<p><img style="display: inline; border: 0px;" title="Right-click pop-up menu on SSIS folder" src="http://blog.hoegaerden.be/wp-content/uploads/image195.png" border="0" alt="Right-click pop-up menu on SSIS folder" width="393" height="160" /></p>
<p><img style="display: inline; border: 0px;" title="I am sure I want to delete this non-empty SSIS folder" src="http://blog.hoegaerden.be/wp-content/uploads/image196.png" border="0" alt="I am sure I want to delete this non-empty SSIS folder" width="621" height="137" /></p>
<p>So you click the Yes button.  But then it shows you the following message:</p>
<blockquote><p>SSIS folder &#8216;FolderWithSubfolders&#8217; contains packages and/or other folders. You must drop these first. (Microsoft SQL Server Native Client 10.0)</p></blockquote>
<p>Graphically it looks like this:</p>
<p><img style="display: inline; border: 0px;" title="Object Explorer pop-up: you can't delete SSIS folders that contain packages or other folders" src="http://blog.hoegaerden.be/wp-content/uploads/image197.png" border="0" alt="Object Explorer pop-up: you can't delete SSIS folders that contain packages or other folders" width="621" height="137" /></p>
<p>And this message can be really annoying if you’ve got a main folder with, let’s say, five subfolders, and each subfolder contains about 20-30 packages.  If you want to delete this folder you first need to delete each package separately and then delete the five subfolders, and then you can finally delete the main folder.  And all that through the right-click pop-up menu because you can’t just select the object in the Object Explorer and hit the Delete button on the keyboard – it doesn’t have an action on SSIS objects…</p>
<p>So, I wasn’t planning on doing such a job manually and came up with the following stored procedure.</p>
<p>It’s probably a bit long but don’t run away just yet, I will explain what’s going on down below the code, and there are some comments in the code as well.</p>
<p><a href="http://11011.net/software/vspaste"></a></p>
<pre class="code"><span style="color: green;">/*
DESCRIPTION: Deletes all folders and packages under, and including, specified folder.
WRITTEN BY:  Valentino Vranken
CREATED:     2010-02-28
VERSION:     1.0
USAGE:
  -- mind the forward slash
  EXEC dbo.SSIS_RecursiveDeleteFolder '/FolderWithSubfolders'
  -- to delete a subfolder
  EXEC dbo.SSIS_RecursiveDeleteFolder '/FolderWithSubfolders/ASubfolderWithPackages'

COPIED FROM: http://blog.hoegaerden.be

Note 1: folder names are not case-sensitive
Note 2: uses system tables and (undocumented) stored procedures located in MSDB.
Note 3: this code was written for SQL Server 2008. For 2005:
  o sysssispackagefolders -&gt; sysdtspackagefolders90
  o sysssispackages -&gt; sysdtspackages90
  o sp_ssis_deletefolder -&gt; sp_dts_deletefolder
  o sp_ssis_deletepackage -&gt; sp_dts_deletepackage
*/
</span><span style="color: blue;">CREATE PROCEDURE </span>dbo<span style="color: gray;">.</span>SSIS_RecursiveDeleteFolder
    @Folder <span style="color: blue;">varchar</span><span style="color: gray;">(</span>2000<span style="color: gray;">)
</span><span style="color: blue;">AS
BEGIN
    set nocount on</span><span style="color: gray;">;

    </span><span style="color: blue;">declare </span>@foldersToDelete <span style="color: blue;">table
    </span><span style="color: gray;">(
        </span>folderid <span style="color: blue;">uniqueidentifier</span><span style="color: gray;">,
        </span>Lvl <span style="color: blue;">int
    </span><span style="color: gray;">);

    </span><span style="color: blue;">declare </span>@packagesToDelete <span style="color: blue;">table
    </span><span style="color: gray;">(
        </span>PackageName <span style="color: blue;">sysname</span><span style="color: gray;">,
        </span>folderid <span style="color: blue;">uniqueidentifier</span><span style="color: gray;">,
        </span>Lvl <span style="color: blue;">int
    </span><span style="color: gray;">);

    </span><span style="color: green;">--retrieve list of folders to be deleted
    </span><span style="color: blue;">with </span>ChildFolders
    <span style="color: blue;">as
    </span><span style="color: gray;">(
        </span><span style="color: blue;">select </span>PARENT<span style="color: gray;">.</span>parentfolderid<span style="color: gray;">, </span>PARENT<span style="color: gray;">.</span>folderid<span style="color: gray;">, </span>PARENT<span style="color: gray;">.</span>foldername<span style="color: gray;">,
            </span><span style="color: magenta;">cast</span><span style="color: gray;">(</span><span style="color: red;">'' </span><span style="color: blue;">as sysname</span><span style="color: gray;">) </span><span style="color: blue;">as </span>RootFolder<span style="color: gray;">,
            </span><span style="color: magenta;">cast</span><span style="color: gray;">(</span>PARENT<span style="color: gray;">.</span>foldername <span style="color: blue;">as varchar</span><span style="color: gray;">(</span><span style="color: magenta;">max</span><span style="color: gray;">)) </span><span style="color: blue;">as </span>FullPath<span style="color: gray;">,
            </span>0 <span style="color: blue;">as </span>Lvl
        <span style="color: blue;">from </span>msdb<span style="color: gray;">.</span>dbo<span style="color: gray;">.</span>sysssispackagefolders PARENT
        <span style="color: blue;">where </span>PARENT<span style="color: gray;">.</span>parentfolderid <span style="color: gray;">is null
        </span><span style="color: blue;">UNION </span><span style="color: gray;">ALL
        </span><span style="color: blue;">select </span>CHILD<span style="color: gray;">.</span>parentfolderid<span style="color: gray;">, </span>CHILD<span style="color: gray;">.</span>folderid<span style="color: gray;">, </span>CHILD<span style="color: gray;">.</span>foldername<span style="color: gray;">,
            </span><span style="color: blue;">case </span>ChildFolders<span style="color: gray;">.</span>Lvl
                <span style="color: blue;">when </span>0 <span style="color: blue;">then </span>CHILD<span style="color: gray;">.</span>foldername
                <span style="color: blue;">else </span>ChildFolders<span style="color: gray;">.</span>RootFolder
            <span style="color: blue;">end as </span>RootFolder<span style="color: gray;">,
            </span><span style="color: magenta;">cast</span><span style="color: gray;">(</span>ChildFolders<span style="color: gray;">.</span>FullPath <span style="color: gray;">+ </span><span style="color: red;">'/' </span><span style="color: gray;">+ </span>CHILD<span style="color: gray;">.</span>foldername <span style="color: blue;">as varchar</span><span style="color: gray;">(</span><span style="color: magenta;">max</span><span style="color: gray;">))
                </span><span style="color: blue;">as </span>FullPath<span style="color: gray;">,
            </span>ChildFolders<span style="color: gray;">.</span>Lvl <span style="color: gray;">+ </span>1 <span style="color: blue;">as </span>Lvl
        <span style="color: blue;">from </span>msdb<span style="color: gray;">.</span>dbo<span style="color: gray;">.</span>sysssispackagefolders CHILD
            <span style="color: gray;">inner join </span>ChildFolders <span style="color: blue;">on </span>ChildFolders<span style="color: gray;">.</span>folderid <span style="color: gray;">= </span>CHILD<span style="color: gray;">.</span>parentfolderid
    <span style="color: gray;">)
    </span><span style="color: blue;">insert into </span>@foldersToDelete
    <span style="color: blue;">select </span>F<span style="color: gray;">.</span>folderid<span style="color: gray;">, </span>F<span style="color: gray;">.</span>Lvl
    <span style="color: blue;">from </span>ChildFolders F
    <span style="color: blue;">where </span>F<span style="color: gray;">.</span>FullPath <span style="color: gray;">like </span>@Folder <span style="color: gray;">+ </span><span style="color: red;">'%'</span><span style="color: gray;">;

    </span><span style="color: green;">--retrieve list of packages to be deleted
    </span><span style="color: blue;">with </span>ChildFolders
    <span style="color: blue;">as
    </span><span style="color: gray;">(
        </span><span style="color: blue;">select </span>PARENT<span style="color: gray;">.</span>parentfolderid<span style="color: gray;">, </span>PARENT<span style="color: gray;">.</span>folderid<span style="color: gray;">, </span>PARENT<span style="color: gray;">.</span>foldername<span style="color: gray;">,
            </span><span style="color: magenta;">cast</span><span style="color: gray;">(</span><span style="color: red;">'' </span><span style="color: blue;">as sysname</span><span style="color: gray;">) </span><span style="color: blue;">as </span>RootFolder<span style="color: gray;">,
            </span><span style="color: magenta;">cast</span><span style="color: gray;">(</span>PARENT<span style="color: gray;">.</span>foldername <span style="color: blue;">as varchar</span><span style="color: gray;">(</span><span style="color: magenta;">max</span><span style="color: gray;">)) </span><span style="color: blue;">as </span>FullPath<span style="color: gray;">,
            </span>0 <span style="color: blue;">as </span>Lvl
        <span style="color: blue;">from </span>msdb<span style="color: gray;">.</span>dbo<span style="color: gray;">.</span>sysssispackagefolders PARENT
        <span style="color: blue;">where </span>PARENT<span style="color: gray;">.</span>parentfolderid <span style="color: gray;">is null
        </span><span style="color: blue;">UNION </span><span style="color: gray;">ALL
        </span><span style="color: blue;">select </span>CHILD<span style="color: gray;">.</span>parentfolderid<span style="color: gray;">, </span>CHILD<span style="color: gray;">.</span>folderid<span style="color: gray;">, </span>CHILD<span style="color: gray;">.</span>foldername<span style="color: gray;">,
            </span><span style="color: blue;">case </span>ChildFolders<span style="color: gray;">.</span>Lvl
                <span style="color: blue;">when </span>0 <span style="color: blue;">then </span>CHILD<span style="color: gray;">.</span>foldername
                <span style="color: blue;">else </span>ChildFolders<span style="color: gray;">.</span>RootFolder
            <span style="color: blue;">end as </span>RootFolder<span style="color: gray;">,
            </span><span style="color: magenta;">cast</span><span style="color: gray;">(</span>ChildFolders<span style="color: gray;">.</span>FullPath <span style="color: gray;">+ </span><span style="color: red;">'/' </span><span style="color: gray;">+ </span>CHILD<span style="color: gray;">.</span>foldername <span style="color: blue;">as varchar</span><span style="color: gray;">(</span><span style="color: magenta;">max</span><span style="color: gray;">))
                </span><span style="color: blue;">as </span>FullPath<span style="color: gray;">,
            </span>ChildFolders<span style="color: gray;">.</span>Lvl <span style="color: gray;">+ </span>1 <span style="color: blue;">as </span>Lvl
        <span style="color: blue;">from </span>msdb<span style="color: gray;">.</span>dbo<span style="color: gray;">.</span>sysssispackagefolders CHILD
            <span style="color: gray;">inner join </span>ChildFolders <span style="color: blue;">on </span>ChildFolders<span style="color: gray;">.</span>folderid <span style="color: gray;">= </span>CHILD<span style="color: gray;">.</span>parentfolderid
    <span style="color: gray;">)
    </span><span style="color: blue;">insert into </span>@packagesToDelete
    <span style="color: blue;">select </span>P<span style="color: gray;">.</span>name<span style="color: gray;">, </span>F<span style="color: gray;">.</span>folderid<span style="color: gray;">, </span>F<span style="color: gray;">.</span>Lvl
    <span style="color: blue;">from </span>ChildFolders F
        <span style="color: gray;">inner join </span>msdb<span style="color: gray;">.</span>dbo<span style="color: gray;">.</span>sysssispackages P <span style="color: blue;">on </span>P<span style="color: gray;">.</span>folderid <span style="color: gray;">= </span>F<span style="color: gray;">.</span>folderid
    <span style="color: blue;">where </span>F<span style="color: gray;">.</span>FullPath <span style="color: gray;">like </span>@Folder <span style="color: gray;">+ </span><span style="color: red;">'%'</span><span style="color: gray;">;

    </span><span style="color: green;">--use cursor to loop over objects to be deleted
    </span><span style="color: blue;">declare </span>objectsToDelete_cursor <span style="color: blue;">cursor
    for
        select </span>P<span style="color: gray;">.</span>folderid<span style="color: gray;">, </span>P<span style="color: gray;">.</span>Lvl<span style="color: gray;">, </span>P<span style="color: gray;">.</span>PackageName<span style="color: gray;">, </span><span style="color: red;">'P' </span><span style="color: blue;">as </span>ObjectType
        <span style="color: blue;">from </span>@packagesToDelete P
        <span style="color: blue;">UNION </span><span style="color: gray;">ALL
        </span><span style="color: blue;">select </span>F<span style="color: gray;">.</span>folderid<span style="color: gray;">, </span>F<span style="color: gray;">.</span>Lvl<span style="color: gray;">, null, </span><span style="color: red;">'F'
        </span><span style="color: blue;">from </span>@foldersToDelete F
        <span style="color: blue;">order by </span>Lvl <span style="color: blue;">desc</span><span style="color: gray;">, </span>ObjectType <span style="color: blue;">desc</span><span style="color: gray;">;

    </span><span style="color: blue;">open </span>objectsToDelete_cursor<span style="color: gray;">;

    </span><span style="color: blue;">declare </span>@folderid <span style="color: blue;">uniqueidentifier</span><span style="color: gray;">;
    </span><span style="color: blue;">declare </span>@lvl <span style="color: blue;">int</span><span style="color: gray;">;
    </span><span style="color: blue;">declare </span>@packageName <span style="color: blue;">sysname</span><span style="color: gray;">;
    </span><span style="color: blue;">declare </span>@objectType <span style="color: blue;">char</span><span style="color: gray;">;

    </span><span style="color: blue;">fetch next from </span>objectsToDelete_cursor
    <span style="color: blue;">into </span>@folderid<span style="color: gray;">, </span>@lvl<span style="color: gray;">, </span>@packageName<span style="color: gray;">, </span>@objectType<span style="color: gray;">;

    </span><span style="color: blue;">while </span><span style="color: magenta;">@@FETCH_STATUS </span><span style="color: gray;">= </span>0
    <span style="color: blue;">begin
        if </span>@objectType <span style="color: gray;">= </span><span style="color: red;">'F'
        </span><span style="color: blue;">begin
            print </span><span style="color: red;">'exec msdb.dbo.sp_ssis_deletefolder '
                </span><span style="color: gray;">+ </span><span style="color: magenta;">cast</span><span style="color: gray;">(</span>@folderid <span style="color: blue;">as varchar</span><span style="color: gray;">(</span><span style="color: magenta;">max</span><span style="color: gray;">));
            </span><span style="color: blue;">exec </span>msdb<span style="color: gray;">.</span>dbo<span style="color: gray;">.</span><span style="color: maroon;">sp_ssis_deletefolder </span>@folderid<span style="color: gray;">;
        </span><span style="color: blue;">end
        else
        begin
            print </span><span style="color: red;">'exec msdb.dbo.sp_ssis_deletepackage '
                </span><span style="color: gray;">+ </span>@packageName <span style="color: gray;">+ </span><span style="color: red;">', ' </span><span style="color: gray;">+ </span><span style="color: magenta;">cast</span><span style="color: gray;">(</span>@folderid <span style="color: blue;">as varchar</span><span style="color: gray;">(</span><span style="color: magenta;">max</span><span style="color: gray;">));
            </span><span style="color: blue;">exec </span>msdb<span style="color: gray;">.</span>dbo<span style="color: gray;">.</span><span style="color: maroon;">sp_ssis_deletepackage </span>@packageName<span style="color: gray;">, </span>@folderid<span style="color: gray;">;
        </span><span style="color: blue;">end

        fetch next from </span>objectsToDelete_cursor
        <span style="color: blue;">into </span>@folderid<span style="color: gray;">, </span>@lvl<span style="color: gray;">, </span>@packageName<span style="color: gray;">, </span>@objectType<span style="color: gray;">;
    </span><span style="color: blue;">end</span><span style="color: gray;">;

    </span><span style="color: blue;">close </span>objectsToDelete_cursor<span style="color: gray;">;
    </span><span style="color: blue;">deallocate </span>objectsToDelete_cursor<span style="color: gray;">;
</span><span style="color: blue;">END</span></pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>Before trying to dismantle this stored procedure, I recommend you to read <a title="List All SSIS Packages Deployed On Your Integration Server" href="http://blog.hoegaerden.be/2010/01/10/list-all-ssis-packages-deployed-on-your-integration-server/" target="_blank">my previous article on retrieving the list of packages</a>.  That already explains half of the code, if not 75%.</p>
<p>Our mission is to find a way to recursively delete packages and folders contained in a specified folder.  To be able to loop over those objects in the correct order (from the deepest level up until the level of the folder specified), the SP creates two table variables: one to hold all folders under the specified folder (@foldersToDelete) and one to hold the packages under the specified folder, including all subfolders (@packagesToDelete).</p>
<p>Based on those two lists I create a cursor that joins these two together, taking their level and object type into consideration.  That’s important because we first need to delete the packages in the lowest level folder, followed by their containing folder, then move one level up and do the same.</p>
<p>We then use the cursor to loop over the packages and folders and use two undocumented system stored procedures – one for each object type- to delete the package or folder.  These system SPs are located in the MSDB.  Here’s how they are defined:</p>
<pre class="code"><span style="color: blue;">ALTER PROCEDURE </span>[dbo]<span style="color: gray;">.</span>[sp_ssis_deletefolder]
  @folderid <span style="color: blue;">uniqueidentifier
AS</span></pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<pre class="code"><span style="color: blue;">ALTER PROCEDURE </span>[dbo]<span style="color: gray;">.</span>[sp_ssis_deletepackage]
  @name <span style="color: blue;">sysname</span><span style="color: gray;">,
  </span>@folderid <span style="color: blue;">uniqueidentifier
AS</span></pre>
<p><a href="http://11011.net/software/vspaste"></a>As you can see, the parameters for these procedures are not that complicated.  Both of them expect a <em>uniqueidentifier</em> as identification for the folder.  That’s okay, these IDs are stored in the <em>msdb.dbo.sysssispackagefolders</em> table and retrieved by our queries to create the list of to-be-deleted objects.</p>
<p>Furthermore, the <em>sp_ssis_deletepackage</em> SP expects the name of the package to be deleted.  Not a problem either, those names are obtained from the<em> msdb.dbo.sysssispackages</em> table.</p>
<p><strong>Note for SQL Server 2005 users:</strong> this code was written for SQL Server 2008.  The system stored procedures and system tables exist in 2005 as well, but they have different names.  See the comment header of my SP for more details.</p>
<p>So, let’s give it a little test.  Following screenshot shows the setup.  What I will do is use the stored procedure to delete the <em>FolderWithSubfolders</em> folder.  If you’ve been paying close attention, that is the same folder which I tried to delete manually through the Management Studio’s right-click menu (see first screenshot above).</p>
<p><img style="display: inline; border: 0px;" title="Overview of my deployed folders and packages" src="http://blog.hoegaerden.be/wp-content/uploads/image198.png" border="0" alt="Overview of my deployed folders and packages" width="255" height="207" /></p>
<p>After creating the SP, I ran following command:</p>
<pre class="code"><span style="color: blue;">EXEC </span>dbo<span style="color: gray;">.</span>SSIS_RecursiveDeleteFolder <span style="color: red;">'/FolderWithSubfolders'</span></pre>
<p><a href="http://11011.net/software/vspaste"></a>And that gave me the following output in the Messages pane:</p>
<blockquote><p>exec msdb.dbo.sp_ssis_deletepackage AnotherPackage, 7F38288D-4370-40A8-80E3-E92283033E4C</p>
<p>exec msdb.dbo.sp_ssis_deletepackage Package, 7F38288D-4370-40A8-80E3-E92283033E4C</p>
<p>exec msdb.dbo.sp_ssis_deletefolder 4102ED59-ED75-4D93-BBAE-0A162447BF02</p>
<p>exec msdb.dbo.sp_ssis_deletefolder 7F38288D-4370-40A8-80E3-E92283033E4C</p>
<p>exec msdb.dbo.sp_ssis_deletefolder C156B436-8C78-4BF9-99F9-5ABFAB10C405</p></blockquote>
<p>I have deliberately put a couple of print commands in the stored procedure to dump the commands that are actually being executed.  This gives us a good idea of what’s going on.</p>
<p>That’s it for now folks.  Thank you for reading this, and if you found it useful or you’ve got some questions about it: post a comment!</p>
<p>Have fun!</p>
<p>Valentino.</p>
<p><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fblog.hoegaerden.be%2F2010%2F02%2F28%2Frecursively-delete-ssis-folder%2F&amp;title=Recursively%20Delete%20SSIS%20Folder" id="wpa2a_16"><img src="http://blog.hoegaerden.be/wp-content/plugins/add-to-any/share_save_171_16.png" width="171" height="16" alt="Share"/></a></p>]]></content:encoded>
			<wfw:commentRss>http://blog.hoegaerden.be/2010/02/28/recursively-delete-ssis-folder/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Memory Dumps And Crazy Code Samples</title>
		<link>http://blog.hoegaerden.be/2010/02/06/memory-dumps-and-crazy-code-samples/</link>
		<comments>http://blog.hoegaerden.be/2010/02/06/memory-dumps-and-crazy-code-samples/#comments</comments>
		<pubDate>Sat, 06 Feb 2010 10:53:29 +0000</pubDate>
		<dc:creator>Valentino Vranken</dc:creator>
				<category><![CDATA[Reporting Services]]></category>
		<category><![CDATA[T-SQL]]></category>
		<category><![CDATA[data]]></category>
		<category><![CDATA[Fun]]></category>
		<category><![CDATA[memory dump]]></category>
		<category><![CDATA[SSRS]]></category>

		<guid isPermaLink="false">http://blog.hoegaerden.be/2010/02/06/memory-dumps-and-crazy-code-samples/</guid>
		<description><![CDATA[I usually don’t write posts just to mention a link to another site.  Except when I’ve come across articles which are so good that I want everyone to know about them.  Here are a couple of articles in that particular category. What Does Microsoft Do With Those Memory Dumps? The first one is written by [...]]]></description>
			<content:encoded><![CDATA[<p>I usually don’t write posts just to mention a link to another site.  Except when I’ve come across articles which are so good that I want everyone to know about them.  Here are a couple of articles in that particular category.</p>
<h2>What Does Microsoft Do With Those Memory Dumps?</h2>
<p>The first one is written by <strong>Adam W. Saxton</strong>, a member of the <a title="CSS SQL Server Engineers blog" href="http://blogs.msdn.com/psssql/default.aspx" target="_blank">Microsoft Customer Service and Support (CSS) SQL Server Escalation Services team</a>.</p>
<p>The subject of the post is an issue that developers of Reporting Services reports may come across: <strong><em>“InvalidReportParameterException</em></strong> <strong>with Data Driven Subscription”</strong>.  The report complains about an invalid parameter while the parameters seem to be okay, visually.  Then the author goes on to describe the actual issue: trailing spaces!  If you’ve been doing BI for a while, this is a quite common issue.  Adam also shows the difference between the <a title="BOL 2008: LEN (Transact-SQL)" href="http://msdn.microsoft.com/en-us/library/ms190329.aspx" target="_blank">LEN()</a> and the <a title="BOL 2008: DATALENGTH (Transact-SQL)" href="http://msdn.microsoft.com/en-us/library/ms173486.aspx" target="_blank">DATALENGTH()</a> functions.</p>
<p>But that’s not the reason that I’m mentioning that article here.  The best part starts following the Summary chapter, and is entitled <strong>Techie Details</strong>.  In that extra chapter he shows how the CSS team uses those crash memory dumps which I’m sure you’ve all seen now and then. </p>
<p>I won’t give you any details about that, just<strong> </strong><a title="InvalidReportParameterException with Data Driven Subscription" href="http://blogs.msdn.com/psssql/archive/2009/12/10/invalidreportparameterexception-with-data-driven-subscription.aspx" target="_blank"><strong>have a look at the article</strong></a>.  If you’re a developer, there’s some very valuable information there!</p>
<p>I am definitely looking forward to seeing his pre-conference session at the <a title="PASS European Conference 2010" href="http://www.sqlpass.org/summit/eu2010/" target="_blank">SQL PASS European Conference in Germany</a> this year: <a title="Pre-Conference Sessions: PASS European Conference 2010 - Tackling Top Reporting Services Issues" href="http://www.sqlpass.org/summit/eu2010/Agenda/PreConference/TacklingTopReportingServicesIssues.aspx" target="_blank">Tackling Top Reporting Services Issues</a>!</p>
<h2>Some Crazy Code Samples</h2>
<p>The second article that I’d like to mention here is written by <a title="Phil Factor" href="http://www.simple-talk.com/author/phil-factor/" target="_blank">Phil Factor</a>, a regular at the <a title="simple-talk" href="http://www.simple-talk.com" target="_blank">Simple-Talk</a> site.</p>
<p>This article is called <a title="Laying out SQL Code" href="http://www.simple-talk.com/sql/t-sql-programming/laying-out-sql-code" target="_blank">Laying out SQL Code</a> and mentions some database naming conventions and T-SQL coding layout, as the title already implies.  Even if you’re not interested in that (although as a serious developer you should be!!), the article is worth the effort of reading just for its code samples.</p>
<p>Here’s my favorite one:</p>
<pre class="code"><span style="color: blue">CREATE TABLE </span>"╚╦╩╗" <span style="color: gray">( </span>"└┬┴┐" <span style="color: blue">nvarchar</span><span style="color: gray">(</span>10<span style="color: gray">))
</span><span style="color: blue">DECLARE </span>@ <span style="color: blue">nvarchar</span><span style="color: gray">(</span>10<span style="color: gray">)  </span><span style="color: blue">set </span>@<span style="color: gray">=</span><span style="color: red">'═'
</span><span style="color: blue">INSERT  INTO </span>"╚╦╩╗"
        <span style="color: gray">( </span>"└┬┴┐" <span style="color: gray">)
        </span><span style="color: blue">SELECT  </span><span style="color: magenta">replicate</span><span style="color: gray">(</span>@<span style="color: gray">,</span>5<span style="color: gray">)

</span><span style="color: blue">SELECT  </span><span style="color: gray">*
</span><span style="color: blue">FROM    </span>"╚╦╩╗"</pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>And just to prove to you that this really is valid code, here’s the result:</p>
<p><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="Result of crazy query" src="http://blog.hoegaerden.be/wp-content/uploads/image173.png" border="0" alt="Result of crazy query" width="123" height="71" /></p>
<p>So, how about that database for that plumbing company? <img src='http://blog.hoegaerden.be/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
<p>BTW: when running that code without paying attention to the details (such as what DB your SSMS is connected to), you may end up with something like this:</p>
<p><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="Silly table stored in master DB - that's not a best practice!" src="http://blog.hoegaerden.be/wp-content/uploads/image174.png" border="0" alt="Silly table stored in master DB - that's not a best practice!" width="192" height="94" /> </p>
<p>So you may want to clean up after running the query using this statement:</p>
<pre class="code"><span style="color: blue">drop table </span>"╚╦╩╗"</pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>Have fun!</p>
<p><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fblog.hoegaerden.be%2F2010%2F02%2F06%2Fmemory-dumps-and-crazy-code-samples%2F&amp;title=Memory%20Dumps%20And%20Crazy%20Code%20Samples" id="wpa2a_18"><img src="http://blog.hoegaerden.be/wp-content/plugins/add-to-any/share_save_171_16.png" width="171" height="16" alt="Share"/></a></p>]]></content:encoded>
			<wfw:commentRss>http://blog.hoegaerden.be/2010/02/06/memory-dumps-and-crazy-code-samples/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>List All SSIS Packages Deployed On Your Integration Server</title>
		<link>http://blog.hoegaerden.be/2010/01/10/list-all-ssis-packages-deployed-on-your-integration-server/</link>
		<comments>http://blog.hoegaerden.be/2010/01/10/list-all-ssis-packages-deployed-on-your-integration-server/#comments</comments>
		<pubDate>Sun, 10 Jan 2010 21:23:40 +0000</pubDate>
		<dc:creator>Valentino Vranken</dc:creator>
				<category><![CDATA[Integration Services]]></category>
		<category><![CDATA[SQLServerPedia Syndication]]></category>
		<category><![CDATA[T-SQL]]></category>
		<category><![CDATA[data]]></category>
		<category><![CDATA[ETL]]></category>
		<category><![CDATA[Script]]></category>
		<category><![CDATA[SSIS]]></category>

		<guid isPermaLink="false">http://blog.hoegaerden.be/2010/01/10/list-all-ssis-packages-deployed-on-your-integration-server/</guid>
		<description><![CDATA[When deploying packages to SQL Server Integration Services, it&#8217;s advisable to set up a folder structure so that you can easily distinguish packages belonging to different projects.  Furthermore it may be interesting to create subfolders under the main project folder to separate packages according to the different phases in your ETL (Extract, Transform, Load) process.  [...]]]></description>
			<content:encoded><![CDATA[<p>When deploying packages to SQL Server Integration Services, it&#8217;s advisable to set up a folder structure so that you can easily distinguish packages belonging to different projects.  Furthermore it may be interesting to create subfolders under the main project folder to separate packages according to the different phases in your <a title="Wikipedia: Extract, transform, load" href="http://en.wikipedia.org/wiki/Extract,_transform,_load" target="_blank">ETL (Extract, Transform, Load)</a> process.  When loading a data warehouse, interesting folder names are Dimensions for your dimension ETLs and Facts for the packages that load the fact tables.</p>
<p>After a while you end up with lots of packages spread over lots of folders.  To get a good view of what is deployed on your server, it may be interesting to find a way to list all the packages.  And that&#8217;s exactly the reason why I&#8217;m writing this article.</p>
<p>The query further down generates <strong>a list of all packages deployed in the MSDB database</strong> on your SQL Server.  What you get is the name of the packages, their location and version-related information.  I&#8217;ve also created a  RootFolder column so that it&#8217;s easy to filter on project.  (See now why it&#8217;s interesting to create separate folders per project?)</p>
<p>It’s <strong>important to note</strong> that packages deployed to the <strong>File System will not be shown</strong> in the list.  After all, they are not stored in the MSDB database but in a folder somewhere on the server&#8217;s hard drive, more precisely in a subfolder of where you&#8217;ve installed your SQL Server.  In case you&#8217;ve forgotten where that was, here&#8217;s a small tip.  On your server, open up the list of Windows Services (Start &gt; Run &gt; type &#8220;services.msc&#8221; &gt; enter) and locate the service called <em>SQL Server Integration Services 10.0</em>.  Open the properties of that service and have a look at the <em>Path to executable</em> value in the General tab.  Take the path, drop the \Binn part and add \Packages instead.  That is where, by default, the packages are deployed.  (If you’re running SQL Server 2005, apply the same procedure but look for a service called <em>SQL Server Integration Services</em>.)</p>
<p>On my system, this is where the packages are located: D:\Program Files\Microsoft SQL Server\100\DTS\Packages.  I will also prove it with following screenshot:</p>
<p><img style="display: inline; border-width: 0px;" title="Packages deployed to the file system on SSIS 2008" src="http://blog.hoegaerden.be/wp-content/uploads/image140.png" border="0" alt="Packages deployed to the file system on SSIS 2008" width="381" height="82" /></p>
<p>If you&#8217;re looking for a way to list the packages deployed to the file system by using a T-SQL statement, check out the following article by Phil Factor: <a title="The TSQL of Text Files" href="http://www.simple-talk.com/sql/t-sql-programming/the-tsql-of-text-files/" target="_blank">The TSQL of Text Files.</a></p>
<p>Okay, time for the real stuff, the query:</p>
<pre class="code"><span style="color: green;">/*
    DESCRIPTION: Lists all SSIS packages deployed to the MSDB database.
    WRITTEN BY: Valentino Vranken
    VERSION: 1.1
    COPIED FROM: http://blog.hoegaerden.be

    Note: this query was written for SQL Server 2008. For SQL2005:
        o sysssispackagefolders =&gt; sysdtspackagefolders90
        o sysssispackages =&gt; sysdtspackages90
*/
</span><span style="color: blue;">with </span>ChildFolders
<span style="color: blue;">as
</span><span style="color: gray;">(
    </span><span style="color: blue;">select </span>PARENT<span style="color: gray;">.</span>parentfolderid<span style="color: gray;">, </span>PARENT<span style="color: gray;">.</span>folderid<span style="color: gray;">, </span>PARENT<span style="color: gray;">.</span>foldername<span style="color: gray;">,
        </span><span style="color: magenta;">cast</span><span style="color: gray;">(</span><span style="color: red;">'' </span><span style="color: blue;">as sysname</span><span style="color: gray;">) </span><span style="color: blue;">as </span>RootFolder<span style="color: gray;">,
        </span><span style="color: magenta;">cast</span><span style="color: gray;">(</span>PARENT<span style="color: gray;">.</span>foldername <span style="color: blue;">as varchar</span><span style="color: gray;">(</span><span style="color: magenta;">max</span><span style="color: gray;">)) </span><span style="color: blue;">as </span>FullPath<span style="color: gray;">,
        </span>0 <span style="color: blue;">as </span>Lvl
    <span style="color: blue;">from </span>msdb<span style="color: gray;">.</span>dbo<span style="color: gray;">.</span>sysssispackagefolders PARENT
    <span style="color: blue;">where </span>PARENT<span style="color: gray;">.</span>parentfolderid <span style="color: gray;">is null
    </span><span style="color: blue;">UNION </span><span style="color: gray;">ALL
    </span><span style="color: blue;">select </span>CHILD<span style="color: gray;">.</span>parentfolderid<span style="color: gray;">, </span>CHILD<span style="color: gray;">.</span>folderid<span style="color: gray;">, </span>CHILD<span style="color: gray;">.</span>foldername<span style="color: gray;">,
        </span><span style="color: blue;">case </span>ChildFolders<span style="color: gray;">.</span>Lvl
            <span style="color: blue;">when </span>0 <span style="color: blue;">then </span>CHILD<span style="color: gray;">.</span>foldername
            <span style="color: blue;">else </span>ChildFolders<span style="color: gray;">.</span>RootFolder
        <span style="color: blue;">end as </span>RootFolder<span style="color: gray;">,
        </span><span style="color: magenta;">cast</span><span style="color: gray;">(</span>ChildFolders<span style="color: gray;">.</span>FullPath <span style="color: gray;">+ </span><span style="color: red;">'/' </span><span style="color: gray;">+ </span>CHILD<span style="color: gray;">.</span>foldername <span style="color: blue;">as varchar</span><span style="color: gray;">(</span><span style="color: magenta;">max</span><span style="color: gray;">))
            </span><span style="color: blue;">as </span>FullPath<span style="color: gray;">,
        </span>ChildFolders<span style="color: gray;">.</span>Lvl <span style="color: gray;">+ </span>1 <span style="color: blue;">as </span>Lvl
    <span style="color: blue;">from </span>msdb<span style="color: gray;">.</span>dbo<span style="color: gray;">.</span>sysssispackagefolders CHILD
        <span style="color: gray;">inner join </span>ChildFolders <span style="color: blue;">on </span>ChildFolders<span style="color: gray;">.</span>folderid <span style="color: gray;">= </span>CHILD<span style="color: gray;">.</span>parentfolderid
<span style="color: gray;">)
</span><span style="color: blue;">select </span>F<span style="color: gray;">.</span>RootFolder<span style="color: gray;">, </span>F<span style="color: gray;">.</span>FullPath<span style="color: gray;">, </span>P<span style="color: gray;">.</span>name <span style="color: blue;">as </span>PackageName<span style="color: gray;">,
    </span>P<span style="color: gray;">.</span><span style="color: blue;">description as </span>PackageDescription<span style="color: gray;">, </span>P<span style="color: gray;">.</span>packageformat<span style="color: gray;">, </span>P<span style="color: gray;">.</span>packagetype<span style="color: gray;">,
    </span>P<span style="color: gray;">.</span>vermajor<span style="color: gray;">, </span>P<span style="color: gray;">.</span>verminor<span style="color: gray;">, </span>P<span style="color: gray;">.</span>verbuild<span style="color: gray;">, </span>P<span style="color: gray;">.</span>vercomments<span style="color: gray;">,
    </span><span style="color: magenta;">cast</span><span style="color: gray;">(</span><span style="color: magenta;">cast</span><span style="color: gray;">(</span>P<span style="color: gray;">.</span>packagedata <span style="color: blue;">as varbinary</span><span style="color: gray;">(</span><span style="color: magenta;">max</span><span style="color: gray;">)) </span><span style="color: blue;">as xml</span><span style="color: gray;">) </span><span style="color: blue;">as </span>PackageData
<span style="color: blue;">from </span>ChildFolders F
    <span style="color: gray;">inner join </span>msdb<span style="color: gray;">.</span>dbo<span style="color: gray;">.</span>sysssispackages P <span style="color: blue;">on </span>P<span style="color: gray;">.</span>folderid <span style="color: gray;">= </span>F<span style="color: gray;">.</span>folderid
<span style="color: blue;">order by </span>F<span style="color: gray;">.</span>FullPath <span style="color: blue;">asc</span><span style="color: gray;">, </span>P<span style="color: gray;">.</span>name <span style="color: blue;">asc</span><span style="color: gray;">;</span></pre>
<p>The query uses a recursive CTE (<a title="BOL 2008: Using Common Table Expressions" href="http://msdn.microsoft.com/en-us/library/ms190766.aspx">Common Table Expression</a>) to get data out of a system table called <a title="BOL 2008: sysssispackagefolders (Transact-SQL)" href="http://msdn.microsoft.com/en-us/library/ms189477.aspx">sysssispackagefolders</a>, located in the MSDB system database.  The CTE gives us a list of all folders stored in the database and at the same time uses the hierarchical structure of the table to build the <em>FullPath</em> and the <em>Lvl</em> columns.</p>
<p><strong>Note:</strong> the <a title="BOL 2008: CAST and CONVERT (Transact-SQL)" href="http://msdn.microsoft.com/en-us/library/ms187928.aspx">CAST()</a> calls are needed because the data type of the <em>foldername</em> column is <a title="BOL 2008: Using Special Data Types" href="http://msdn.microsoft.com/en-us/library/ms191240.aspx">sysname</a>.  And sysname does not implicitly convert to varchar, which is needed for the concatenation building the <em>FullPath</em> column.</p>
<p>The CTE is joined with another system table called <a title="BOL 2008: sysssispackages (Transact-SQL)" href="http://msdn.microsoft.com/en-us/library/ms181582.aspx">sysssispackages</a>, also located in MSDB.  Not all columns are being retrieved from that table but I believe I&#8217;ve selected the most important ones.  Have a look in the Books Online for more info on the columns available.</p>
<p>There’s one column however on which I’d like to add some additional info myself.  That column is called <em>packagedata</em> and it contains the actual SSIS package.  The data type of this column is <a title="BOL 2008: ntext, text, and image (Transact-SQL)" href="http://msdn.microsoft.com/en-us/library/ms187993.aspx">image</a>, not sure why because after all, an SSIS package (or .dtsx file for that matter) is pure XML.  So why isn’t it stored as XML? <span style="text-decoration: line-through;"> If anyone knows the reason: post a comment!</span></p>
<p><strong>Update:</strong> since I wrote the above paragraph I’ve come across the answer myself.  The reason that the XML is not stored as xml datatype is because of the overhead that this would cause.  So there you go, use image instead of xml if you’re not going to query the xml structure itself.</p>
<p>Anyway, as you can see in the query, to get it converted from image to XML you need to go through varbinary.  The image datatype cannot convert directly to XML.  See the Books Online here on what casts are allowed: <a title="http://msdn.microsoft.com/en-us/library/ms187928.aspx" href="http://msdn.microsoft.com/en-us/library/ms187928.aspx">http://msdn.microsoft.com/en-us/library/ms187928.aspx</a></p>
<p><strong>Note for SQL Server 2005 users:</strong> as mentioned in the query’s comments, these tables don’t exist in SQL Server 2005.  Well, actually they do, they just have different names.  See the comment in the code for their equivalent.</p>
<p>To finish off I&#8217;ll show you what the results look like when executing the query on my test system.  But first, following screenshot shows all deployed packages as reported by the Management Studio.  As you can see, two packages are deployed to the File System.  These two packages were shown earlier in the first screenshot.  Some other packages have been deployed to the MSDB database.</p>
<p><img style="display: inline; border-width: 0px;" title="Object Explorer showing all deployed SSIS packages" src="http://blog.hoegaerden.be/wp-content/uploads/image141.png" border="0" alt="Object Explorer showing all deployed SSIS packages" width="329" height="306" /></p>
<p>And here are the results of the query:</p>
<p><a href="http://blog.hoegaerden.be/wp-content/uploads/image143.png"><img style="display: inline; border-width: 0px;" title="A list of all SSIS packages deployed to my SQL Server 2008 MSDB database" src="http://blog.hoegaerden.be/wp-content/uploads/image_thumb37.png" border="0" alt="A list of all SSIS packages deployed to my SQL Server 2008 MSDB database" width="692" height="84" /></a></p>
<p>To be honest, I added a little filter to keep the results clean.  The <a title="BOL 2008: Introducing the Data Collector" href="http://msdn.microsoft.com/en-us/library/bb677248.aspx">Data Collector</a>, a new feature of SQL Server 2008, also uses some packages so I’ve filtered those out by adding a WHERE clause to the SELECT statement at the bottom of the full query:</p>
<pre class="code"><span style="color: blue;">select </span>F<span style="color: gray;">.</span>RootFolder<span style="color: gray;">, </span>F<span style="color: gray;">.</span>FullPath<span style="color: gray;">, </span>P<span style="color: gray;">.</span>name <span style="color: blue;">as </span>PackageName<span style="color: gray;">,
    </span>P<span style="color: gray;">.</span><span style="color: blue;">description as </span>PackageDescription<span style="color: gray;">, </span>P<span style="color: gray;">.</span>packageformat<span style="color: gray;">, </span>P<span style="color: gray;">.</span>packagetype<span style="color: gray;">,
    </span>P<span style="color: gray;">.</span>vermajor<span style="color: gray;">, </span>P<span style="color: gray;">.</span>verminor<span style="color: gray;">, </span>P<span style="color: gray;">.</span>verbuild<span style="color: gray;">, </span>P<span style="color: gray;">.</span>vercomments<span style="color: gray;">,
    </span><span style="color: magenta;">cast</span><span style="color: gray;">(</span><span style="color: magenta;">cast</span><span style="color: gray;">(</span>P<span style="color: gray;">.</span>packagedata <span style="color: blue;">as varbinary</span><span style="color: gray;">(</span><span style="color: magenta;">max</span><span style="color: gray;">)) </span><span style="color: blue;">as xml</span><span style="color: gray;">) </span><span style="color: blue;">as </span>PackageData
<span style="color: blue;">from </span>ChildFolders F
    <span style="color: gray;">inner join </span>msdb<span style="color: gray;">.</span>dbo<span style="color: gray;">.</span>sysssispackages P <span style="color: blue;">on </span>P<span style="color: gray;">.</span>folderid <span style="color: gray;">= </span>F<span style="color: gray;">.</span>folderid
<span style="color: blue;">where </span>F<span style="color: gray;">.</span>RootFolder <span style="color: gray;">&lt;&gt; </span><span style="color: red;">'Data Collector'
</span><span style="color: blue;">order by </span>F<span style="color: gray;">.</span>FullPath <span style="color: blue;">asc</span><span style="color: gray;">, </span>P<span style="color: gray;">.</span>name <span style="color: blue;">asc</span><span style="color: gray;">;</span></pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>If you’ve been paying attention, you’ve noticed that the two packages deployed to the File System are not mentioned in the output of the query, as expected.</p>
<p>Now that you know how to list your packages, <a title="Recursively Delete SSIS Folder" href="http://blog.hoegaerden.be/2010/02/28/recursively-delete-ssis-folder/" target="_blank">check out my article on deleting them</a>.</p>
<p>That’s all for now folks, have fun!</p>
<p><strong>References</strong></p>
<p><a title="BOL 2008: Tutorial: Creating a Simple ETL Package" href="http://msdn.microsoft.com/en-us/library/ms169917.aspx">BOL 2008: Tutorial: Creating a Simple ETL Package</a></p>
<p><a title="BOL 2008: sysssispackagefolders (Transact-SQL)" href="http://msdn.microsoft.com/en-us/library/ms189477.aspx">BOL 2008: sysssispackagefolders (Transact-SQL)</a></p>
<p><a title="BOL 2008: sysssispackages (Transact-SQL)" href="http://msdn.microsoft.com/en-us/library/ms181582.aspx">BOL 2008: sysssispackages (Transact-SQL)</a></p>
<p><a title="BOL 2008: Recursive Queries Using Common Table Expressions" href="http://msdn.microsoft.com/en-us/library/ms186243.aspx">BOL 2008: Recursive Queries Using Common Table Expressions</a></p>
<p><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fblog.hoegaerden.be%2F2010%2F01%2F10%2Flist-all-ssis-packages-deployed-on-your-integration-server%2F&amp;title=List%20All%20SSIS%20Packages%20Deployed%20On%20Your%20Integration%20Server" id="wpa2a_20"><img src="http://blog.hoegaerden.be/wp-content/plugins/add-to-any/share_save_171_16.png" width="171" height="16" alt="Share"/></a></p>]]></content:encoded>
			<wfw:commentRss>http://blog.hoegaerden.be/2010/01/10/list-all-ssis-packages-deployed-on-your-integration-server/feed/</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
	</channel>
</rss>
