<?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>A Developer&#039;s Blog &#187; data</title>
	<atom:link href="http://blog.hoegaerden.be/tag/data/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.hoegaerden.be</link>
	<description>SQL Server, BI, .NET, IT and anything else I have been playing with.</description>
	<lastBuildDate>Sun, 11 Jul 2010 12:19:25 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Put Some Images On Those SSRS Reports</title>
		<link>http://blog.hoegaerden.be/2010/07/07/put-some-images-on-those-ssrs-reports/</link>
		<comments>http://blog.hoegaerden.be/2010/07/07/put-some-images-on-those-ssrs-reports/#comments</comments>
		<pubDate>Wed, 07 Jul 2010 16:05:21 +0000</pubDate>
		<dc:creator>Valentino Vranken</dc:creator>
				<category><![CDATA[Reporting Services]]></category>
		<category><![CDATA[SQLServerPedia Syndication]]></category>
		<category><![CDATA[data]]></category>
		<category><![CDATA[Reporting Services 2008]]></category>
		<category><![CDATA[SSRS]]></category>
		<category><![CDATA[Tutorial]]></category>

		<guid isPermaLink="false">http://blog.hoegaerden.be/2010/07/07/put-some-images-on-those-ssrs-reports/</guid>
		<description><![CDATA[On the forums I now and then encounter questions regarding images on SSRS reports.&#160; Instead of re-inventing the wheel each time, I decided to write an article about the subject.&#160; So in this article I’ll be discussing and demonstrating several different ways of how images can be put on a report.
I’m using SQL Server Reporting [...]]]></description>
			<content:encoded><![CDATA[<p>On the forums I now and then encounter questions regarding images on SSRS reports.&#160; Instead of re-inventing the wheel each time, I decided to write an article about the subject.&#160; So in this article I’ll be discussing and demonstrating several different ways of how images can be put on a report.</p>
<p>I’m using SQL Server Reporting Services 2008 R2 CTP, more precisely version 10.50.1352.12, but the methods explained here will work on any SSRS 2008.&#160; Furthermore I’m using the AdventureWorks2008R2 database, <a title="AdventureWorks 2008R2 RTM" href="http://msftdbprodsamples.codeplex.com/releases/view/45907" target="_blank">available at CodePlex</a>.</p>
<p>The resulting report, including image files, <a title="SSRS and images download" href="http://cid-81c8b064cbbe1698.office.live.com/self.aspx/.Public/blog/SSRS%5E_and%5E_images.zip" target="_blank">can be downloaded from my Skydrive</a>.</p>
<h2>The Scenario</h2>
<p>The marketing department has requested a product catalogue.&#160; This catalogue should contain all products produced by our two daughter companies: The Canyon Peak and Great Falls Soft.&#160; The catalogue should be grouped on company, with the next company&#8217;s products starting on a new page.</p>
<p>Further requirements are:</p>
<ol>
<ol>
<li>Each page needs an image in its header, with even pages displaying a different image than odd pages. </li>
<li>Each company has a logo.&#160; The logo should be displayed in the company’s header. </li>
<li>Each product has a logo.&#160; The logo should be displayed as part of the product details. </li>
</ol>
</ol>
<p>A design document containing the expected layout, including all image material, has been provided.</p>
<h2>The Data</h2>
<p>The following query provides us with all the data needed to produce the report:</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>
<pre class="code"><span style="color: blue">SELECT </span><span style="color: red">'The Canyon Peak' </span><span style="color: blue">as </span>Company<span style="color: gray">, </span><span style="color: red">'TheCanyonPeak_logo.png' </span>CompanyLogo<span style="color: gray">,
    </span><span style="color: red">'The Canyon Peak company specializes in all kinds of bikes, such as touring and road bikes.' </span>CompanyDescription<span style="color: gray">,
    </span>P<span style="color: gray">.</span>Name <span style="color: blue">as </span>Product<span style="color: gray">, </span>PS<span style="color: gray">.</span>Name <span style="color: blue">as </span>Subcategory<span style="color: gray">, </span>PC<span style="color: gray">.</span>Name <span style="color: blue">as </span>Category<span style="color: gray">,
    </span>PP<span style="color: gray">.</span>LargePhoto<span style="color: gray">, </span>P<span style="color: gray">.</span>ListPrice<span style="color: gray">, </span>P<span style="color: gray">.</span><span style="color: blue">Weight</span><span style="color: gray">, </span>P<span style="color: gray">.</span>Size<span style="color: gray">,
    </span>P<span style="color: gray">.</span>SizeUnitMeasureCode<span style="color: gray">, </span>P<span style="color: gray">.</span>WeightUnitMeasureCode
<span style="color: blue">FROM </span>Production<span style="color: gray">.</span>Product <span style="color: blue">AS </span>P
    <span style="color: gray">INNER JOIN </span>Production<span style="color: gray">.</span>ProductSubcategory <span style="color: blue">AS </span>PS
        <span style="color: blue">ON </span>PS<span style="color: gray">.</span>ProductSubcategoryID <span style="color: gray">= </span>P<span style="color: gray">.</span>ProductSubcategoryID
    <span style="color: gray">INNER JOIN </span>Production<span style="color: gray">.</span>ProductCategory <span style="color: blue">AS </span>PC
        <span style="color: blue">ON </span>PC<span style="color: gray">.</span>ProductCategoryID <span style="color: gray">= </span>PS<span style="color: gray">.</span>ProductCategoryID
    <span style="color: gray">LEFT OUTER JOIN </span>Production<span style="color: gray">.</span>ProductProductPhoto PPP
        <span style="color: blue">ON </span>PPP<span style="color: gray">.</span>ProductID <span style="color: gray">= </span>P<span style="color: gray">.</span>ProductID
    <span style="color: gray">LEFT OUTER JOIN </span>Production<span style="color: gray">.</span>ProductPhoto PP
        <span style="color: blue">ON </span>PPP<span style="color: gray">.</span>ProductPhotoID <span style="color: gray">= </span>PP<span style="color: gray">.</span>ProductPhotoID
<span style="color: blue">WHERE </span>PC<span style="color: gray">.</span>Name <span style="color: gray">= </span><span style="color: red">'Bikes' </span><span style="color: green">--The Canyon Peak sells bikes
    </span><span style="color: gray">and </span>PP<span style="color: gray">.</span>ProductPhotoID <span style="color: gray">&gt; </span>1 <span style="color: green">--I don't want NO IMAGE AVAILABLE
</span><span style="color: blue">UNION </span><span style="color: gray">ALL
</span><span style="color: blue">SELECT </span><span style="color: red">'Great Falls Soft' </span><span style="color: blue">as </span>Company<span style="color: gray">, </span><span style="color: red">'GreatFallsSoft_logo.png' </span>CompanyLogo<span style="color: gray">,
    </span><span style="color: red">'Great Falls Soft uses only the softest tissues available for those sporting clothes.  And on top of that, they''re waterproof.' </span>CompanyDescription<span style="color: gray">,
    </span>P<span style="color: gray">.</span>Name <span style="color: blue">as </span>Product<span style="color: gray">, </span>PS<span style="color: gray">.</span>Name <span style="color: blue">as </span>Subcategory<span style="color: gray">, </span>PC<span style="color: gray">.</span>Name <span style="color: blue">as </span>Category<span style="color: gray">,
    </span>PP<span style="color: gray">.</span>LargePhoto<span style="color: gray">, </span>P<span style="color: gray">.</span>ListPrice<span style="color: gray">, </span>P<span style="color: gray">.</span><span style="color: blue">Weight</span><span style="color: gray">, </span>P<span style="color: gray">.</span>Size<span style="color: gray">,
    </span>P<span style="color: gray">.</span>SizeUnitMeasureCode<span style="color: gray">, </span>P<span style="color: gray">.</span>WeightUnitMeasureCode
<span style="color: blue">FROM </span>Production<span style="color: gray">.</span>Product <span style="color: blue">AS </span>P
    <span style="color: gray">INNER JOIN </span>Production<span style="color: gray">.</span>ProductSubcategory <span style="color: blue">AS </span>PS
        <span style="color: blue">ON </span>PS<span style="color: gray">.</span>ProductSubcategoryID <span style="color: gray">= </span>P<span style="color: gray">.</span>ProductSubcategoryID
    <span style="color: gray">INNER JOIN </span>Production<span style="color: gray">.</span>ProductCategory <span style="color: blue">AS </span>PC
        <span style="color: blue">ON </span>PC<span style="color: gray">.</span>ProductCategoryID <span style="color: gray">= </span>PS<span style="color: gray">.</span>ProductCategoryID
    <span style="color: gray">LEFT OUTER JOIN </span>Production<span style="color: gray">.</span>ProductProductPhoto PPP
        <span style="color: blue">ON </span>PPP<span style="color: gray">.</span>ProductID <span style="color: gray">= </span>P<span style="color: gray">.</span>ProductID
    <span style="color: gray">LEFT OUTER JOIN </span>Production<span style="color: gray">.</span>ProductPhoto PP
        <span style="color: blue">ON </span>PPP<span style="color: gray">.</span>ProductPhotoID <span style="color: gray">= </span>PP<span style="color: gray">.</span>ProductPhotoID
<span style="color: blue">WHERE </span>PC<span style="color: gray">.</span>Name <span style="color: gray">= </span><span style="color: red">'Clothing' </span><span style="color: green">--Great Falls Soft sells clothes, waterstopping soft clothes
    </span><span style="color: gray">and </span>PP<span style="color: gray">.</span>ProductPhotoID <span style="color: gray">&gt; </span>1 <span style="color: green">--I don't want NO IMAGE AVAILABLE
</span><span style="color: blue">ORDER BY </span>Category <span style="color: blue">asc</span><span style="color: gray">, </span>Subcategory <span style="color: blue">asc</span><span style="color: gray">, </span>Product <span style="color: blue">asc</span><span style="color: gray">;</span></pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>I’m not going into the details of this query.&#160; Let’s just say that I’m manipulating data from the database in combination with some hardcoded data to get usable data for our example.&#160; I’ve added some comments to make it clear what the query is doing.&#160; If you have a look at its output, you’ll see that it produces a list of products with some additional fields.</p>
<p><a href="http://blog.hoegaerden.be/wp-content/uploads/image303.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="Results of the query" border="0" alt="Results of the query" src="http://blog.hoegaerden.be/wp-content/uploads/image_thumb71.png" width="700" height="72" /></a> </p>
<h2>Different Ways Of Adding Images</h2>
<p>To get started, open up a SSRS solution, add a new report, add a data source connecting to your AdventureWorks 2008 R2 DB, and add a dataset using the above query.</p>
<h3>Embedding Images In Your Report</h3>
<p>The first way of adding images to a report that we’ll take a look at is by embedding them inside the report.&#160; Looking at the scenario requirements described earlier, this is requirement 1.</p>
<p>Let’s add a header to the report.&#160; In the BIDS menu, select <strong>Report</strong> &gt; <strong>Add Page Header</strong>.</p>
<p><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="Adding a header to a report" border="0" alt="Adding a header to a report" src="http://blog.hoegaerden.be/wp-content/uploads/image304.png" width="265" height="178" /> </p>
<p>If you don’t see the Report menu item, you probably have not selected your report.&#160; Click your report in the Design view to select it.</p>
<p>From the Toolbox, drag the Image report item onto the header portion of the report.&#160; Doing that will show a pop-up window, the Image Properties.&#160; By default, the <strong>Select the image source </strong>combobox is set to <em>Embedded</em>.&#160; Good, that’s what we need at this point.&#160; What we now need to do is import an image into the report, using the <strong>Import</strong> button.</p>
<p>Clicking the Import button shows a common file Open dialog.&#160; Our marketing department has given me two images for use in the header: Cloudy_banner.png and AnotherCloudy_banner.png.&#160; Let’s select the first one.</p>
<p><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="Adding an image to a report by using the Import button on the Image Properties window" border="0" alt="Adding an image to a report by using the Import button on the Image Properties window" src="http://blog.hoegaerden.be/wp-content/uploads/image305.png" width="625" height="434" /> </p>
<p>If you don’t see any images, have a look at that filter dropdown as highlighted in the screenshot above.&#160; By default this is set to JPEG files.</p>
<p>Here’s the result in the Image Properties:</p>
<p><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="Image Properties with an image selected" border="0" alt="Image Properties with an image selected" src="http://blog.hoegaerden.be/wp-content/uploads/image306.png" width="585" height="532" /> </p>
<p>On the Size page, select <em>Clip</em> instead of <em>Fit proportional</em>.&#160; This is a setting that you’ll need to look at case per case.&#160; For our header images, <em>Clip</em> is the most suitable option.</p>
<p><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="Image Properties: set Display to Clip" border="0" alt="Image Properties: set Display to Clip" src="http://blog.hoegaerden.be/wp-content/uploads/image307.png" width="585" height="532" /> </p>
<p>Close the Image Properties window and enlarge the image placeholder so that it occupies the whole header area:</p>
<p><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="Image added to report header" border="0" alt="Image added to report header" src="http://blog.hoegaerden.be/wp-content/uploads/image308.png" width="544" height="180" /> </p>
<p>As you can see, we now have an image in the header.&#160; But we haven’t fully implemented the requirement yet.&#160; The even pages should display a different image than the uneven ones.</p>
<p>To be able to do that, we’ll first add the second banner image to the report.&#160; In the Report Data pane, locate the <em>Images</em> node and open it up.&#160; You’ll notice that the image that we inserted earlier can be found here.</p>
<p><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="The Images node in the Report Data pane shows all embedded images" border="0" alt="The Images node in the Report Data pane shows all embedded images" src="http://blog.hoegaerden.be/wp-content/uploads/image309.png" width="168" height="159" /> </p>
<p>Right-click the <em>Images</em> node and select <strong>Add Image</strong>.</p>
<p><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="Right-click Images node to add an embedded image to the report" border="0" alt="Right-click Images node to add an embedded image to the report" src="http://blog.hoegaerden.be/wp-content/uploads/image310.png" width="229" height="155" /> </p>
<p>That opens up the familiar file Open dialog which was used to add the first image.&#160; So I’m now selecting the file called AnotherCloudy_banner.png, after changing the default filter to PNG.&#160; After clicking OK, the image gets added under the Images node.</p>
<p><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="Second banner image added to the report" border="0" alt="Second banner image added to the report" src="http://blog.hoegaerden.be/wp-content/uploads/image311.png" width="190" height="64" />&#160; </p>
<p>With the second image added, all that remains to be done is tell the header that it should pick different images depending on the page number.</p>
<p>Right-click the image in the header and select <strong>Image Properties</strong>.&#160; On the General page, when you click the dropdown of the setting called Use this image, you’ll notice that there are two values now.&#160; These are the same values as displayed in the Report Data pane.&#160; And these are the values to be used in the expression that we’ll create to rotate the images depending on page number.</p>
<p>Click the fx button next to the dropdown and enter the following expression:</p>
<pre class="code">=IIF(Globals!PageNumber <span style="color: blue">Mod </span>2 = 0, <span style="color: #a31515">&quot;Cloudy_banner&quot;</span>, <span style="color: #a31515">&quot;AnotherCloudy_banner&quot;</span>)</pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>This is a fairly simple expression, using the Mod operator and the IIF() function.&#160; When page number can be divided by two, which means it’s an even page number, Cloudy_banner is displayed.&#160; Otherwise the other banner is displayed.</p>
<p>That’s it, the report header is finished.&#160; When you have a look at the report in Preview, it should now show the second banner on the first page – this is an uneven page.</p>
<p>To conclude this chapter I’d like to mention that this method is usually not the preferred one.&#160; A <strong>disadvantage</strong> here is that the images are stored inside the report RDL and thus cannot be modified without altering the report itself.</p>
<p>Here’s the evidence:</p>
<pre class="code"> <span style="color: blue">&lt;</span><span style="color: #a31515">EmbeddedImages</span><span style="color: blue">&gt;
    &lt;</span><span style="color: #a31515">EmbeddedImage </span><span style="color: red">Name</span><span style="color: blue">=</span>&quot;<span style="color: blue">Cloudy_banner</span>&quot;<span style="color: blue">&gt;
      &lt;</span><span style="color: #a31515">MIMEType</span><span style="color: blue">&gt;</span>image/png<span style="color: blue">&lt;/</span><span style="color: #a31515">MIMEType</span><span style="color: blue">&gt;
      &lt;</span><span style="color: #a31515">ImageData</span><span style="color: blue">&gt;</span>iVBORw0KGgoAAAANSUhEUgAABVsAAABaCAIAAA...</pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>To have a look at the RDL yourself, just right-click the report in the <strong>Solution Explorer</strong> and select <em>View Code</em>.</p>
<p>On to requirement number two!</p>
<h3>Displaying Images Through A URL</h3>
<p>At the moment, the report body is still empty, so drag a Table onto it.&#160; Put the Table in the upper-left corner, remove one of the columns so that two remain, remove the Header row and make it a bit wider.</p>
<p>Now set the <strong>DataSetName</strong> property of the Tablix to the name of your dataset, in my case that’s <em>dsProducts</em>.</p>
<p>The report should display the data grouped on company, so right-click on the line that says <em>Details</em> in the Row Groups window part at the bottom of the Design View.&#160; Select <strong>Add Group &gt; Parent Group</strong>.</p>
<p><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="Right-click the Details line in Row Groups to add a new parent group" border="0" alt="Right-click the Details line in Row Groups to add a new parent group" src="http://blog.hoegaerden.be/wp-content/uploads/image312.png" width="646" height="160" /> </p>
<p>Group by Company and add a group header:</p>
<p><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="Tablix grouping" border="0" alt="Tablix grouping" src="http://blog.hoegaerden.be/wp-content/uploads/image313.png" width="451" height="201" /> </p>
<p>Remove the extra first column that just got generated:</p>
<p><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="Remove unwanted column" border="0" alt="Remove unwanted column" src="http://blog.hoegaerden.be/wp-content/uploads/image314.png" width="646" height="189" /> </p>
<p>We’ve now got an empty tablix with two columns, a Details row and a Company header row.&#160; In our dataset, one of the fields is called <em>CompanyDescription</em>.&#160; Hover the mouse pointer above the textbox in the top-right, click the small icon that appears and choose the field from the dropdown that appears when you click the icon.</p>
<p><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="Click the small icon to get a list of fields" border="0" alt="Click the small icon to get a list of fields" src="http://blog.hoegaerden.be/wp-content/uploads/image315.png" width="299" height="64" /> </p>
<p>To add the company’s logo, drag an Image from the Toolbox pane into the textbox on the left of the company description.&#160; Doing this opens up the by now familiar Image Properties dialog.</p>
<p>Give it a good name, such as CompanyLogo, and select <em>External</em> as image source.</p>
<p>Click the fx button next to the <strong>Use this image</strong> box and enter an expression such as this one:</p>
<pre class="code">=<span style="color: #a31515">&quot;file:C:\vavr\test\&quot; </span>+ Fields!CompanyLogo.Value</pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>When using External as image source, the image expression should result in a valid URL, any valid URL.&#160; In my example the files are located in a local folder called c:\vavr\test.&#160; Keep in mind that, when you deploy the report to a server, the images should by located in that same folder, this time located on the server.</p>
<p><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="The Image Properties configured to display an External image" border="0" alt="The Image Properties configured to display an External image" src="http://blog.hoegaerden.be/wp-content/uploads/image316.png" width="417" height="200" /> </p>
<p>By default the image gets displayed using the <em>Fit Proportional </em>setting.&#160; You can verify this in the <strong>Size</strong> page of the Image Properties.&#160; We want the image to get fully displayed while maintaining the aspect ratio, so leave the setting as it is.&#160; Close the image properties dialog.</p>
<p>Vertically enlarge the first row in our tablix to an acceptable size.&#160; In my case the marketing department specified to use a height of 1.5 inches for the company logo.&#160; With the image selected, locate the Size &gt; Height property and set it to “1,5in”.&#160; Note that the decimal separator used here depends on your local settings.</p>
<p>Now have a look at the report in Preview:</p>
<p><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="The report with company logos added" border="0" alt="The report with company logos added" src="http://blog.hoegaerden.be/wp-content/uploads/image317.png" width="664" height="328" /> </p>
<p>Note that I’ve removed the borders of all textboxes by setting their <strong>BorderStyle </strong>property to <em>None</em>.</p>
<p>With the logo images implemented we have fulfilled requirement two.&#160; On to number three.</p>
<h3>Retrieving Images From The Database</h3>
<p>In this last requirement we’ll have a look at displaying images that are retrieved from the database, also known as data-bound images.</p>
<p>The retrieving part is actually already implemented.&#160; In our dataset there’s a field called <strong>LargePhoto</strong>, that one contains a picture of the product.</p>
<p>Let’s add some product details and a picture in that remaining blank row.&#160; To get full control over layout I want to make the detail part of the tablix a freestyle part.&#160; First merge the two cells together by selecting both of them, then right-click and choose <strong>Merge Cells</strong>.</p>
<p><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="Merging two cells together in a tablix" border="0" alt="Merging two cells together in a tablix" src="http://blog.hoegaerden.be/wp-content/uploads/image318.png" width="686" height="632" /> </p>
<p>Now select a <strong>Rectangle</strong> in the Toolbox pane and drop it into the merged area.&#160; To add fields such as Subcategory and Product you can just select them from the <strong>Report Data </strong>pane and drop them inside the rectangle.&#160; I’m also adding some additional labels and fields, as shown in the next screenshot.</p>
<p><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="The product details in Design view" border="0" alt="The product details in Design view" src="http://blog.hoegaerden.be/wp-content/uploads/image319.png" width="347" height="196" /> </p>
<p>As you can see I’ve modified the fonts a bit.&#160; The rendered version:</p>
<p><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="The rendered product details" border="0" alt="The rendered product details" src="http://blog.hoegaerden.be/wp-content/uploads/image320.png" width="306" height="187" /> </p>
<p>This is the expression used for displaying the weight:</p>
<pre class="code">=IIF(
    IsNothing(Fields!Weight.Value),
    <span style="color: #a31515">&quot;unknown&quot;</span>,
    Fields!Weight.Value &amp; <span style="color: #a31515">&quot; &quot; </span>&amp; Fields!WeightUnitMeasureCode.Value
)</pre>
<p><a href="http://11011.net/software/vspaste"></a>And here’s the expression for the size field:</p>
<pre class="code">=Fields!Size.Value &amp; <span style="color: #a31515">&quot; &quot; </span>&amp; Fields!SizeUnitMeasureCode.Value</pre>
<p><a href="http://11011.net/software/vspaste"></a>For the layout the price field I’ve just entered C in the <strong>Format</strong> property of the textbox.</p>
<p>With the textual product details completed, all that remains to be done is adding the product image.</p>
<p>From the Toolbox pane, drag an <strong>Image</strong> into the remaining whitespace in the rectangle, next to the product details.&#160; (You did keep some space available, right?)</p>
<p>Again we get the familiar Image Properties popup.&#160; Give it a good name, like ProductImage, and select the image source that we haven’t used yet, <em>Database</em>.&#160; In the <strong>Use this field </strong>dropdown, select <em>LargePhoto</em>, and select <em>image/gif</em> as <strong>MIME type</strong>.</p>
<p><strong>Note:</strong> the images are stored as GIF.&#160; You can verify this by running a select on the Production.ProductPhoto table.&#160; Looking at the LargePhotoFileName field we see that the extension is .gif.</p>
<p>There one textbox on the General page that’s still blank.&#160; That one is called Tooltip.&#160; Click the fx button next to it and enter following formula:</p>
<pre class="code">=Fields!Product.Value</pre>
<p><a href="http://11011.net/software/vspaste"></a>Click sufficient OK buttons until the properties dialog is gone, then resize the image placeholder so that it occupies the remaining whitespace.</p>
<p>Here’s what the result looks like in preview:</p>
<p><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="The final report, with a tooltip on the product image" border="0" alt="The final report, with a tooltip on the product image" src="http://blog.hoegaerden.be/wp-content/uploads/image321.png" width="656" height="344" /> </p>
<p>When hovering the mouse pointer above the product image, you’ll get a nice tooltip.</p>
<h2>Conclusion</h2>
<p>In this article I have illustrated the three possible methods of adding an image to your Reporting Services report.</p>
<p>Have fun!</p>
<p>Valentino.</p>
<p><strong>References</strong></p>
<p><a title="Adding Images to a Report" href="http://msdn.microsoft.com/en-us/library/ms156482.aspx" target="_blank">BOL: Adding Images to a Report</a></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.hoegaerden.be/2010/07/07/put-some-images-on-those-ssrs-reports/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Getting Cooperation From A Stubborn Data Dude</title>
		<link>http://blog.hoegaerden.be/2010/06/09/getting-cooperation-from-a-stubborn-data-dude/</link>
		<comments>http://blog.hoegaerden.be/2010/06/09/getting-cooperation-from-a-stubborn-data-dude/#comments</comments>
		<pubDate>Wed, 09 Jun 2010 19:59:49 +0000</pubDate>
		<dc:creator>Valentino Vranken</dc:creator>
				<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[Visual Studio]]></category>
		<category><![CDATA[data]]></category>
		<category><![CDATA[Visual Studio 2008]]></category>

		<guid isPermaLink="false">http://blog.hoegaerden.be/2010/06/09/getting-cooperation-from-a-stubborn-data-dude/</guid>
		<description><![CDATA[Lately I’ve been using Visual Studio Team System 2008 Database Edition with the GDR R2 add-on, also known as “The Data Dude”, to compare databases with each other.
And during a schema compare I noticed that the dude can be quite stubborn now and then.
Here’s an example and an explanation of why, as far as I [...]]]></description>
			<content:encoded><![CDATA[<p>Lately I’ve been using <a title="Download: Microsoft® Visual Studio Team System 2008 Database Edition GDR R2" href="http://www.microsoft.com/downloads/details.aspx?FamilyID=bb3ad767-5f69-4db9-b1c9-8f55759846ed&amp;displaylang=en" target="_blank">Visual Studio Team System 2008 Database Edition with the GDR R2 add-on</a>, also known as “The Data Dude”, to compare databases with each other.</p>
<p>And during a schema compare I noticed that the dude can be quite stubborn now and then.</p>
<p>Here’s an example and an explanation of why, as far as I can tell, the dude is behaving that way.</p>
<h2>The Scenario</h2>
<p>At work we use the data dude’s schema compare to prepare our deployment scripts.  The most fun can be had when preparing a script to deploy the latest finalized version, <strong>version x</strong>, from the Development (DEV) to the Acceptance (UAT) environment.</p>
<p>In our DEV database there have already been changes for the next version, <strong>version x+1</strong>, so these changes need to be skipped.</p>
<p>Now imagine the following situation:</p>
<ul>
<li>a table called <em>TableA</em> exists</li>
<li>a new field called <em>NewFieldA</em> was added to <em>TableA</em></li>
<li>a view called <em>ViewA</em> exists and was modified – it uses <em>TableA</em> but not <em>NewFieldA</em></li>
</ul>
<p>The changes to <em>ViewA</em> belong with <strong>version x</strong>, while the changes to <em>TableA</em> are part of <strong>version x+1</strong>.  So the script that I’m going to generate should contain an ALTER VIEW ViewA … but not an ALTER TABLE TableA ….</p>
<h2>The Behavior</h2>
<p>Using the dude, I open up the dropdown on the line that states <em>Different definition </em>on <em>TableA</em> and I select <em>Skip</em>.  The value gets changed from <em>Update</em> to <em>Skip</em>.  However, when the focus changes to another line in the grid the value changes back to <em>Update</em>!  Furthermore, it’s now grayed out, impossible to change!</p>
<p>The first time that you see this happening you can’t believe your eyes and try it once more, first closing everything and then re-doing your actions.  Just to see if you didn’t select the wrong option somewhere.  But alas, the dude remains stubborn!</p>
<p><strong>Tip:</strong> if you have just executed the compare and you find that some update actions cannot be modified, try to do a refresh (right-click on the background of the comparison window and click Refresh).  I noticed that after the refresh you will get the dropdown for these lines and thus will be able to change the Update Action.</p>
<h2>The Explanation</h2>
<p>It took me a while to realize, but here’s what seems to be happening.  Like with all things stubborn, it helps if you know what the thing in question is actually being stubborn for.</p>
<p>The dude knows that <em>ViewA</em> depends on <em>TableA</em> and it also knows that both <em>ViewA</em> and <em>TableA</em> where changed.  However, the dude is a little short-sighted, it doesn’t know that <em>NewFieldA</em> is not used in <em>ViewA</em>.  It doesn’t know that the change to <em>ViewA</em> was not related to the change on <em>TableA</em>.  So it automatically assumes that you’re making a mistake and prevents you from messing up.  It thinks, “You stupid user, I know it better, I am smart software!  And that’s not all, I AM THE DATA DUDE! And if there’s one thing you don’t want, that’s to mess with the Data Dude!”.</p>
<p>Here’s a screenshot of the dude’s stubbornness:</p>
<p><img style="display: inline; border-width: 0px;" title="Grayed-out Update Action: the result of a stubborn data dude" src="http://blog.hoegaerden.be/wp-content/uploads/image291.png" border="0" alt="Grayed-out Update Action: the result of a stubborn data dude" width="105" height="182" /></p>
<p>In this particular case it refused to skip certain new tables, even though there were no dependent objects that needed those tables!  The first two lines show the grayed-out items.  These tables were located under a new schema and I had to put the schema’s <strong>Update Action</strong> to <em>Skip</em> to get the tables be skipped as well.  The Update Action for the tables was changed automatically when I modified the action for the schema.</p>
<p>But, if you ask me, I don’t see why I shouldn’t be able to <em>Skip</em> a new table even when I am going to create the new schema to which it belongs?  It’s the table that depends on the schema, not the other way around.</p>
<h2>The Feature Request</h2>
<p>It would have been great if I could manually override the dude’s decision.  As that was not the case, I have to manually change the generated script so that it only contains the changes that I want.  As long as it’s technically feasible, I think manual changes to the <strong>Update Action</strong> should be allowed.</p>
<h2>One More Tip</h2>
<p>Take a good look at the following screenshot.</p>
<p><a href="http://blog.hoegaerden.be/wp-content/uploads/image292.png"><img style="display: inline; border: 0px;" title="Beware the miniature exclamation mark in the icon's corner!" src="http://blog.hoegaerden.be/wp-content/uploads/image_thumb70.png" border="0" alt="Beware the miniature exclamation mark in the icon's corner!" width="700" height="328" /></a></p>
<p>By default, the node that reads <em>Different dependencies</em> was collapsed.  So you think, good, the line says <em>Skip</em> so nothing will happen.  Except, did you notice that minuscule exclamation mark in the bottom-left corner of the icon?  That means that other actions may be happening further down the dependency tree, as illustrated in the screenshot.</p>
<p>So don’t let the dude mislead you into thinking nothing will happen when it says <em>Skip</em>.  Sometimes the actual actions are hidden further down and you need to open up every node with the exclamation mark on the icon to verify if that’s what you want.</p>
<p>Have fun training the dude,</p>
<p>Valentino.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.hoegaerden.be/2010/06/09/getting-cooperation-from-a-stubborn-data-dude/feed/</wfw:commentRss>
		<slash:comments>1</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 a view called [...]]]></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>
]]></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>Implementing Data Bars In A Grouped Table</title>
		<link>http://blog.hoegaerden.be/2010/05/27/implementing-data-bars-in-a-grouped-table/</link>
		<comments>http://blog.hoegaerden.be/2010/05/27/implementing-data-bars-in-a-grouped-table/#comments</comments>
		<pubDate>Thu, 27 May 2010 20:16:21 +0000</pubDate>
		<dc:creator>Valentino Vranken</dc:creator>
				<category><![CDATA[Reporting Services]]></category>
		<category><![CDATA[SQLServerPedia Syndication]]></category>
		<category><![CDATA[data]]></category>
		<category><![CDATA[Reporting Services 2008 R2]]></category>
		<category><![CDATA[SQL Server 2008 R2]]></category>
		<category><![CDATA[SSRS]]></category>
		<category><![CDATA[Tutorial]]></category>

		<guid isPermaLink="false">http://blog.hoegaerden.be/2010/05/27/implementing-data-bars-in-a-grouped-table/</guid>
		<description><![CDATA[Earlier I wrote an article about the new lookup functions that ship with SQL Server 2008 R2.  Today I’m going to show you another new feature of SSRS 2008 R2, this time in the visualization department.  This feature is the Data Bar.  With this new component it’s fairly easy to make your tabular data a [...]]]></description>
			<content:encoded><![CDATA[<p>Earlier I wrote an article about <a title="Looking Up Data On Different Sources" href="http://blog.hoegaerden.be/2010/05/16/looking-up-data-on-different-sources/">the new lookup functions</a> that ship with SQL Server 2008 R2.  Today I’m going to show you another new feature of SSRS 2008 R2, this time in the visualization department.  This feature is the <a title="BOL 2008R2: Sparklines and Data Bars" href="http://msdn.microsoft.com/en-us/library/ee636365%28v=SQL.105%29.aspx" target="_blank">Data Bar</a>.  With this new component it’s fairly easy to make your tabular data a lot more visual, and thus easier to interpret.  And here’s how to do it.</p>
<p>I’ll be starting from the report that I created in <a title="Your First OLAP Report" href="http://blog.hoegaerden.be/2010/01/24/your-first-olap-report/">Your First OLAP Report</a>.  That allows me to focus on the visualization part, without first needing to build a table report.  (Okay, I admit, it’s not 100% the same report – I’ve modified the colors a bit because I felt the green was too dark.) But obviously this method will work with any report that’s showing data in a table.</p>
<p>Furthermore I’m running SQL Server 2008 R2 Nov CTP, 64-bit, and I’m using the BIDS to develop the report.</p>
<p>The final result <a title="Download: &quot;First OLAP Report With Data Bars&quot;" href="http://cid-81c8b064cbbe1698.skydrive.live.com/self.aspx/.Public/blog/FirstOLAPReportWithDataBars.rdl" target="_blank">can be downloaded from Skydrive here</a>.</p>
<h2>Implementing The Data Bar</h2>
<p>The report that we’re using is showing some sales figures grouped in three levels: Country, State/Province and City.  We’re going to add an extra column on the right of the table to contain the data bar.  Let’s first explore the Toolbox pane to discover the new visualization report items.</p>
<p><img style="display: inline; border-width: 0px;" title="The new Reporting Services 2008 R2 report items: Map, Data Bar, Sparkline and Indicator" src="http://blog.hoegaerden.be/wp-content/uploads/image262.png" border="0" alt="The new Reporting Services 2008 R2 report items: Map, Data Bar, Sparkline and Indicator" width="120" height="342" /></p>
<p>The new items have been highlighted in yellow.  As you can see, besides <strong>Data Bar </strong>there’s also <strong>Sparkline, </strong><strong>Map </strong>and <strong>Indicator</strong>.  But those are not on topic now.</p>
<p>To add a Data Bar, simply drag it from the Toolbox into a textbox on the report.  Doing that will show the following popup window:</p>
<p><img style="display: inline; border-width: 0px;" title="Select Data Bar Type window" src="http://blog.hoegaerden.be/wp-content/uploads/image263.png" border="0" alt="Select Data Bar Type window" width="348" height="482" /></p>
<p>From left to right, there’s Bar, Stacked Bar and 100% Stacked Bar.  And those are also available in vertical direction, Column.  I’m going to use the regular Bar as highlighted in the screenshot.</p>
<p>The Data Bar has now been added to the report, but it doesn’t do anything yet.  We first need to tell it what data to visualize.  Clicking it once will select it, clicking it once more will show us the following <strong>Chart Data </strong>popup:</p>
<p><img style="display: inline; border-width: 0px;" title="Data Bar: Chart Data" src="http://blog.hoegaerden.be/wp-content/uploads/image264.png" border="0" alt="Data Bar: Chart Data" width="612" height="434" /></p>
<p>Click the plus icon to get a drop-down of fields in the dataset.  Select the numeric field that you want to visualize, in my case that’s the Reseller_Sales_Amount.</p>
<p><a href="http://blog.hoegaerden.be/wp-content/uploads/image265.png"><img style="display: inline; border-width: 0px;" title="Data Bar: Chart Data with Reseller_Sales_Amount selected=" border="0" alt="Data Bar: Chart Data with Reseller_Sales_Amount selected=" height="366" /></a></p>
<p>By default, the aggregation used on the data is Sum.  But there are other options as well, have a look at the dropdown next to the [Sum(Reseller_Sales…  In the example here I’m going to keep the Sum.</p>
<p><img style="display: inline; border-width: 0px;" title="Other aggregation functions of the Data Bar" src="http://blog.hoegaerden.be/wp-content/uploads/image266.png" border="0" alt="Other aggregation functions of the Data Bar" width="437" height="450" /></p>
<p>The report Preview looks like this:</p>
<p><a href="http://blog.hoegaerden.be/wp-content/uploads/image267.png"><img style="display: inline; border: 0px;" title="Report preview with the data bar" src="http://blog.hoegaerden.be/wp-content/uploads/image_thumb63.png" border="0" alt="Report preview with the data bar" width="700" height="208" /></a></p>
<p>In this report we can easily see what states have got a higher sales amount: those with the longer bars.</p>
<p>Let’s add bars for the Country level as well.  There are two ways to achieve that: you can either drag a new Data Bar onto the report or you can just copy/paste the textbox containing our first Data Bar.</p>
<p>To make the report easier to read I will change the color of the Data Bar to the color of the group’s background.  Setting up the color of the Data Bar is done as follows: select the Data Bar so that you get the Chart Data popup.  In the Properties pane you should see that the Chart Series is selected.  Then right-click on the bar and select <strong>Series Properties</strong>.</p>
<p><img style="display: inline; border-width: 0px;" title="The right-click menu on the Data Bar." src="http://blog.hoegaerden.be/wp-content/uploads/image268.png" border="0" alt="The right-click menu on the Data Bar." width="434" height="445" /></p>
<p>In the <strong>Series Properties</strong> window, select the <strong>Fill</strong> page and select your favorite color.  If you want you can also use a gradient fill or pattern.  You can even use a Switch statement and color them differently depending on their value, similar to the method that I used in my <a title="SSRS and MDX: Detecting Missing Fields" href="http://blog.hoegaerden.be/2009/07/06/ssrs-and-mdx-detecting-missing-fields/">SSRS and MDX: Detecting Missing Fields</a> article.  Well, in short, any expression that you can think of and results in a color will work fine!</p>
<p><img style="display: inline; border-width: 0px;" title="Series Properies: setting up the Fill color" src="http://blog.hoegaerden.be/wp-content/uploads/image269.png" border="0" alt="Series Properies: setting up the Fill color" width="585" height="520" /></p>
<p>With the second data bar and custom coloring set up, here’s the rendered report:</p>
<p><a href="http://blog.hoegaerden.be/wp-content/uploads/image270.png"><img style="display: inline; border: 0px;" title="Report with data bar on two grouping levels" src="http://blog.hoegaerden.be/wp-content/uploads/image_thumb64.png" border="0" alt="Report with data bar on two grouping levels" width="700" height="237" /></a></p>
<p>Hang on, is that correct? As you can see, the data bar for California is longer than the one for Canada while the sales amount for Canada is definitely the higher one.  An even nicer example is United Kingdom with only one state England.  Both amounts are equal yet their data bars are certainly not.</p>
<p>Well, this is because the data bar by default uses the same scope as the group where it’s put.  Canada and United Kingdom are in the Country group and all Country data bars compare nicely to each other.  California and England are in the State/Province group and also compare nicely to each other!</p>
<p>Depending on the report’s requirements this may or may not be the desired effect.  But I wouldn’t be mentioning this if there weren’t any other options, would I?</p>
<h3>Setting The Maximum Value Of The Horizontal Axis</h3>
<p>A correct column label for our current column would be <em>“% of group</em>”.  Let’s add a second column which will show the percentage of the row compared to the total of the dataset, “<em>% of overall total</em>”.  After adding the extra column, copy/paste the data bars from the first column over into the new textboxes in the second column.</p>
<p>To get what we want, we need to tell the data bar that the maximum for the horizontal axis is the total of the dataset and not the total of the grouping level of the table.  So, right-click one of the data bars in the new column and select <strong>Horizontal Axis Properties</strong>.</p>
<p><img style="display: inline; border-width: 0px;" title="Right-click menu on tablix textbox with a data bar" src="http://blog.hoegaerden.be/wp-content/uploads/image271.png" border="0" alt="Right-click menu on tablix textbox with a data bar" width="545" height="568" /></p>
<p>As you can see, the default for the <em>Maximum</em> value is set to “<em>Auto</em>”.  Note that the name of my tablix is “Tablix1”, as shown in the <em>Align axes in</em> dropdown.</p>
<p><img style="display: inline; border-width: 0px;" title="Horizontal Axis Properties" src="http://blog.hoegaerden.be/wp-content/uploads/image272.png" border="0" alt="Horizontal Axis Properties" width="585" height="530" /></p>
<p>Let’s replace the <em>Auto</em> as <strong>Maximum</strong> with the following expression:</p>
<pre class="code">=Sum(Fields!Reseller_Sales_Amount.Value, <span style="color: #a31515;">"Tablix1"</span>)</pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>This expression says that we need the sum of the Reseller_Sales_Amount, scoped over the whole tablix.</p>
<p>Having changed the <strong>Maximum</strong> value on both data bars gives us the following preview:</p>
<p><a href="http://blog.hoegaerden.be/wp-content/uploads/image273.png"><img style="display: inline; border-width: 0px;" title="Report rendered with status bar scoped over whole dataset" src="http://blog.hoegaerden.be/wp-content/uploads/image_thumb65.png" border="0" alt="Report rendered with status bar scoped over whole dataset" width="700" height="203" /></a></p>
<p>As you can see, this time around the data bars for England and United Kingdom have gotten the same size.  Bars from different grouping levels can now be compared with each other.</p>
<h3>Showing Labels On The Data Bars</h3>
<p>In this report it would be interesting to add a label that displays the percentage to the status bars, so let’s do that.</p>
<p>For the percentage calculation of the first column of data bars we need to get the total of the current group and divide that by the total of the group one level higher.  For the State/Province level that gives us the following expression:</p>
<pre class="code">=Sum(Fields!Reseller_Sales_Amount.Value)
    / Sum(Fields!Reseller_Sales_Amount.Value, <span style="color: #a31515;">"grpCountry"</span>)</pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>The grpCountry refers to the name of the grouping one level higher, as shown in following screenshot:</p>
<p><img style="display: inline; border-width: 0px;" title="The groupings as defined on my example tablix" src="http://blog.hoegaerden.be/wp-content/uploads/image274.png" border="0" alt="The groupings as defined on my example tablix" width="680" height="103" /></p>
<p>And for the Country level we need this expression:</p>
<pre class="code">=Sum(Fields!Reseller_Sales_Amount.Value)
    / Sum(Fields!Reseller_Sales_Amount.Value, <span style="color: #a31515;">"Tablix1"</span>)</pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>The only difference with the previous expression is the scope.</p>
<p>“Ok, so where do I type those expressions?” I hear you think.  Right-click the Data Bar (after first selecting the textbox that contains it) and choose <strong>Show Data Labels</strong>.</p>
<p><img style="display: inline; border-width: 0px;" title="Show Data Labels in right-click popup on Chart Series" src="http://blog.hoegaerden.be/wp-content/uploads/image275.png" border="0" alt="Show Data Labels in right-click popup on Chart Series" width="309" height="300" /></p>
<p>That adds a label to the chart but we still need to configure it to show the percentage.  By default it uses the value as label.  This can be verified in the <strong>Chart Series </strong>properties:</p>
<p><img style="display: inline; border-width: 0px;" title="Chart Series properties: UseValueAsLabel" src="http://blog.hoegaerden.be/wp-content/uploads/image276.png" border="0" alt="Chart Series properties: UseValueAsLabel" width="287" height="392" /></p>
<p>The <strong>UseValueAsLabel</strong> is set to <em>True</em>, confirming what I just stated.  Furthermore, activating the <strong>Show Data Labels </strong>option also set the <strong>Visible</strong> property to <em>True</em>.  So another way of adding the label is by just setting this property to <em>True</em>.</p>
<p>Let’s now configure that label.  Right-click it and select <strong>Series Label Properties</strong>.</p>
<p><img style="display: inline; border-width: 0px;" title="Right-click menu on label" src="http://blog.hoegaerden.be/wp-content/uploads/image277.png" border="0" alt="Right-click menu on label" width="275" height="179" /></p>
<p>The General page of the <strong>Series Label Properties </strong>window allows you to define the Label data.  That’s where you need to enter the expression that I mentioned earlier.</p>
<p>After you’ve entered the expression, the BIDS will ask you if you want to set the UseValueAsLabel to False, so click Yes.</p>
<p><img style="display: inline; border-width: 0px;" title="Do you want to set the UseValueAsLabel to False?" src="http://blog.hoegaerden.be/wp-content/uploads/image278.png" border="0" alt="Do you want to set the UseValueAsLabel to False?" width="593" height="540" /></p>
<p>With the label selected as shown in the next screenshot you can use the formatting toolbar buttons to give it a decent font and all.</p>
<p><img style="display: inline; border-width: 0px;" title="Data Bar with Label selected" src="http://blog.hoegaerden.be/wp-content/uploads/image279.png" border="0" alt="Data Bar with Label selected" width="207" height="37" /></p>
<p>For our second column of data bars the expressions need to take into account that we need the percentage as compared to all the data, unrelated to the level.  This actually makes it easier because we can use the same expression on both the State/Province and Country level.  Furthermore, it’s exactly the same expression as the one used on Country level in the first data bar column (i. e. the second expression above) because Country is the highest level.</p>
<p>Let’s have a look at the result in preview:</p>
<p><a href="http://blog.hoegaerden.be/wp-content/uploads/image280.png"><img style="display: inline; border-width: 0px;" title="Preview of report with labels on data bars" src="http://blog.hoegaerden.be/wp-content/uploads/image_thumb66.png" border="0" alt="Preview of report with labels on data bars" width="700" height="185" /></a></p>
<p>Woah, looks like we forgot something doesn’t it?  We forgot to format the label as being a percentage!  There are two ways to get that done: through the <strong>Number</strong> page on the <strong>Series Label Properties </strong>window:</p>
<p><img style="display: inline; border-width: 0px;" title="Series Label Properties: formatting number as percentage" src="http://blog.hoegaerden.be/wp-content/uploads/image281.png" border="0" alt="Series Label Properties: formatting number as percentage" width="585" height="482" /></p>
<p>Or by using the properties with the label selected (the Properties pane should show <strong>Chart Series Labels </strong>in the dropdown on top):</p>
<p><img style="display: inline; border-width: 0px;" title="Chart Series Labels properties" src="http://blog.hoegaerden.be/wp-content/uploads/image282.png" border="0" alt="Chart Series Labels properties" width="297" height="528" /></p>
<p>I’ve opted for a percentage without any decimals.</p>
<p>Let’s have another look at that report:</p>
<p><a href="http://blog.hoegaerden.be/wp-content/uploads/image283.png"><img style="display: inline; border-width: 0px;" title="The final report with two data bar columns" src="http://blog.hoegaerden.be/wp-content/uploads/image_thumb67.png" border="0" alt="The final report with two data bar columns" width="700" height="201" /></a></p>
<p>That sure looks better doesn’t it?  The status bars on the right compare over the grouping levels while those on the left compare with each other within the same group.  And they all have a clear label indicating what they represent.</p>
<p>Have fun putting those data bars on your reports!</p>
<p>Valentino.</p>
<p><strong>References</strong></p>
<p><a title="BOL 2008R2: What's New (Reporting Services)" href="http://msdn.microsoft.com/en-us/library/ms170438%28v=SQL.105%29.aspx" target="_blank">BOL 2008R2: What&#8217;s New (Reporting Services)</a></p>
<p><a title="BOL 2008R2: Sparklines and Data Bars" href="http://msdn.microsoft.com/en-us/library/ee636365%28v=SQL.105%29.aspx" target="_blank">BOL 2008R2: Sparklines and Data Bars</a></p>
<p><a title="An Introduction to Data Bars in SQL Server Reporting Services 2008 R2" href="http://blogs.msdn.com/seanboon/archive/2009/11/23/an-introduction-to-data-bars-in-sql-server-reporting-services-2008-r2.aspx" target="_blank">An Introduction to Data Bars in SQL Server Reporting Services 2008 R2</a></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.hoegaerden.be/2010/05/27/implementing-data-bars-in-a-grouped-table/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Looking Up Data On Different Sources</title>
		<link>http://blog.hoegaerden.be/2010/05/16/looking-up-data-on-different-sources/</link>
		<comments>http://blog.hoegaerden.be/2010/05/16/looking-up-data-on-different-sources/#comments</comments>
		<pubDate>Sun, 16 May 2010 12:41:27 +0000</pubDate>
		<dc:creator>Valentino Vranken</dc:creator>
				<category><![CDATA[Reporting Services]]></category>
		<category><![CDATA[SQLServerPedia Syndication]]></category>
		<category><![CDATA[data]]></category>
		<category><![CDATA[Reporting Services 2008 R2]]></category>
		<category><![CDATA[SQL Server 2008 R2]]></category>
		<category><![CDATA[SSRS]]></category>
		<category><![CDATA[Tutorial]]></category>

		<guid isPermaLink="false">http://blog.hoegaerden.be/2010/05/16/looking-up-data-on-different-sources/</guid>
		<description><![CDATA[Almost a year ago I wrote a small blog post to ask everyone to vote on a certain Connect item related to the linking of two datasets in a Reporting Services report.&#160; At this moment there have been 87 positive votes on the request.
Now that SQL Server 2008 R2 has been RTM’ed, it’s time to [...]]]></description>
			<content:encoded><![CDATA[<p>Almost a year ago I wrote <a title="Want to link datasets? Please vote!" href="http://blog.hoegaerden.be/2009/06/13/want-to-link-datasets-please-vote/">a small blog post</a> to ask everyone to vote on <a title="Merging / Linking datasets on report level (SSRS 2008)" href="https://connect.microsoft.com/SQLServer/feedback/ViewFeedback.aspx?FeedbackID=419819" target="_blank">a certain Connect item</a> related to the linking of two datasets in a Reporting Services report.&#160; At this moment there have been 87 positive votes on the request.</p>
<p>Now that <a title="Microsoft SQL Server 2008 R2" href="http://www.microsoft.com/sqlserver/2008/en/us/r2.aspx" target="_blank">SQL Server 2008 R2</a> has been RTM’ed, it’s time to demonstrate new functionality in SSRS that allows the retrieval of data from another dataset, a dataset not linked to your data region.&#160; It’s not completely the same as actually joining two datasets, but it’s better than nothing – and it works!</p>
<p>In this article I’ll show you how exactly this works by using the new Lookup, LookupSet and MultiLookup SSRS functions.</p>
<p>I’m using the AdventureWorks 2008R2 relational database and the AdventureWorksDW 2008R2 data warehouse, <a title="AdventureWorks 2008R2 November CTP" href="http://msftdbprodsamples.codeplex.com/releases/view/24854" target="_blank">available from CodePlex</a>.&#160; The main data is coming from the data warehouse while all the lookups are done on the relational database.</p>
<h2>Setting Up A Basic Table Report</h2>
<p>In my report I’ve created a dataset called dsInternetSales.&#160; This dataset is retrieving data from the AdventureWorksDW2008R2 data warehouse using the following query:</p>
<pre class="code"><span style="color: blue">select </span>PROD<span style="color: gray">.</span>EnglishProductName<span style="color: gray">, </span>PROD<span style="color: gray">.</span>ProductAlternateKey<span style="color: gray">, </span>PROD<span style="color: gray">.</span>ListPrice<span style="color: gray">,
    </span>PSC<span style="color: gray">.</span>EnglishProductSubcategoryName<span style="color: gray">, </span>PC<span style="color: gray">.</span>EnglishProductCategoryName<span style="color: gray">,
    </span>S<span style="color: gray">.</span>OrderQuantity<span style="color: gray">, </span>S<span style="color: gray">.</span>SalesAmount
<span style="color: blue">from </span>FactInternetSales S
<span style="color: gray">inner join </span>DimProduct PROD <span style="color: blue">on </span>S<span style="color: gray">.</span>ProductKey <span style="color: gray">= </span>PROD<span style="color: gray">.</span>ProductKey
<span style="color: gray">inner join </span>DimProductSubcategory PSC
    <span style="color: blue">on </span>PROD<span style="color: gray">.</span>ProductSubcategoryKey <span style="color: gray">= </span>PSC<span style="color: gray">.</span>ProductSubcategoryKey
<span style="color: gray">inner join </span>DimProductCategory PC <span style="color: blue">on </span>PSC<span style="color: gray">.</span>ProductCategoryKey <span style="color: gray">= </span>PC<span style="color: gray">.</span>ProductCategoryKey</pre>
<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></p>
<p>Using that dataset, I’ve set up a Table as shown in following screenshot.</p>
<p><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="Basic Table Report" border="0" alt="Basic Table Report" src="http://blog.hoegaerden.be/wp-content/uploads/image249.png" width="630" height="294" /> </p>
<p>The Details group has been set up to group on ProductAlternateKey.&#160; On top of the Details group, I’ve grouped on EnglishProductSubcategoryName and the top-level group is grouping on EnglishProductCategoryName.&#160; The Order Quantity column is displaying the sum of the OrderQuantity values for each ProductAlternateKey.</p>
<p>The result is a report that shows all (internet) sales per product, without any filtering.&#160; Very useful report if you want to know how many items your company has sold since it’s existence.&#160; Okay, management would probably like to see some filtering on here, but that’s not the purpose of this article.</p>
<p>Here’s what it looks like in Preview:</p>
<p><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="Basic Table Report Rendered" border="0" alt="Basic Table Report Rendered" src="http://blog.hoegaerden.be/wp-content/uploads/image250.png" width="481" height="360" /> </p>
<p>&#160;</p>
<h2>Adding Data From Another Database</h2>
<p>Imagine now that you need to add an extra line under each product, containing the product description.&#160; But this description is not available in the data warehouse.&#160; In fact it could even be stored on another server.</p>
<p>In the example here we will retrieve the description from the AdventureWorks2008R2 relational database.</p>
<h3>Setting Up The Second Dataset</h3>
<p>I’ve created an additional dataset called dsProductInfo, using the following query:</p>
<p><a href="http://11011.net/software/vspaste"></a></p>
<pre class="code"><span style="color: blue">select </span>P<span style="color: gray">.</span>ProductNumber<span style="color: gray">, </span>PD<span style="color: gray">.</span><span style="color: blue">Description
from </span>Production<span style="color: gray">.</span>Product P
<span style="color: gray">inner join </span>Production<span style="color: gray">.</span>ProductModel PM <span style="color: blue">on </span>P<span style="color: gray">.</span>ProductModelID <span style="color: gray">= </span>PM<span style="color: gray">.</span>ProductModelID
<span style="color: gray">inner join </span>Production<span style="color: gray">.</span>ProductModelProductDescriptionCulture PMPDC
    <span style="color: blue">on </span>PMPDC<span style="color: gray">.</span>ProductModelID <span style="color: gray">= </span>PM<span style="color: gray">.</span>ProductModelID
    <span style="color: gray">and </span>PMPDC<span style="color: gray">.</span>CultureID <span style="color: gray">= </span><span style="color: red">'en'
</span><span style="color: gray">inner join </span>Production<span style="color: gray">.</span>ProductDescription PD
    <span style="color: blue">on </span>PMPDC<span style="color: gray">.</span>ProductDescriptionID <span style="color: gray">= </span>PD<span style="color: gray">.</span>ProductDescriptionID</pre>
<p><a href="http://11011.net/software/vspaste"></a>Not only does it retrieve the product’s description, we’re also fetching the ProductNumber.&#160; Here’s what part of the result looks like:</p>
<p><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="Result of product description query" border="0" alt="Result of product description query" src="http://blog.hoegaerden.be/wp-content/uploads/image251.png" width="462" height="153" /> </p>
<p>The reason that we’re retrieving ProductNumber as well is because it matches with the ProductAlternateKey which we’ve retrieved earlier in our first dataset.&#160; And this is very important because that’s the key on which we’re going to link the datasets.</p>
<h3>Using The Lookup Function</h3>
<p>I’ve added an additional row inside the Details group and inserted a <a title="The Power Of The Placeholder" href="http://blog.hoegaerden.be/2009/04/07/the-power-of-the-placeholder/">Placeholder</a> to retrieve the product’s description, using the new <a title="BOL2008R2: Lookup Function" href="http://msdn.microsoft.com/en-us/library/ee210575%28v=SQL.105%29.aspx" target="_blank">Lookup function</a>.</p>
<p><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="Additional row inside Details group" border="0" alt="Additional row inside Details group" src="http://blog.hoegaerden.be/wp-content/uploads/image252.png" width="543" height="150" /> </p>
<p>So, what does the Placeholder’s expression look like?&#160; Here it is:</p>
<pre class="code">=Lookup(
    Fields!ProductAlternateKey.Value,
    Fields!ProductNumber.Value,
    Fields!Description.Value,
    <span style="color: #a31515">&quot;dsProductInfo&quot;
</span>)</pre>
<p><a href="http://11011.net/software/vspaste"></a>As you can see, the Lookup function requires four parameters.</p>
<p>The first parameter is the key value in your current dataset, the dataset used by the table data region.&#160; In our case that’s the ProductAlternateKey field in the dsInternetSales dataset.</p>
<p>The second parameter is the name of the key field in the second dataset, the one on which the lookup will happen.&#160; In our case that’s the ProductNumber in the dsProductInfo dataset.</p>
<p>The third parameter is the field from the second dataset that you’re wanting to retrieve using the lookup, in our case the Description field from dsProductInfo.</p>
<p>And finally, the last parameter is the name of the dataset on which you want to do the lookup.</p>
<p>Please note that parameter number four is a string parameter, so the value needs to be enclosed by double quotes.&#160; If you forget about that, you’ll get a couple of nice error messages like these:</p>
<blockquote>
<p>[rsInvalidLookupScope]&#160; The Value expression for the textrun ‘Textbox29.Paragraphs[0].TextRuns[0]’ has a scope parameter that is not valid for a lookup function. The scope parameter must be set to a string constant that is the name of a dataset.</p>
</blockquote>
<p>So, don’t forget the quotes.</p>
<p>With the Lookup call set up as explained, here’s the updated report Preview:</p>
<p><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="Rendered report with product descriptions added through Lookup" border="0" alt="Rendered report with product descriptions added through Lookup" src="http://blog.hoegaerden.be/wp-content/uploads/image253.png" width="518" height="594" /> </p>
<p>How cool is that huh?&#160; Each product has gotten a description, retrieved from another database, and still in the same table data region.&#160; Before R2 of SQL Server 2008, this wasn’t possible to achieve (well, not easily anyway) and now it’s actually fairly simple!</p>
<p>Is that all?&#160; Ha, I was kinda hoping that you were going to ask that.&#160; No, it’s not all, there are two more new lookup functions: LookupSet and MultiLookup.</p>
<h3>More Lookups: The LookupSet Function</h3>
<p>In case you’re wondering, in the Expression Builder the new lookup functions are located under the Miscellaneous node:</p>
<p><a href="http://blog.hoegaerden.be/wp-content/uploads/image254.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="Expression Builder: the new lookup functions are under Miscellaneous" border="0" alt="Expression Builder: the new lookup functions are under Miscellaneous" src="http://blog.hoegaerden.be/wp-content/uploads/image_thumb61.png" width="682" height="231" /></a> </p>
<p>Let’s say that you want to add another detail row, this time it needs to show all colors in which the product is manufactured.&#160; Again this additional info is coming from the AdventureWorks relational database.</p>
<p>I’ve created a dataset called dsProductColors using the following query:</p>
<pre class="code"><span style="color: blue">select distinct </span><span style="color: gray">LEFT(</span>P<span style="color: gray">.</span>ProductNumber<span style="color: gray">, </span>6<span style="color: gray">) </span><span style="color: blue">as </span>ProductCodeWithoutColorAndSize<span style="color: gray">,
    </span>P<span style="color: gray">.</span>Color
<span style="color: blue">from </span>Production<span style="color: gray">.</span>Product P
<span style="color: blue">where </span>P<span style="color: gray">.</span>Color <span style="color: gray">is not null</span></pre>
<p><a href="http://11011.net/software/vspaste"></a>When looking at the product codes, I noticed that for the products which are available in several colors and sizes, the last four characters represent the color and size.&#160; Which means the first six characters define the product itself, without color or size.&#160; That’s why the query is using the Left function to create a product code of only the first six characters of the ProductNumber.&#160; Using the distinct keyword, we remove any duplicate records.</p>
<p>(Please note that I’m not 100% sure if this logic applies to all products but for this demo it’s fine.)</p>
<p>Here’s what the query retrieves:</p>
<p><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="List of product colors per product" border="0" alt="List of product colors per product" src="http://blog.hoegaerden.be/wp-content/uploads/image255.png" width="264" height="321" /> </p>
<p>As you can see, for some products there’s more than one record.&#160; And that’s exactly what the <a title="BOL2008R2: LookupSet Function" href="http://msdn.microsoft.com/en-us/library/ee210576%28v=SQL.105%29.aspx" target="_blank">LookupSet function</a> was made for: it retrieves a set of data based on the key given to it.&#160; This is different from the Lookup, where for each key value it would fetch only one value.</p>
<p>Again I’ve added an additional row inside the Details group and used a placeholder with the following expression:</p>
<pre class="code">=Join(
    LookupSet(
        Left(Fields!ProductAlternateKey.Value, 6),
        Fields!ProductCodeWithoutColorAndSize.Value,
        Fields!Color.Value,
        <span style="color: #a31515">&quot;dsProductColors&quot;
    </span>),
    <span style="color: #a31515">&quot;, &quot;
</span>)</pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>The LookupSet call itself looks very similar to the Lookup, with the same four parameters.&#160; I’ve used the Left function on the first parameter to apply the same logic to the ProductAlternateKey as we did with the ProductNumber.</p>
<p>However, there’s one important difference: the call of the Join function.&#160; This is needed because the LookupSet is returning a set, or better, a VariantArray, not just a single value.&#160; And an array cannot be visualized without first concatenating the values somehow.&#160; With the Join, we can concatenate the different values, using a comma as separator.</p>
<p>And here’s the resulting report:</p>
<p><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="Our report displaying the list of colors for each product" border="0" alt="Our report displaying the list of colors for each product" src="http://blog.hoegaerden.be/wp-content/uploads/image256.png" width="519" height="341" /> </p>
<p>With the first two lookup functions covered there’s one more to go.</p>
<h3>Just One More Lookup: The MultiLookup Function</h3>
<p>Guess what crazy request the business people have come up with this time?!&#160; The report should have a multi-value filter on region, and for each region selected, the top of the table should list the number of shops opened in the first year in those regions.&#160; For example, if the first shop in France was opened in 1970 and in that same year there were two other shops opened in France, the report should state “France: 3 shop(s) opened in 1970”.</p>
<p>Ow, and that list should be located right under the main header so deciding to use a textbox outside of the table is not a good idea <img src='http://blog.hoegaerden.be/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
<p>Sounds like we can use the <a title="BOL2008R2: Multilookup Function" href="http://msdn.microsoft.com/en-us/library/ee210583%28v=SQL.105%29.aspx" target="_blank">MultiLookup</a> function for this request.&#160; But let’s first set up the filter.</p>
<p>I’ve created a dataset called dsRegions, using the following query on the data warehouse:</p>
<pre class="code"><span style="color: blue">select </span>DST<span style="color: gray">.</span>SalesTerritoryAlternateKey<span style="color: gray">,
    </span>DST<span style="color: gray">.</span>SalesTerritoryCountry <span style="color: gray">+ </span><span style="color: red">' - ' </span><span style="color: gray">+ </span>DST<span style="color: gray">.</span>SalesTerritoryRegion <span style="color: blue">as </span>CountryRegion
<span style="color: blue">from </span>DimSalesTerritory DST
<span style="color: blue">where </span>DST<span style="color: gray">.</span>SalesTerritoryAlternateKey <span style="color: gray">&gt; </span>0</pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>Then I’ve added a multi-value parameter called Regions with the Available Values coming from the dsRegions dataset.</p>
<p><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="Regions parameter: the Available Values" border="0" alt="Regions parameter: the Available Values" src="http://blog.hoegaerden.be/wp-content/uploads/image257.png" width="585" height="482" /> </p>
<p>This parameter can now be used in our main dataset.&#160; Here’s the updated query:</p>
<p><a href="http://11011.net/software/vspaste"></a></p>
<pre class="code"><span style="color: blue">select </span>PROD<span style="color: gray">.</span>EnglishProductName<span style="color: gray">, </span>PROD<span style="color: gray">.</span>ProductAlternateKey<span style="color: gray">, </span>PROD<span style="color: gray">.</span>ListPrice<span style="color: gray">,
    </span>PSC<span style="color: gray">.</span>EnglishProductSubcategoryName<span style="color: gray">, </span>PC<span style="color: gray">.</span>EnglishProductCategoryName<span style="color: gray">,
    </span>S<span style="color: gray">.</span>OrderQuantity<span style="color: gray">, </span>S<span style="color: gray">.</span>SalesAmount
<span style="color: blue">from </span>FactInternetSales S
<span style="color: gray">inner join </span>DimProduct PROD <span style="color: blue">on </span>S<span style="color: gray">.</span>ProductKey <span style="color: gray">= </span>PROD<span style="color: gray">.</span>ProductKey
<span style="color: gray">inner join </span>DimProductSubcategory PSC
    <span style="color: blue">on </span>PROD<span style="color: gray">.</span>ProductSubcategoryKey <span style="color: gray">= </span>PSC<span style="color: gray">.</span>ProductSubcategoryKey
<span style="color: gray">inner join </span>DimProductCategory PC <span style="color: blue">on </span>PSC<span style="color: gray">.</span>ProductCategoryKey <span style="color: gray">= </span>PC<span style="color: gray">.</span>ProductCategoryKey
<span style="color: gray">inner join </span>DimSalesTerritory DST <span style="color: blue">on </span>S<span style="color: gray">.</span>SalesTerritoryKey <span style="color: gray">= </span>DST<span style="color: gray">.</span>SalesTerritoryKey
<span style="color: blue">where </span>DST<span style="color: gray">.</span>SalesTerritoryAlternateKey <span style="color: gray">in (</span>@Regions<span style="color: gray">)</span></pre>
<p><a href="http://11011.net/software/vspaste"></a>The only difference with the previous query are the two last lines: we add DimSalesTerritory to the joins and filter it on SalesTerritoryAlternateKey.</p>
<p>Don’t forget to set up the parameter.</p>
<p><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="Setting up the parameter on dsInternetSales" border="0" alt="Setting up the parameter on dsInternetSales" src="http://blog.hoegaerden.be/wp-content/uploads/image258.png" width="600" height="234" /> </p>
<p>With the filter implemented, let’s get started on that extra lookup.&#160; </p>
<p>First we need to add the dataset containing the data that we need.&#160; I’ve created a dataset called dsShopsOpenedInFirstYear, using the following query on the relational database:</p>
<pre class="code"><span style="color: blue">with </span>ShopOpened <span style="color: blue">as
</span><span style="color: gray">(
    </span><span style="color: blue">select </span>T<span style="color: gray">.</span>TerritoryID<span style="color: gray">, </span>T<span style="color: gray">.</span>Name Territory<span style="color: gray">, </span>S<span style="color: gray">.</span>Name ShopName<span style="color: gray">,
        </span>S<span style="color: gray">.</span>[Demographics]<span style="color: gray">.</span>value<span style="color: gray">(</span><span style="color: red">'declare default element namespace &quot;http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/StoreSurvey&quot;;
        (/StoreSurvey/YearOpened)[1]'</span><span style="color: gray">, </span><span style="color: red">'integer'</span><span style="color: gray">) </span><span style="color: blue">AS </span>[YearOpened]
    <span style="color: blue">from </span>Sales<span style="color: gray">.</span>Store S
    <span style="color: gray">inner join </span>Sales<span style="color: gray">.</span>SalesPerson SP <span style="color: blue">on </span>S<span style="color: gray">.</span>SalesPersonID <span style="color: gray">= </span>SP<span style="color: gray">.</span>BusinessEntityID
    <span style="color: gray">inner join </span>Sales<span style="color: gray">.</span>SalesTerritory T <span style="color: blue">on </span>SP<span style="color: gray">.</span>TerritoryID <span style="color: gray">= </span>T<span style="color: gray">.</span>TerritoryID
<span style="color: gray">),
</span>FirstShopOpened <span style="color: blue">as
</span><span style="color: gray">(
    </span><span style="color: blue">select </span><span style="color: magenta">MIN</span><span style="color: gray">(</span>YearOpened<span style="color: gray">) </span>YearOpened<span style="color: gray">, </span>TerritoryID
    <span style="color: blue">from </span>ShopOpened
    <span style="color: blue">group by </span>TerritoryID
<span style="color: gray">)
</span><span style="color: blue">select </span>SO<span style="color: gray">.</span>TerritoryID<span style="color: gray">,
    </span>SO<span style="color: gray">.</span>Territory <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><span style="color: magenta">COUNT</span><span style="color: gray">(*) </span><span style="color: blue">as varchar</span><span style="color: gray">(</span>100<span style="color: gray">)) +
    </span><span style="color: red">' shop(s) opened in ' </span><span style="color: gray">+ </span><span style="color: magenta">CAST</span><span style="color: gray">(</span>SO<span style="color: gray">.</span>YearOpened <span style="color: blue">as char</span><span style="color: gray">(</span>4<span style="color: gray">)) </span><span style="color: blue">as </span>ShopString
<span style="color: blue">from </span>FirstShopOpened FSO
<span style="color: gray">inner join </span>ShopOpened SO <span style="color: blue">on </span>SO<span style="color: gray">.</span>TerritoryID <span style="color: gray">= </span>FSO<span style="color: gray">.</span>TerritoryID
    <span style="color: gray">and </span>SO<span style="color: gray">.</span>YearOpened <span style="color: gray">= </span>FSO<span style="color: gray">.</span>YearOpened
<span style="color: blue">group by </span>SO<span style="color: gray">.</span>TerritoryID<span style="color: gray">, </span>SO<span style="color: gray">.</span>Territory<span style="color: gray">, </span>SO<span style="color: gray">.</span>YearOpened</pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>This query uses a couple of <a title="BOL2008: Using Common Table Expressions" href="http://msdn.microsoft.com/en-us/library/ms190766.aspx" target="_blank">Common Table Expressions</a> to get to the result as we need it.&#160; The first CTE, ShopOpened, creates a list of all shops with their territory and the opening year.&#160; The second CTE, FirstShopOpened uses the ShopOpened CTE to retrieve the first opening year for each territory.</p>
<p>And finally the main query uses both CTEs to create the following result:</p>
<p><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="Number of shops opened in first year per territory" border="0" alt="Number of shops opened in first year per territory" src="http://blog.hoegaerden.be/wp-content/uploads/image259.png" width="336" height="236" /> </p>
<p>For each territory we’ve constructed a string that shows how many shops were opened in the first year of that region, and in what year it happened.&#160; Coincidentally all regions had shops opened in 1970.</p>
<p>The TerritoryID corresponds with the SalesTerritoryAlternateKey, which is the value of our Regions parameter.</p>
<p>I’ve added an extra row under the top row in the table data region, and I’m using the following expression in that row:</p>
<pre class="code">=Join(
    MultiLookup(
        Parameters!Regions.Value,
        Fields!TerritoryID.Value,
        Fields!ShopString.Value,
        <span style="color: #a31515">&quot;dsShopsOpenedInFirstYear&quot;
    </span>),
    <span style="color: #a31515">&quot;&lt;br&gt;&quot;
</span>)</pre>
<p>The MultiLookup takes four parameters, just like the two previous lookup functions.&#160; They are all the same, except for the first one.&#160; It may not be very obvious in the example here, but the Parameters!Regions.Value is in fact not just a single value.&#160; It’s an array because we’ve set up the parameter as being multi-valued.</p>
<p>And that’s exactly what the MultiLookup function requires.&#160; Here’s the description for that first parameter, as stated in the Books Online:</p>
<blockquote>
<p>(<strong>VariantArray</strong>) An expression that is evaluated in the current scope and that specifies the set of names or keys to look up. For example, for a multivalue parameter, <code>=Parameters!IDs.value</code>.</p>
</blockquote>
<p>Just like the LookupSet function, MultiLookup returns a VariantArray, so we use the Join function to concatenate the values.</p>
<p>Interesting to note here is that I’m adding the break HTML tag as separator.&#160; I want the result of the expression to be treated as HTML, so that each value retrieved ends up at a new line in the textbox.&#160; To get this to work as expected, you need to tell the Placeholder that the resulting value should be treated as HTML:</p>
<p><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="Using HTML in a Placeholder" border="0" alt="Using HTML in a Placeholder" src="http://blog.hoegaerden.be/wp-content/uploads/image260.png" width="592" height="479" /> </p>
<p>Everything is now set up to have another report Preview.&#160; The following screenshot shows the report with the data filtered on Canada, France and Australia:</p>
<p><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="The final report performing three different lookups" border="0" alt="The final report performing three different lookups" src="http://blog.hoegaerden.be/wp-content/uploads/image261.png" width="518" height="452" /> </p>
<p>Seems to be working fine, doesn’t it?</p>
<p>Okay, that’s it for now, have fun looking up that data!</p>
<p>Valentino.</p>
<p><strong>References</strong></p>
<p><a title="BOL2008R2: Lookup Function" href="http://msdn.microsoft.com/en-us/library/ee210575%28v=SQL.105%29.aspx" target="_blank">BOL2008R2: Lookup Function</a></p>
<p><a title="BOL2008R2: LookupSet Function" href="http://msdn.microsoft.com/en-us/library/ee210576%28v=SQL.105%29.aspx" target="_blank">BOL2008R2: LookupSet Function</a></p>
</p>
<p><a title="BOL2008R2: MultiLookup Function" href="http://msdn.microsoft.com/en-us/library/ee210583%28v=SQL.105%29.aspx" target="_blank">BOL2008R2: MultiLookup Function</a></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.hoegaerden.be/2010/05/16/looking-up-data-on-different-sources/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Remove Unused Columns From The Data Flow</title>
		<link>http://blog.hoegaerden.be/2010/05/12/remove-unused-columns-from-the-data-flow/</link>
		<comments>http://blog.hoegaerden.be/2010/05/12/remove-unused-columns-from-the-data-flow/#comments</comments>
		<pubDate>Wed, 12 May 2010 16:25:47 +0000</pubDate>
		<dc:creator>Valentino Vranken</dc:creator>
				<category><![CDATA[Integration Services]]></category>
		<category><![CDATA[data]]></category>
		<category><![CDATA[Integration Services 2008]]></category>
		<category><![CDATA[SSIS]]></category>

		<guid isPermaLink="false">http://blog.hoegaerden.be/2010/05/12/remove-unused-columns-from-the-data-flow/</guid>
		<description><![CDATA[Consider the following scenario. You&#8217;ve been developing some SSIS packages, nicely making sure that all errors and warnings are gone from the Error List window. So the next step is to deploy the packages to the server and schedule them. After some days you decide to have a look at the sysssislog table and discover [...]]]></description>
			<content:encoded><![CDATA[<p>Consider the following scenario. You&#8217;ve been developing some SSIS packages, nicely making sure that all errors and warnings are gone from the Error List window. So the next step is to deploy the packages to the server and schedule them. After some days you decide to have a look at the <a title="BOL 2008: sysssislog (Transact-SQL)" href="http://technet.microsoft.com/en-us/library/ms186984.aspx" target="_blank">sysssislog</a> table and discover that it&#8217;s filled with OnWarning records!</p>
<p>Well, this is an extreme example of course. Careful developers would notice the warnings in either the Output window or the Progress tab while testing the package in the BIDS.</p>
<p>All these warnings are indicating that some columns in your data flows have become obsolete.  Here&#8217;s an example:</p>
<blockquote><p>The output column &#8220;BKCustomerID&#8221; (13529) on output &#8220;OLE DB Source Output&#8221; (12088) and component &#8220;OLE_SRC MySource&#8221; (12077) is not subsequently used in the Data Flow task. Removing this unused output column can increase Data Flow task performance.</p></blockquote>
<p>The problem with these warnings is not only that they slow down the transformation process but they obfuscate the SSIS log with thousands of records, making it difficult for you when you need to investigate an issue. And this is something that you really don’t want, especially when the business people are calling you to ask why the data is not up-to-date on the production servers.</p>
<p>If you ever wished that you&#8217;d see these warnings whilst developing the package instead of at runtime only, now&#8217;s your chance to make a change. Or better, to ask Microsoft to implement the change.</p>
<p><a title="Connect: Dataflow unused columns should be highlighed in BIDS" href="https://connect.microsoft.com/SQLServer/feedback/details/553088/dataflow-unused-columns" target="_blank"><strong>Follow this link to cast your vote on Connect!</strong></a></p>
<p>Thank you Jamie <a title="SSIS code smell – Unused columns in the dataflow" href="http://sqlblog.com/blogs/jamie_thomson/archive/2010/04/21/ssis-code-smell-unused-columns-in-the-dataflow.aspx" target="_blank">for pointing this out</a>.</p>
<p>Have fun!</p>
<p>Valentino.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.hoegaerden.be/2010/05/12/remove-unused-columns-from-the-data-flow/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Free SQLUG Event: SCOM 2007 R2 And Managing SQL Server</title>
		<link>http://blog.hoegaerden.be/2010/05/06/free-sqlug-event-scom-2007-r2-and-managing-sql-server/</link>
		<comments>http://blog.hoegaerden.be/2010/05/06/free-sqlug-event-scom-2007-r2-and-managing-sql-server/#comments</comments>
		<pubDate>Thu, 06 May 2010 19:38:27 +0000</pubDate>
		<dc:creator>Valentino Vranken</dc:creator>
				<category><![CDATA[Events]]></category>
		<category><![CDATA[Community Event]]></category>
		<category><![CDATA[data]]></category>
		<category><![CDATA[SQLUG]]></category>

		<guid isPermaLink="false">http://blog.hoegaerden.be/2010/05/06/free-sqlug-event-scom-2007-r2-and-managing-sql-server/</guid>
		<description><![CDATA[No excuses for not attending if you’re a SQL Server DBA, system administrator or just interested in the subject.  It’s a free training event, it’s presented by Ricardo Noulez and it’s taking place in the Microsoft offices in Zaventem.
When?
Thursday, April 20th, 2010
Where?
Microsoft België
Corporate Village
Leonardo Da Vincilaan 3
1935 Zaventem
More info
Managing SQL Server requires insight in the [...]]]></description>
			<content:encoded><![CDATA[<p>No excuses for not attending if you’re a SQL Server DBA, system administrator or just interested in the subject.  It’s a free training event, it’s presented by Ricardo Noulez and it’s taking place in the Microsoft offices in Zaventem.</p>
<p><strong>When?</strong></p>
<p>Thursday, April 20th, 2010</p>
<p><strong>Where?</strong></p>
<p>Microsoft België<br />
Corporate Village<br />
Leonardo Da Vincilaan 3<br />
1935 Zaventem</p>
<p><strong>More info</strong></p>
<p>Managing SQL Server requires insight in the state and performance of the underlying infrastructure, OS and individual SQL components.</p>
<p>System Center Operations Manager 2007 R2 cannot only give you that insight, but it can be reused in a broader application/service monitoring view.<br />
This session focuses on the out-of-the-box capabilities of System Center Operations Manager 2007 R2 but also shows you how a distributed application can easily reuse these capabilities to provide an extra step in monitoring.</p>
<p><strong>About Ricardo Noulez</strong></p>
<p>Ricardo Noulez is a Senior Consultant at Microsoft Consulting Services in the Benelux. Been with Microsoft since January 2002 as a subject matter expert in Microsoft Operations Manager (now System Center Operations Manager) and Systems Management Server (System Center Configuration Manager). Ricardo has a great focus around the System Center product family and he is currently expanding his horizons towards the System Center Service Manager product. He is always interested in finding ways to improve the way that customers manage their IT environment and has certifications and great affinity with ITIL good practices and the MOF framework to help them achieve this. In the first third of his 18 year career, Ricardo was a developer but gradually converted himself to Systems Management and IT operations. This gives him a broad understanding in the different needs and view-points of all technical players that make IT ideas come true.</p>
<p><strong>Click </strong><a title="What is System Center Operations Manager 2007 R2 and how can it help in managing SQL Servers" href="http://sqlug.be/nextevent/event/?id=23" target="_blank"><strong>here for more info</strong></a><strong> or </strong><a title="Register for What is System Center Operations Manager 2007 R2 and how can it help in managing SQL Servers" href="http://sqlug.be/nextevent/event/register/?id=23" target="_blank"><strong>here to register</strong></a><strong>!</strong></p>
<p>See you there,</p>
<p>Valentino</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.hoegaerden.be/2010/05/06/free-sqlug-event-scom-2007-r2-and-managing-sql-server/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Importing Data Using The Wizard: Mixing The Wrong Ingredients</title>
		<link>http://blog.hoegaerden.be/2010/05/03/importing-data-using-the-wizard-mixing-the-wrong-ingredients/</link>
		<comments>http://blog.hoegaerden.be/2010/05/03/importing-data-using-the-wizard-mixing-the-wrong-ingredients/#comments</comments>
		<pubDate>Mon, 03 May 2010 20:39:18 +0000</pubDate>
		<dc:creator>Valentino Vranken</dc:creator>
				<category><![CDATA[Integration Services]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[data]]></category>
		<category><![CDATA[Excel]]></category>
		<category><![CDATA[SSIS]]></category>

		<guid isPermaLink="false">http://blog.hoegaerden.be/2010/05/03/importing-data-using-the-wizard-mixing-the-wrong-ingredients/</guid>
		<description><![CDATA[A while ago I wrote an article about how you can use the SQL Server Import and Export wizard to import Excel data into a SQL Server database.  In this sequel I’m going to show you some problems which you may encounter when using that wizard.  Just like any good old wizard, he’s only as [...]]]></description>
			<content:encoded><![CDATA[<p>A while ago I wrote an <a title="Importing Excel Data Using Integration Services" href="http://blog.hoegaerden.be/2010/04/20/importing-excel-data-using-integration-services/">article about how you can use the SQL Server Import and Export wizard to import Excel data into a SQL Server database</a>.  In this sequel I’m going to show you some problems which you may encounter when using that wizard.  Just like any good old wizard, he’s only as good as his recipes.  If a recipe is missing an ingredient, the resulting potion will probably not behave as expected and before you know it it explodes in your face.</p>
<p>I’ll be using the same Excel file as in my previous article.  In case you’re having some problems understanding the data or locating certain screens mentioned in this article, I recommend you to first <a title="Importing Excel Data Using Integration Services" href="http://blog.hoegaerden.be/2010/04/20/importing-excel-data-using-integration-services/">read the prequel</a>.</p>
<h2>Some Common Wizard Pitfalls And Their Solution</h2>
<h3>Drop And Re-Create Destination Table</h3>
<p>Imagine that for your import process you’re planning to import that Excel data regularly.  So the logical step is to make sure that each time the process runs, it starts from scratch.  Following that, what seems to be an interesting checkbox is located on the <strong>Column Mappings</strong> screen.  This checkbox is called<em> Drop and re-create destination table</em>.</p>
<p><img style="display: inline; border: 0px;" title="Column Mappings: Drop and re-create destination table" src="http://blog.hoegaerden.be/wp-content/uploads/image234.png" border="0" alt="Column Mappings: Drop and re-create destination table" width="608" height="486" /></p>
<p>So you activate that checkbox and execute the package.  However, it ends with an error in the Executing phase:</p>
<p><img style="display: inline; border: 0px;" title="SQL Server Import and Export Wizard: error while Executing" src="http://blog.hoegaerden.be/wp-content/uploads/image235.png" border="0" alt="SQL Server Import and Export Wizard: error while Executing" width="566" height="579" /></p>
<p>Here’s what the error details say:</p>
<p><img style="display: inline; border: 0px;" title="image" src="http://blog.hoegaerden.be/wp-content/uploads/image236.png" border="0" alt="image" width="621" height="167" /></p>
<blockquote><p>Error 0xc002f210: Drop table failed… Cannot drop the table dbo.ProductList$ because it does not exist or you do not have permission.</p></blockquote>
<p>So why does this error occur?  Let’s examine the SSIS package that was generated.  This is what the Control Flow looks like:</p>
<p><img style="display: inline; border: 0px;" title="A Control Flow that doesn't take a non-existing table into account" src="http://blog.hoegaerden.be/wp-content/uploads/image237.png" border="0" alt="A Control Flow that doesn't take a non-existing table into account" width="161" height="232" /></p>
<p>New here is that Drop table SQL Task.  Taking a closer look at the task, here’s the query:</p>
<pre class="code"><span style="color: blue;">drop table </span>[dbo]<span style="color: gray;">.</span>[ProductList$]
<span style="color: blue;">GO</span></pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>So the first task is to drop the table.  This flow does not take into account that the table may not yet exist, as is the case here, causing the error.</p>
<p>However, do you notice the blue arrow connecting the Drop table task with the next SQL Task?  That means that it doesn’t require the execution of the task to end successfully, the flow will continue even when an error occurred (unlike when the green connector – meaning Success – is used).</p>
<p>Double-clicking the blue connector gives us the <strong>Precedence Constraint Editor</strong> window:</p>
<p><img style="display: inline; border: 0px;" title="Precedence Constraint Editor" src="http://blog.hoegaerden.be/wp-content/uploads/image238.png" border="0" alt="Precedence Constraint Editor" width="645" height="483" /></p>
<p>Indeed, the value for the constraint is set to <em>Completion</em>.</p>
<p>This also explains why the wizard continued executing all phases, even though an error occurred.  And the end result was that the table is really created, containing the data as expected.</p>
<p>If you’d like to avoid the error and handle the non-existing table, you could replace the query in the Drop table SQL Task with the following one:</p>
<pre class="code"><span style="color: blue;">if </span><span style="color: gray;">exists
(
    </span><span style="color: blue;">select </span><span style="color: gray;">* </span><span style="color: blue;">from </span><span style="color: green;">INFORMATION_SCHEMA</span><span style="color: gray;">.</span><span style="color: green;">TABLES </span>T
    <span style="color: blue;">where </span>T<span style="color: gray;">.</span>TABLE_NAME <span style="color: gray;">= </span><span style="color: red;">'ProductList$'
        </span><span style="color: gray;">and </span>T<span style="color: gray;">.</span>TABLE_SCHEMA <span style="color: gray;">= </span><span style="color: red;">'dbo'
</span><span style="color: gray;">)
    </span><span style="color: blue;">drop table </span>[dbo]<span style="color: gray;">.</span>[ProductList$]<span style="color: gray;">;</span></pre>
<p><a href="http://11011.net/software/vspaste"></a>It first checks if the table exists, taking schema into account, and will only perform the DROP TABLE statement if the table actually exists.</p>
<h3>Field Length Insufficient</h3>
<p>On to the next possible issue.  When examining our Excel sheet we find out that the content of the Class column is either blank or just one character:</p>
<p><img style="display: inline; border: 0px;" title="Possible values of the Class field" src="http://blog.hoegaerden.be/wp-content/uploads/image239.png" border="0" alt="Possible values of the Class field" width="287" height="308" /></p>
<p>So we decide to change its type to char(1), again using the <strong>Column Mappings</strong> screen.</p>
<p><img style="display: inline; border: 0px;" title="Column Mappings: change type to char(1)" src="http://blog.hoegaerden.be/wp-content/uploads/image240.png" border="0" alt="Column Mappings: change type to char(1)" width="584" height="653" /></p>
<p>However, clicking Finish at the end of the wizard gives us a fatal error:</p>
<p><img style="display: inline; border: 0px;" title="SQL Server Import and Export Wizard: Operatoin stopped in Error" src="http://blog.hoegaerden.be/wp-content/uploads/image241.png" border="0" alt="SQL Server Import and Export Wizard: Operatoin stopped in Error" width="566" height="579" /></p>
<p>And these are the details of the error:</p>
<ul>
<li>
<ul>
<li>Error 0xc02020c5: Data Flow Task 1: Data conversion failed while converting column &#8220;Class&#8221; (63) to column &#8220;Class&#8221; (139).  The conversion returned status value 4 and status text<strong> &#8220;Text was truncated or one or more characters had no match in the target code page.&#8221;</strong>.(SQL Server Import and Export Wizard)</li>
<li>Error 0xc020902a: Data Flow Task 1: The &#8220;output column &#8220;Class&#8221; (139)&#8221; failed because truncation occurred, and the truncation row disposition on &#8220;output column &#8220;Class&#8221; (139)&#8221; specifies failure on truncation. A truncation error occurred on the specified object of the specified component.
<p>(SQL Server Import and Export Wizard)</li>
<li>Error 0xc0047022: Data Flow Task 1: SSIS Error Code DTS_E_PROCESSINPUTFAILED.  The ProcessInput method on component &#8220;Data Conversion 0 &#8211; 0&#8243; (131) failed with error code 0xC020902A while processing input &#8220;Data Conversion Input&#8221; (132). The identified component returned an error from the ProcessInput method. The error is specific to the component, but the error is fatal and will cause the Data Flow task to stop running.  There may be error messages posted before this with more information about the failure.
<p>(SQL Server Import and Export Wizard)</li>
</ul>
</li>
</ul>
<p>Wow, that’s quite a long list for such a simple change!  Apparently it wasn’t such a good idea to limit the length of this field.  But why does this error occur?  After all, the values in this field are just one character long.  Except, after taking a closer look at the Excel sheet it turns out that these values contain a trailing space!</p>
<p>One way of avoiding this issue is by specifying a higher length for the field.  In this particular case however, you may decide to modify your <strong>Data Flow</strong> so that it removes the trailing space.  Here’s what the Data Flow currently looks like:</p>
<p><img style="display: inline; border: 0px;" title="Data Flow to import Excel data" src="http://blog.hoegaerden.be/wp-content/uploads/image242.png" border="0" alt="Data Flow to import Excel data" width="147" height="221" /></p>
<p>The <strong>Data Conversion transformation</strong> takes care of converting the Class column into a DT_STR of length 1.</p>
<p><a href="http://blog.hoegaerden.be/wp-content/uploads/image243.png"><img style="display: inline; border: 0px;" title="Data Conversion Transformation Editor" src="http://blog.hoegaerden.be/wp-content/uploads/image_thumb57.png" border="0" alt="Data Conversion Transformation Editor" width="684" height="593" /></a></p>
<p>We’re going to replace that <strong>Data Conversion Transformation</strong> with another one, a <strong>Derived Column transformation</strong>.  This is one of the transformations which I’m using all the time.</p>
<p><a href="http://blog.hoegaerden.be/wp-content/uploads/image244.png"><img style="display: inline; border: 0px;" title="Derived Column Transformation Editor" src="http://blog.hoegaerden.be/wp-content/uploads/image_thumb58.png" border="0" alt="Derived Column Transformation Editor" width="682" height="545" /></a></p>
<p>As you can see in the screenshot, I’ve set it up so that it uses <a title="BOL 2008: TRIM (SSIS Expressions)" href="http://msdn.microsoft.com/en-us/library/ms139947.aspx" target="_blank">the TRIM() function</a> on the Class input column to remove any leading or trailing spaces.  Furthermore I’m using a cast to DT_STR of length 1 to ensure the correct field type.  The resulting column is called Class_STR, to clearly indicate that it has been converted to DT_STR.</p>
<p>This is the resulting Data Flow:</p>
<p><img style="display: inline; border: 0px;" title="Data Flow: Destination component complains about lineage ID" src="http://blog.hoegaerden.be/wp-content/uploads/image245.png" border="0" alt="Data Flow: Destination component complains about lineage ID" width="178" height="234" /></p>
<p>Looks like we’ve got an issue with our Destination component now.  Which is quite logical: we still need to tell it that it should use the newly-created Class_STR column.  So double-click the component.</p>
<p><a href="http://blog.hoegaerden.be/wp-content/uploads/image246.png"><img style="display: inline; border: 0px;" title=" Restore Invalid Column References Editor" src="http://blog.hoegaerden.be/wp-content/uploads/image_thumb59.png" border="0" alt=" Restore Invalid Column References Editor" width="694" height="507" /></a></p>
<p>Double-clicking the Destination component will open up the<strong> Restore Invalid Column References Editor</strong>.  In Available Columns, select the new Class_STR column to replace the Class column which was used previously.</p>
<p>That’s it, your SSIS package will now remove the trailing spaces from the Class column and store it in a column of char(1).</p>
<h3>Excel Layout Change</h3>
<p>Here’s another common issue when dealing with Excel SSIS imports.  If you’re not 100% in control of that Excel sheet, someone will someday make a structural change to it and it will cause your import process to fail.</p>
<p>In this really simple example I’ve opened up the Excel sheet and renamed the Color column to Colour.  Which is something that may happen in real life: a British person takes over the maintenance of that product list and sees that the Color column is spelled the wrong way, and corrects it without informing anyone.</p>
<p>What does that mean for our import?  Here’s the result when manually executing the package using <a title="BOL 2008: How to: Run a Package Using the DTExecUI Utility" href="http://msdn.microsoft.com/en-us/library/ms141707.aspx" target="_blank">DTExecUI.exe</a>:</p>
<p><a href="http://blog.hoegaerden.be/wp-content/uploads/image247.png"><img style="display: inline; border: 0px;" title="DTExecUI: Package Execution Progress" src="http://blog.hoegaerden.be/wp-content/uploads/image_thumb60.png" border="0" alt="DTExecUI: Package Execution Progress" width="688" height="313" /></a></p>
<p>In this case execution will fail because the package cannot find the Color column in the Excel sheet.  More precisely it says VS_NEEDSNEWMETADATA.  This is a really common error when using Integration Services, but you need to manually update the package to handle such changes.</p>
<p>In other words: try to be in control of that sheet as much as you can, and if possible: set up another way to maintain such data.  For instance by using a <a title="SQL Server 2008 R2 Master Data Services" href="http://www.microsoft.com/sqlserver/2008/en/us/mds.aspx" target="_blank">Master Data Management system</a>.  But that’s stuff for later on, in future articles.</p>
<h2>Conclusion</h2>
<p>As long as you’re aware of some of the common issues into which you may run, I still think the Import and Export Wizard is an interesting option to start your first SSIS package.  When running into an issue, I recommend to open up the SSIS package and have a closer look through the Business Intelligence Development Studio.  Then make any changes there so you can handle the errors.</p>
<p>Happy importing!</p>
<p>Valentino.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.hoegaerden.be/2010/05/03/importing-data-using-the-wizard-mixing-the-wrong-ingredients/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Importing Excel Data Using Integration Services</title>
		<link>http://blog.hoegaerden.be/2010/04/20/importing-excel-data-using-integration-services/</link>
		<comments>http://blog.hoegaerden.be/2010/04/20/importing-excel-data-using-integration-services/#comments</comments>
		<pubDate>Tue, 20 Apr 2010 21:21:02 +0000</pubDate>
		<dc:creator>Valentino Vranken</dc:creator>
				<category><![CDATA[Integration Services]]></category>
		<category><![CDATA[SQLServerPedia Syndication]]></category>
		<category><![CDATA[data]]></category>
		<category><![CDATA[Excel]]></category>
		<category><![CDATA[SSIS]]></category>
		<category><![CDATA[Tutorial]]></category>

		<guid isPermaLink="false">http://blog.hoegaerden.be/2010/04/20/importing-excel-data-using-integration-services/</guid>
		<description><![CDATA[In a previous article I’ve shown you how to import data from an Excel sheet using the OPENROWSET() function.&#160; And I concluded by stating that it’s not the best option when automating your data import.
Today I’ll repeat the Excel data import process by using SQL Server Integration Services, also known as SSIS.
I’ll be using SQL [...]]]></description>
			<content:encoded><![CDATA[<p>In a previous article I’ve shown you <a title="Retrieving Data From Excel" href="http://blog.hoegaerden.be/2010/03/29/retrieving-data-from-excel/" target="_blank">how to import data from an Excel sheet using the OPENROWSET() function</a>.&#160; And I concluded by stating that it’s not the best option when automating your data import.</p>
<p>Today I’ll repeat the Excel data import process by using SQL Server Integration Services, also known as SSIS.</p>
<p>I’ll be using SQL Server 2008 R2, but I’m quite sure that the process is very similar to the first release of 2008, and even to 2005.&#160; The Excel file that I will be importing is the one used in my previous article, and I’ll also refer to some parts of that article, so you may want to have a read over that one when something here isn’t clear.</p>
<p>Furthermore I’m using a Windows 7 64-bit machine, with <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;amp;displaylang=ja&amp;displaylang=en" target="_blank">the ACE 14 driver</a> (beta) installed.&#160; To avoid any discussion about versions and for my own (future) reference, here’s the result of a SELECT @@VERSION:</p>
<blockquote><p>Microsoft SQL Server 2008 R2 (CTP) &#8211; 10.50.1352.12 (X64)&#160;&#160; Oct 30 2009 18:06:48&#160;&#160; Copyright (c) Microsoft Corporation&#160; Enterprise Evaluation Edition (64-bit) on Windows NT 6.1 &lt;X64&gt; (Build 7600: ) </p>
</blockquote>
<h2>Create SSIS Package To Import Excel Data</h2>
<p>Usually you will start by creating a new package in an Integration Services project, add an Excel source to a new Data Flow, throw in some Data Flow Transformations and end your flow with an OLE DB Destination connecting to your SQL Server.</p>
<p>But that’s not the approach that I’ll take in this article.&#160; I’ll make use of a shortcut (and meanwhile I’m showing you how well integrated some components really are).</p>
<p>First, I’m creating a new database called <em>ExcelImport</em>, using the Management Studio (aka SSMS).&#160; Once the database is created, right-click on it and choose Tasks &gt; Import Data.</p>
<p><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="Start the Import Data Wizard through Management Studio" border="0" alt="Start the Import Data Wizard through Management Studio" src="http://blog.hoegaerden.be/wp-content/uploads/image213.png" width="586" height="652" /> </p>
<p>This will open up the <a title="BOL 2008: Importing and Exporting Data by Using the SQL Server Import and Export Wizard" href="http://msdn.microsoft.com/en-us/library/ms141209.aspx" target="_blank">SQL Server Import And Export Data Wizard</a>.&#160; Like all good wizards, it starts with a <strong>Welcome</strong> screen containing an introductory text about its purpose – something about “create simple packages that import and export data between many popular data formats including databases, spreadsheets” – and so on.&#160; It also includes a checkbox that says “Do not show this starting page again”.&#160; That’s my favorite object on the page <img src='http://blog.hoegaerden.be/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> &#160; Okay, I agree, the page is useful for people who have never seen the wizard before and who may have opened it up by accident, but that’s about as far as its use goes methinks.</p>
<p>So, do whatever you like with the checkbox and click Next.&#160; That opens up the <strong>Choose a Data Source</strong> screen.</p>
<p><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="SQL Server Import and Export Wizard - Choose a Data Source" border="0" alt="SQL Server Import and Export Wizard - Choose a Data Source" src="http://blog.hoegaerden.be/wp-content/uploads/image214.png" width="566" height="579" /> </p>
<p>In that screen you have several options in the Data Source dropdown.&#160; The one we’re interested in is called <em>Microsoft Excel</em>.&#160; Once that option is selected, the controls further down the screen change into what is shown in the screenshot.&#160; Select your file and the right version, in my case I’ve got an Excel 2007 file.&#160; If your sheet contains a header row, activate the <em>First row has column names</em> checkbox.</p>
<p>Clicking Next will open up the <strong>Choose a Destination</strong> screen.</p>
<p><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="SQL Server Import and Export Wizard - Choose a Destination" border="0" alt="SQL Server Import and Export Wizard - Choose a Destination" src="http://blog.hoegaerden.be/wp-content/uploads/image215.png" width="566" height="579" /> </p>
<p>In that screen, select <em>Microsoft OLE DB Provider for SQL Server</em> as Destination.&#160; Ensure that your SQL Server instance is the right one in the <em>Server Name</em> dropdown and the <em>Authentication</em> is filled out as expected.&#160; The correct database should be selected by default because we did a right-click on it to start the wizard.</p>
<p>Another Next click opens up the <strong>Specify Table Copy or Query</strong> window.</p>
<p><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="SQL Server Import and Export Wizard - Specify Table Copy or Query" border="0" alt="SQL Server Import and Export Wizard - Specify Table Copy or Query" src="http://blog.hoegaerden.be/wp-content/uploads/image216.png" width="566" height="579" /> </p>
<p>Here we can choose between either retrieving the full table – all rows, all columns – or writing a query ourselves.&#160; I’ll go for the first option.</p>
<p>Click Next once more to open the <strong>Select</strong> <strong>Source Tables and Views</strong> screen.&#160; That name seems a bit weird in the context of an Excel import but I guess that’s not really important here.&#160; Just read it as “Select the sheet that you’d like to import”.</p>
<p><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="SQL Server Import and Export Wizard - Select Source Table and Views" border="0" alt="SQL Server Import and Export Wizard - Select Source Table and Views" src="http://blog.hoegaerden.be/wp-content/uploads/image217.png" width="567" height="579" />&#160;</p>
<p>I’ll go for the ProductList$ Source.&#160; The sheet in my Excel file is called ProductList.&#160; Note that the Destination is editable – I’m changing the destination table to dbo.ProductList (with the dollar sign removed).</p>
<p>If you’d like to view your data right now you can hit the Preview button.&#160; It opens up a window such as this one:</p>
<p><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="SQL Server Import and Export Wizard - Select Source Table and Views - Preview Data" border="0" alt="SQL Server Import and Export Wizard - Select Source Table and Views - Preview Data" src="http://blog.hoegaerden.be/wp-content/uploads/image218.png" width="528" height="338" />&#160; </p>
<p>That should look familiar.</p>
<p>A more interesting button is the one called <em>Edit Mappings</em>.&#160; Clicking that button opens a screen that lets you change the destination table’s schema.&#160; By default, all columns that seem to be numeric are mapped to a type of float and all the others are mapped to nvarchar with a length of 255.&#160; Depending on your situation you can either leave it as it is (in case you’re just loading a staging table and want to handle data conversions later on) or you should review each column (for instance when you’re erasing and loading the full table every night and this is the actual table being used by other processes).</p>
<p><strong>Note:</strong> whenever a column contains a blank cell, its type will be nvarchar(255), even when all other values are numeric.&#160; Also, if you don’t need Unicode support, don’t use nvarchar but change it to varchar instead.</p>
<p>Here’s the <strong>Column Mappings </strong>screen with some of the defaults changed:</p>
<p><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="The Column Mappings window" border="0" alt="The Column Mappings window" src="http://blog.hoegaerden.be/wp-content/uploads/image219.png" width="636" height="794" /> </p>
<p>Everything that I changed has been indicated using the yellow marker.&#160; I’ve changed some field types and lenghts, made one field non-nullable and adapted the destination name.</p>
<p>Clicking OK followed by Next brings us to the following screen, <strong>Review Data Type Mapping</strong>.</p>
<p><a href="http://blog.hoegaerden.be/wp-content/uploads/image220.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="SQL Server Import and Export Wizard - Review Data Type Mapping" border="0" alt="SQL Server Import and Export Wizard - Review Data Type Mapping" src="http://blog.hoegaerden.be/wp-content/uploads/image_thumb56.png" width="678" height="834" /></a> </p>
<p>This screen gives another overview of all the columns that we’re planning to import.&#160; Note that each column of which we’ve changed the type has gotten a nice yellow warning sign.&#160; This happens because a data conversion needs to occur, and there’s always something that can go wrong when converting data.&#160; The<em> On Error</em> and <em>On Truncation</em> columns show the action that should happen when such an event occurs.&#160; We’ll leave them all at <em>Use Global</em>.&#160; The Global settings are located at the bottom of the screen and are both set to <em>Fail</em>.&#160; That’s the best option at the moment (the only other one is <em>Ignore</em> but that means you won’t get any notification in case of an error or truncation problem).</p>
<p>After clicking Next we end up at the <strong>Save and Run Package</strong> window.</p>
<p><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="SQL Server Import and Export Wizard - Save and Run Package" border="0" alt="SQL Server Import and Export Wizard - Save and Run Package" src="http://blog.hoegaerden.be/wp-content/uploads/image221.png" width="644" height="579" /> </p>
<p>I have activated the <em>Save SSIS Package</em> checkbox and chose the <em>Do not save sensitive data</em> option.&#160; By default it is saved in SQL Server, which is actually the MSDB.&#160; That’s good because we’re going to examine the contents of the package later on so we want to keep it.</p>
<p>The <em>Run immediately </em>checkbox was activated by default.&#160; This will execute the package in the last step of the wizard.</p>
<p>Another Next click and we’re on the <strong>Save SSIS Package </strong>screen.</p>
<p><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="SQL Server Import and Export Wizard - Save SSIS Package" border="0" alt="SQL Server Import and Export Wizard - Save SSIS Package" src="http://blog.hoegaerden.be/wp-content/uploads/image222.png" width="566" height="579" /> </p>
<p>Here we can give our package a decent name, such as ExcelImport.&#160; You can also see the server on which I’m saving the package.</p>
<p>The final Next click brings us to the <strong>Complete the Wizard</strong> screen, woohoo!</p>
<p>&#160;<img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="SQL Server Import and Export Wizard - Complete the Wizard" border="0" alt="SQL Server Import and Export Wizard - Complete the Wizard" src="http://blog.hoegaerden.be/wp-content/uploads/image223.png" width="566" height="579" /> </p>
<p>We can see a short overview of what we’ve configured in the previous steps.&#160; Click Finish to execute and save the package!</p>
<p><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="SQL Server Import and Export Wizard - The execution was successful" border="0" alt="SQL Server Import and Export Wizard - The execution was successful" src="http://blog.hoegaerden.be/wp-content/uploads/image224.png" width="566" height="579" /> </p>
<p>And we’ve successfully executed the package!</p>
<h2>Taking A Closer Look At The SSIS Package</h2>
<p>When connecting to the Integration Services server (through SSMS for instance), I now have a new package in the root of the MSDB folder.</p>
<p><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="The ExcelImport SSIS package, stored in MSDB" border="0" alt="The ExcelImport SSIS package, stored in MSDB" src="http://blog.hoegaerden.be/wp-content/uploads/image225.png" width="269" height="171" /> </p>
<p>It’s located there because I chose to save the package to SQL Server.</p>
<h3>Adding An Existing Package To An SSIS Project</h3>
<p>We’re now going to open it in the Business Intelligence Development Studio (aka BIDS).&#160; So, open the BIDS and create a new SSIS Project (or use an existing one, doesn’t really matter).</p>
<p>Once the project is open, right-click the SSIS Packages folder in the Solution Explorer and select <em>Add Existing Package</em>.</p>
<p><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="Right-click SSIS Packages folder in Solution Explorer to Add Existing Package" border="0" alt="Right-click SSIS Packages folder in Solution Explorer to Add Existing Package" src="http://blog.hoegaerden.be/wp-content/uploads/image226.png" width="357" height="316" /> </p>
<p>That opens up the <em>Add Copy of Existing Package</em> window.</p>
<p><strong>Sidenote</strong>: do you see that <em>SSIS Import and Export Wizard</em> option in the previous screenshot?&#160; That’s right, the wizard that we’ve used extensively in the earlier part of this article can be launched from here as well.</p>
<p><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="Add Copy of Existing Package" border="0" alt="Add Copy of Existing Package" src="http://blog.hoegaerden.be/wp-content/uploads/image227.png" width="600" height="438" /> </p>
<p>Select <em>SSIS Package Store</em> as location of the package and enter the name of your server in the second dropdown.&#160; Once that is done you can click the button with the ellipsis and select your package under the MSDB node.</p>
<p>Clicking OK will add the package to your project in the Solution Explorer.&#160; Double-click it to open it up.</p>
<h3>The Control Flow</h3>
<p>In the Control Flow you can see two components: a SQL Task that contains a CREATE TABLE statement and a Data Flow.</p>
<p>Here’s what the CREATE TABLE statement looks like:</p>
<p><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="CREATE TABLE statement in the Execute SQL Task" border="0" alt="CREATE TABLE statement in the Execute SQL Task" src="http://blog.hoegaerden.be/wp-content/uploads/image228.png" width="336" height="479" /> </p>
<p>As you can see, the table is created using the column names and types just like we configured them through the wizard.</p>
<p>Important to note here is that the Control Flow does not take anything else into account.&#160; For instance, what happens if we execute the package twice?&#160; It will fail because the table already exists!</p>
<p>In case you don’t believe me, just try it out!</p>
<p><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="Execute SQL Statement failed" border="0" alt="Execute SQL Statement failed" src="http://blog.hoegaerden.be/wp-content/uploads/image229.png" width="167" height="149" />&#160;<img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="There is already an object named &#39;ProductList&#39; in the database." border="0" alt="There is already an object named &#39;ProductList&#39; in the database." src="http://blog.hoegaerden.be/wp-content/uploads/image230.png" width="861" height="144" />&#160;</p>
<h3>The Data Flow</h3>
<p>Opening up the Data Flow we see that it contains three components: an Excel source component that uses an Excel Connection Manager to connect to our now well-known Excel sheet, a Data Conversion Transformation to take care of the conversions that we requested and an OLE DB Destination that uses an OLE DB Connection Manager to connect to our SQL Server.</p>
<p><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="The Data Flow to transfer Excel data into SQL Server" border="0" alt="The Data Flow to transfer Excel data into SQL Server" src="http://blog.hoegaerden.be/wp-content/uploads/image232.png" width="165" height="227" /> </p>
<p>Important to note here is that whenever an issue occurs, such as a conversion problem, the flow will fail.</p>
<p>In production environments, certain events need to be taken into account.&#160; The purpose of this article was just to show you how you can use a wizard to generate an SSIS package for you.&#160; You can now use this package as the basis for a well-developed Excel Import template.</p>
<h2>Conclusion</h2>
<p>With this article I hope to have shown you how to use Integration Services to import Excel data, and also that the Management Studio knows how to use other SQL Server components, such as SSIS, quite well.</p>
<p>If you’re running into some issues while using the wizard, or you just like reading what I write, <a title="Importing Data Using The Wizard: Mixing The Wrong Ingredients" href="http://blog.hoegaerden.be/2010/05/03/importing-data-using-the-wizard-mixing-the-wrong-ingredients/">check out my follow-up article covering some common pitfalls</a>.</p>
<p>Need to go to sleep now, long drive tomorrow, PASS European Conference in Germany!&#160; I do hope that Adam Saxton will be there because I was planning to see his presentations all day long.&#160; I already read that one of the speakers – Brent Ozar – won’t be able to make it.&#160; Darned ash cloud…&#160; Next time someone starts talking to me about Azure I’ll run away screaming.</p>
<p>Just kidding <img src='http://blog.hoegaerden.be/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
<p>Have fun!</p>
<p>Valentino.</p>
<p><strong>References</strong></p>
<p><a title="Microsoft Support: How to import data from Excel to SQL Server" href="http://support.microsoft.com/kb/321686" target="_blank">Microsoft Support: How to import data from Excel to SQL Server</a></p>
</p>
<p><a title="BOL 2008 - How to: Run the SQL Server Import and Export Wizard" href="http://msdn.microsoft.com/en-us/library/ms140052.aspx" target="_blank">How to: Run the SQL Server Import and Export Wizard</a></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.hoegaerden.be/2010/04/20/importing-excel-data-using-integration-services/feed/</wfw:commentRss>
		<slash:comments>4</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 suite. Its [...]]]></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.&#160; I&#8217;ve even seen it used as a picture album.&#160; (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' /> )&#160; 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.&#160; He’s now putting his Photoshopped pictures in PowerPoint…&#160; 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.&#160; 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.&#160; The following screenshot contains an example, and is also the file that I will be using in this article.&#160; 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="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="An Excel sheet used as a data store" border="0" alt="An Excel sheet used as a data store" src="http://blog.hoegaerden.be/wp-content/uploads/image_thumb54.png" 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.&#160; 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.&#160; 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.&#160; This is a T-SQL function that can be used to access any OLE DB data source.&#160; All you need is the right OLE DB driver.&#160; 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>.&#160; This folder contains two files:<em> Products.xls</em> and <em>Products.xlsx</em>.&#160; The first file is saved in the old format, Excel 97-2003, while the second file was saved from Excel 2010.&#160; Both files contain the same data.&#160; 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.&#160; 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.&#160; 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.&#160; 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.&#160; 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.&#160; 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="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="The data types used when combining OPENROWSET with SELECT INTO" border="0" alt="The data types used when combining OPENROWSET with SELECT INTO" src="http://blog.hoegaerden.be/wp-content/uploads/image207.png" 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>.&#160; Not a lot of intelligence there.&#160; 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.&#160; What did I change?&#160; 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="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="SELECT INTO with some field types changed" border="0" alt="SELECT INTO with some field types changed" src="http://blog.hoegaerden.be/wp-content/uploads/image208.png" 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.&#160; And the datetime values are not recognized either.&#160; 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.&#160; 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.&#160; 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><font 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 &quot;Surface Area Configuration&quot; in SQL Server Books Online.</font></p>
</p>
</blockquote>
<p>This is one of the advanced options.&#160; 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><font color="#ff0000">The OLE DB provider &quot;Microsoft.Jet.OLEDB.4.0&quot; for linked server &quot;(null)&quot; reported an error. The provider did not give any information about the error.</font> </p>
</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 &quot;Microsoft.Jet.OLEDB.4.0&quot; for linked server &quot;(null)&quot;.</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.&#160; 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><font color="#ff0000">Cannot create an instance of OLE DB provider &quot;Microsoft.ACE.OLEDB.12.0&quot; for linked server &quot;(null)&quot;.</font></p>
</p>
</blockquote>
<p>To solve the issue, install the right driver and try again.</p>
<p>How can you tell what drivers are installed?&#160; 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.&#160; 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="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="odbcad32.exe - ODBC Data Source Administrator" border="0" alt="odbcad32.exe - ODBC Data Source Administrator" src="http://blog.hoegaerden.be/wp-content/uploads/image209.png" 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;amp;displaylang=ja&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.&#160; 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.&#160; 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.&#160; 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?&#160; I’ll start by saying that I had quite some issues getting OPENROWSET to work, but finally I managed it.&#160; Following is a list of my attempts, each time with the resulting message.&#160; And finally I’ll show you how I got it to work.&#160; 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.&#160; 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.&#160; 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><font color="#ff0000">The OLE DB provider &quot;Microsoft.ACE.OLEDB.14.0&quot; has not been registered.</font></p>
</p>
</blockquote>
<p>Is this because SSMS ships only in 32-bit?&#160; Maybe, but I’m not able to install the 32-bit driver.&#160; It doesn’t allow me to because I’ve got Office in 64-bit installed.&#160; The installer throws me the following error:</p>
<p><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-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..." 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..." src="http://blog.hoegaerden.be/wp-content/uploads/image210.png" 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.&#160; 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><font color="#ff0000">The OLE DB provider &quot;Microsoft.ACE.OLEDB.12.0&quot; for linked server &quot;(null)&quot; reported an error. The provider did not give any information about the error.</font> </p>
</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 &quot;Microsoft.ACE.OLEDB.12.0&quot; for linked server &quot;(null)&quot;.</span></p>
</blockquote>
<p>The Results pane shows all the columns with the right column names, retrieved from Excel.&#160; 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="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="Using sqlcmd 64-bit to query Excel" border="0" alt="Using sqlcmd 64-bit to query Excel" src="http://blog.hoegaerden.be/wp-content/uploads/image_thumb55.png" width="688" height="377" /></a></p>
<p>I actually expected this last method to work, after all, everything is now running in 64-bit.&#160; 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.&#160; 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.&#160; 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.&#160; This made the weird behavior disappear.&#160; So for some reason SQL Server doesn’t like the OPENROWSET function combined with comments inside the query.&#160; 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!&#160; For now my conclusion is: don’t use comments when creating an OPENROWSET query.</p>
<p>&#160;</p>
<p><strong><u>IMPORTANT UPDATE (April 11, 2010)</u>: 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” .&#160; This explains some of the issues shown above.&#160; 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 &#39;Microsoft.ACE.OLEDB.14.0&#39; 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>
<p>&#160;</p>
<h2>Conclusion</h2>
<p>The above has shown that OPENROWSET() can be a useful function, given the right circumstances.&#160; 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.&#160; I would not use it for an automated import process.&#160; 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>
]]></content:encoded>
			<wfw:commentRss>http://blog.hoegaerden.be/2010/03/29/retrieving-data-from-excel/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
	</channel>
</rss>
