<?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>Purple Frog Systems</title>
	<atom:link href="http://www.purplefrogsystems.com/blog/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.purplefrogsystems.com/blog</link>
	<description>Business Intelligence Consultancy</description>
	<lastBuildDate>Tue, 15 May 2012 11:49:31 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>SQL Relay Agenda Announced</title>
		<link>http://www.purplefrogsystems.com/blog/2012/04/sql-relay-agenda-announced/</link>
		<comments>http://www.purplefrogsystems.com/blog/2012/04/sql-relay-agenda-announced/#comments</comments>
		<pubDate>Thu, 19 Apr 2012 09:34:50 +0000</pubDate>
		<dc:creator>Alex</dc:creator>
				<category><![CDATA[Community]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[Conference]]></category>
		<category><![CDATA[SQLBits]]></category>
		<category><![CDATA[User Group]]></category>

		<guid isPermaLink="false">http://www.purplefrogsystems.com/blog/?p=564</guid>
		<description><![CDATA[SQL Relay: Free, Full Day SQL Server Events If you&#8217;ve not heard the news yet, then where have you been hiding?! With SQLBits X such a phenomenal success, but already a distant memory, we&#8217;re all looking for the next SQL Server community event to learn, network and enjoy. The 2012 SQL Relay, following on from [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.SQLServerFaq.com"><img src="http://www.purplefrogsystems.com/blog/wp-content/uploads/2012/04/SQLRelay2012-200x70.png" alt="" title="SQLRelay2012" width="200" height="70" class="alignright size-full wp-image-573" /></a><br />
<h2>SQL Relay: Free, Full Day SQL Server Events</h2>
<p>If you&#8217;ve not heard the news yet, then where have you been hiding?! With SQLBits X such a phenomenal success, but already a distant memory, we&#8217;re all looking for the next SQL Server community event to learn, network and enjoy.<br />
The 2012 SQL Relay, following on from the great inaugural events last year, have now been announced and registration is open.</p>
<h2>Where are they?</h2>
<ul>
<li>Monday 21st May &#8211; <a href="http://sqlserverfaq.com/default.aspx?item=event&amp;itemid=378">Edinburgh</a></li>
<li>Tuesday 22nd May &#8211; <a href="http://sqlserverfaq.com/default.aspx?item=event&amp;itemid=373">Manchester</a></li>
<li>Wednesday 23rd May &#8211; <a href="http://sqlserverfaq.com/default.aspx?item=event&amp;itemid=357">Birmingham</a></li>
<li>Thursday 24th May &#8211; <a href="http://sqlserverfaq.com/default.aspx?item=event&amp;itemid=391">Bristol</a></li>
<li>Wednesday 30th May &#8211; <a href="http://sqlserverfaq.com/default.aspx?item=event&amp;itemid=377">London</a></li>
</ul>
<h2>Who&#8217;s speaking?</h2>
<p>Each event is different, but with a common theme. The awesome line-up of speakers include:</p>
<ul>
<li>Morris Novello, Microsoft, SQL Server Product Manager</li>
<li>Andrew Fryer, Microsoft, Technical Evangelist [<a href="http://blogs.technet.com/b/andrew/">blog</a>|<a href="http://twitter.com/deepfat">twitter</a>]</li>
<li>Allan Mitchell, MVP [<a href="http://http://www.sqlis.com/">blog</a>|<a href="http://twitter.com/allansqlis">twitter</a>]</li>
<li>Chris Testa O&#8217;Neill, MVP [<a href="http://sqlblogcasts.com/blogs/testas/">blog</a>|<a href="http://twitter.com/ctesta_oneill">twitter</a>]</li>
<li>Chris Webb, MVP [<a href="http://cwebbbi.wordpress.com/">blog</a>|<a href="http://twitter.com/technitrain">twitter</a>]</li>
<li>Martin Bell, MVP [<a href="http://sqlblogcasts.com/blogs/martinbell/">blog</a>]</li>
<li>Jamie Thompson, MVP [<a href="http://sqlblog.com/blogs/jamie_thomson/">blog</a>|<a href="http://twitter.com/jamiet">twitter</a>]</li>
<li>Tony Rogerson, MVP [<a href="http://sqlblogcasts.com/blogs/tonyrogerson/">blog</a>|<a href="http://twitter.com/tonyrogerson">twitter</a>]</li>
</ul>
<p>As well as sessions from Microsoft partners and sponsors.</p>
<h2>How big are these events?</h2>
<p>Each of the 5 venues have different capacities, but in the region of 100-120 attendees per event.</p>
<h2>What&#8217;s the format?</h2>
<p>Each event has a morning session, in which the Microsoft speakers and partners will cover the technical challenges facing IT, and an overview of SQL 2012 and its new features that can help.<br />
The afternoon sessions will be technical deep dives by the selection of World class MVPs.<br />
Some events will also have an evening session, which will extend the technical sessions until 9pm, to cater for those who can&#8217;t get time off work!</p>
<p>You can come to 1, 2 or all 3 sessions, just mark your preference during registration.</p>
<h2>How can this be free?!</h2>
<p>Thanks to the kind generosity of our sponsors, who include:</p>
<table width="50%">
<tbody>
<tr>
<td>Gold Sponsor</td>
<td><a href="http://www.microsoft.com" target="_blank">Microsoft</a></td>
</tr>
<tr>
<td>Silver Sponsor</td>
<td><a href="http://http://www.fusionio.com" target="_blank">Fusion IO</a></td>
</tr>
<tr>
<td></td>
<td><a href="http://www.quest-software.co.uk" target="_blank">Quest</a></td>
</tr>
<tr>
<td></td>
<td><a href="http://www.red-gate.com" target="_blank">RedGate</a></td>
</tr>
<tr>
<td></td>
<td><a href="http://strategycompanion.com" target="_blank">Strategy Companion</a></td>
</tr>
<tr>
<td>Bronze Sponsor</td>
<td><a href="http://www.microgen.com/uk-en/" target="_blank">Microgen</a></td>
</tr>
<tr>
<td></td>
<td><a href="http://www.PurpleFrogSystems.com" target="_blank">Purple Frog</a></td>
</tr>
<tr>
<td></td>
<td><a href="http://www.technitrain.com" target="_blank">Technitrain</a></td>
</tr>
<tr>
<td></td>
<td><a href="http://www.wrox.com/WileyCDA/" target="_blank">Wrox</a></td>
</tr>
</tbody>
</table>
<h2>Say no more&#8230; Sign me up!</h2>
<p>Thought you&#8217;d say that &#8211; register at <a href="http://www.SQLServerFAQ.com">SQLServerFAQ.com</a></p>
<p>I&#8217;m delighted to be running the Birmingham event, so look forward to seeing you there!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.purplefrogsystems.com/blog/2012/04/sql-relay-agenda-announced/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Automating T-SQL Merge to load Dimensions (SCD)</title>
		<link>http://www.purplefrogsystems.com/blog/2012/04/automating-t-sql-merge-to-load-dimensions-scd/</link>
		<comments>http://www.purplefrogsystems.com/blog/2012/04/automating-t-sql-merge-to-load-dimensions-scd/#comments</comments>
		<pubDate>Fri, 06 Apr 2012 10:55:38 +0000</pubDate>
		<dc:creator>Alex</dc:creator>
				<category><![CDATA[Business Intelligence]]></category>
		<category><![CDATA[Integration Services]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[Data Warehouse]]></category>
		<category><![CDATA[ETL]]></category>
		<category><![CDATA[Merge]]></category>
		<category><![CDATA[SQLBits]]></category>
		<category><![CDATA[SSIS]]></category>

		<guid isPermaLink="false">http://www.purplefrogsystems.com/blog/?p=546</guid>
		<description><![CDATA[This is the 3rd post in the Frog-Blog series on the awesomeness of T-SQL Merge. Post 1: Introduction to T-SQL merge basics Post 2: Using T-SQL merge to load data warehouse dimensions In this post we&#8217;ll be looking at how we can automate the creation of the merge statement to reduce development time and improve [...]]]></description>
			<content:encoded><![CDATA[<p>This is the 3rd post in the Frog-Blog series on the awesomeness of T-SQL Merge.</p>
<ul>
<li>Post 1: <a href="/blog/2011/12/introduction-to-t-sql-merge-basics/">Introduction to T-SQL merge basics</a></li>
<li>Post 2: <a href="/blog/2012/01/using-t-sql-merge-to-load-data-warehouse-dimensions/">Using T-SQL merge to load data warehouse dimensions</a></li>
</ul>
<p>In this post we&#8217;ll be looking at how we can automate the creation of the merge statement to reduce development time and improve reliability and flexibility of the ETL process. I discussed this in the 2nd half of a talk I gave at the UK technical launch of SQL Server 2012 at <a href="http://www.SQLBits.com" target="_blank">SQLBits X</a>. Thank you to the great audience who came to that talk, this post is for your benefit and is a result of the feedback and requests from you guys.</p>
<h2>Why automate merge?</h2>
<p>As we saw in the <a href="/blog/2012/01/using-t-sql-merge-to-load-data-warehouse-dimensions/">previous post</a>, merge is an incredibly powerful tool when loading data into data warehouse dimensions (specifically SCDs &#8211; slowly changing dimensions). The whole process can be wrapped up into a very neat stored proc which can save a considerable mount of time writing the equivalent functionality in SSIS. In the next installment of this series I&#8217;ll be discussing the performance of it compared to other methods of loading SCDs in SSIS (take a look at the SQLBits talk video [when it's released] for a preview!). Suffice to say for now that in my [pretty comprehensive] tests it&#8217;s one of the fastest methods of loading SCDs.</p>
<p>If you missed the talk, you can <a href="/download/blog/LoadingSCDsInSSIS_SQLBitsX.pdf" target="_blank">download the slide deck here</a> whilst you&#8217;re waiting for the video.</p>
<p>The problem that stops a lot of people using merge is the perceived complexity of the statement. It can be very easy to get things wrong, with pretty bad consequences on your dimension data.</p>
<p>The easiest way to avoid this complexity and simplify the process is to not write merge statements, but let an automated procedure to it for you &#8211; Simples!.</p>
<p>The other huge benefit is that, as we&#8217;ll see during this post, you can base the automation procedure on metadata, meaning that you can change the SCD functionality of your dimensions just by changing metadata, and not rewriting your code.</p>
<p>Note that in this post we&#8217;ll just be looking at <a href="http://en.wikipedia.org/wiki/Slowly_changing_dimension" target="_blank">Type 0 and 1 SCDs, not 2, 3 or 6</a>. This is to keep things simple. Once you&#8217;ve mastered type 0 and 1, it&#8217;s a logical next step to expand things to deal with type 2s.</p>
<h2>OK, so how do we do this?</h2>
<p>First of all we need to set up two tables to use. Let&#8217;s create a simple Customer dimension. Alongside this we also need a staging table. I&#8217;m a big fan of using schemas to differentiate tables, so we&#8217;ll create dim.Customer and etl.Customer as our two tables.</p>
<pre class="code">CREATE SCHEMA [dim] AUTHORIZATION [dbo]
GO
CREATE SCHEMA [etl] AUTHORIZATION [dbo]
GO

CREATE TABLE [dim].[Customer](
    [CustomerKey]   [int] IDENTITY(1,1) NOT NULL,
    [Email]         [varchar](255)      NOT NULL,
    [FirstName]     [varchar](50)       NOT NULL,
    [LastName]      [varchar](50)       NOT NULL,
    [DoB]           [date]              NOT NULL,
    [Sex]           [char](1)           NOT NULL,
    [MaritalStatus] [varchar](10)       NOT NULL,
    [FirstCreated]  [date]              NOT NULL,
    [IsRowCurrent]  [bit]               NOT NULL,
    [ValidFrom]     [datetime]          NOT NULL,
    [ValidTo]       [datetime]          NOT NULL,
    [LastUpdated]   [datetime]          NOT NULL
 CONSTRAINT [PK_DimCustomer] PRIMARY KEY CLUSTERED
(
	[CustomerKey] ASC
))
GO

CREATE TABLE [etl].[Customer](
    [Email]         [varchar](255)  NOT NULL,
    [FirstName]     [varchar](50)   NOT NULL,
    [LastName]      [varchar](50)   NOT NULL,
    [DoB]           [date]          NOT NULL,
    [Sex]           [char](1)       NOT NULL,
    [MaritalStatus] [varchar](10)   NOT NULL,
    [FirstCreated]  [date]          NOT NULL
)</pre>
<p>So the dim table contains our primary surrogate key, business key (email address in this case), customer details and a series of audit fields (IsRowCurrent, ValidFrom, etc.). The etl staging table only contains the business key and customer details.</p>
<p>We then need to store the details of each field. i.e. how should each field be interpreted &#8211; is it a primary key, business, key, type 0 or 1, or an audit field. We need this so that we can put the correct fields into the correct place in the merge statement. You could create a table to store this information, however I prefer to use the extended properties of the fields.</p>
<pre class="code">EXEC sys.sp_addextendedproperty @level2name=N'CustomerKey',  @value=N'PK' ,
    @name=N'SCD', @level0type=N'SCHEMA',@level0name=N'Dim',
    @level1type=N'TABLE',@level1name=N'Customer', @level2type=N'COLUMN'
EXEC sys.sp_addextendedproperty @level2name=N'Email',        @value=N'BK' ,
    @name=N'SCD', @level0type=N'SCHEMA',@level0name=N'Dim',
    @level1type=N'TABLE',@level1name=N'Customer', @level2type=N'COLUMN'
EXEC sys.sp_addextendedproperty @level2name=N'FirstName',    @value=N'1' ,
    @name=N'SCD', @level0type=N'SCHEMA',@level0name=N'Dim',
    @level1type=N'TABLE',@level1name=N'Customer', @level2type=N'COLUMN'
EXEC sys.sp_addextendedproperty @level2name=N'LastName',     @value=N'1' ,
    @name=N'SCD', @level0type=N'SCHEMA',@level0name=N'Dim',
    @level1type=N'TABLE',@level1name=N'Customer', @level2type=N'COLUMN'
EXEC sys.sp_addextendedproperty @level2name=N'DoB',          @value=N'1' ,
    @name=N'SCD', @level0type=N'SCHEMA',@level0name=N'Dim',
    @level1type=N'TABLE',@level1name=N'Customer', @level2type=N'COLUMN'
EXEC sys.sp_addextendedproperty @level2name=N'Sex',          @value=N'1' ,
    @name=N'SCD', @level0type=N'SCHEMA',@level0name=N'Dim',
    @level1type=N'TABLE',@level1name=N'Customer', @level2type=N'COLUMN'
EXEC sys.sp_addextendedproperty @level2name=N'MaritalStatus',@value=N'1' ,
    @name=N'SCD', @level0type=N'SCHEMA',@level0name=N'Dim',
    @level1type=N'TABLE',@level1name=N'Customer', @level2type=N'COLUMN'
EXEC sys.sp_addextendedproperty @level2name=N'FirstCreated', @value=N'1' ,
    @name=N'SCD', @level0type=N'SCHEMA',@level0name=N'Dim',
    @level1type=N'TABLE',@level1name=N'Customer', @level2type=N'COLUMN'
EXEC sys.sp_addextendedproperty @level2name=N'ValidFrom',    @value=N'Audit' ,
    @name=N'SCD', @level0type=N'SCHEMA',@level0name=N'Dim',
    @level1type=N'TABLE',@level1name=N'Customer', @level2type=N'COLUMN'
EXEC sys.sp_addextendedproperty @level2name=N'ValidTo',      @value=N'Audit' ,
    @name=N'SCD', @level0type=N'SCHEMA',@level0name=N'Dim',
    @level1type=N'TABLE',@level1name=N'Customer', @level2type=N'COLUMN'
EXEC sys.sp_addextendedproperty @level2name=N'IsRowCurrent', @value=N'Audit' ,
    @name=N'SCD', @level0type=N'SCHEMA',@level0name=N'Dim',
    @level1type=N'TABLE',@level1name=N'Customer', @level2type=N'COLUMN'
EXEC sys.sp_addextendedproperty @level2name=N'LastUpdated',  @value=N'Audit' ,
    @name=N'SCD', @level0type=N'SCHEMA',@level0name=N'Dim',
    @level1type=N'TABLE',@level1name=N'Customer', @level2type=N'COLUMN'</pre>
<p>Or you can obviously just enter the extended property manually using SSMS</p>
<p><img class="aligncenter size-full wp-image-553" title="SCDExtendedProperty" src="http://www.purplefrogsystems.com/blog/wp-content/uploads/2012/04/SCDExtendedProperty.png" alt="" width="600" height="415" /></p>
<p>The SSIS package should output all customer records into the etl table, with no regard for whether they are new customers, old customers, changed or not. The merge statement will take care of that.</p>
<h2>Automating Merge</h2>
<p>The first stage is to examine the structure of merge.</p>
<pre>   MERGE   <span style="color: #ff0000;">[DIMENSION TABLE]</span>  as Target
   USING   <span style="color: #ff0000;">[STAGING TABLE]</span>    as Source
      ON   <span style="color: #ff0000;">[LIST OF BUSINESS KEY FIELDS]</span>
   WHEN MATCHED AND
         Target.<span style="color: #ff0000;">[LIST OF TYPE 1 FIELDS]</span> &lt;&gt; Source.<span style="color: #ff0000;">[LIST OF TYPE 1 FIELDS]</span>
      THEN UPDATE SET
         <span style="color: #ff0000;">[LIST OF TYPE 1 FIELDS]</span> = Source.<span style="color: #ff0000;">[LIST OF TYPE 1 FIELDS]</span>
   WHEN NOT MATCHED THEN INSERT
         <span style="color: #ff0000;">[LIST OF ALL FIELDS]</span>
      VALUES
         Source.<span style="color: #ff0000;">[LIST OF ALL FIELDS]</span>
</pre>
<p>The text in black is the skeleton of the statement, with the text in red being the details specific to the dimension. It&#8217;s these red items which we need to retrieve from the metadata of the dimension in order to create the full merge statement.</p>
<p>We can retrieve the extended properties using the sys.extended_properties DMV. This allows us to pull out a list of all fields which have a specific extended property set, e.g. all PK fields, all BK fields, all type 2 fields etc. etc. If we then put a few of these queries into cursors, we can loop through them and build up a dynamic SQL query. Yes I know, dynamic SQL should be avoided and is evil etc., however&#8230; this use is an exception and does truly make the World a better place.</p>
<p>I&#8217;m not going to explain the resulting proc in minute detail, so instead please just <a href="/download/blog/GenerateMerge.sql" target="_blank">download it here</a> and work through it yourself. I will however explain a couple of items which are pretty important:</p>
<p>It&#8217;s important to keep the naming convention of your dimensions consistent. This doesn&#8217;t mean that every dimension must be identical, some may need inferred member support, some may need type 2 tracking fields (e.g. IsRowCurrent) and some may not; the critical thing is that all of your fields, if they do exist, should be named consistently. The automation proc can then look for specific field names and include them in the merge statement if necessary.</p>
<p>There is a parameter in the proc called @Execute. This offers the possibility of either executing the resulting merge statement directly, or just printing out the statement. If you only want to use this to automate the development process then this allows you to do just that, you can then just copy and paste the resulting statement into SSIS or into a stored proc.</p>
<h2>Result</h2>
<p>The automated generation of T-SQL merge statement to handle type 0 &#038; 1 SCDs!<br />
Hopefully you can see how you can expand this to also cope with Type 2 SCDs, following the structure in my earlier posts.</p>
<p><a href="/download/blog/GenerateMerge.sql" target="_blank">Download the SQL scripts here</a><br />
&nbsp;<br />
Frog-Blog Out</p>
]]></content:encoded>
			<wfw:commentRss>http://www.purplefrogsystems.com/blog/2012/04/automating-t-sql-merge-to-load-dimensions-scd/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Map Postcodes in SSRS Reporting Services</title>
		<link>http://www.purplefrogsystems.com/blog/2012/03/map-postcodes-in-ssrs-reporting-services/</link>
		<comments>http://www.purplefrogsystems.com/blog/2012/03/map-postcodes-in-ssrs-reporting-services/#comments</comments>
		<pubDate>Mon, 05 Mar 2012 18:45:09 +0000</pubDate>
		<dc:creator>Alex</dc:creator>
				<category><![CDATA[Reporting Services]]></category>
		<category><![CDATA[Map]]></category>
		<category><![CDATA[Spatial]]></category>
		<category><![CDATA[SSRS]]></category>

		<guid isPermaLink="false">http://www.purplefrogsystems.com/blog/?p=528</guid>
		<description><![CDATA[I was asked the other day for some help on how to plot data by postcode on an SSRS spatial map. I&#8217;ve done this a few times, initially for a presentation I gave back in 2010 and most recently a couple of weeks ago when I wanted to analyse the SQLMidlands membership. It occurred to [...]]]></description>
			<content:encoded><![CDATA[<p><img class="alignright size-full wp-image-536" title="Frog-Cartoon-World_web" src="http://www.purplefrogsystems.com/blog/wp-content/uploads/2012/03/Frog-Cartoon-World_web.png" alt="" width="150" height="113" />I was asked the other day for some help on how to plot data by postcode on an SSRS spatial map. I&#8217;ve done this a few times, initially for a presentation I gave back in 2010 and most recently a couple of weeks ago when I wanted to analyse the <a href="http://www.SQLMidlands.com" target="_blank">SQLMidlands</a> membership. It occurred to me that despite posting a number of <a href="http://www.purplefrogsystems.com/blog/tag/spatial/">blogs using spatial data</a>, I&#8217;ve not done one on the basics of plotting postcode data. So here we go&#8230;</p>
<h2>Scenario</h2>
<p>You have a database containing customers, members, visitors, contacts, etc., each of which has a postcode. You want a simple map which shows where these are. Something like this&#8230;</p>
<p><img class="aligncenter size-full wp-image-534" title="SSRSSpatialMap" src="http://www.purplefrogsystems.com/blog/wp-content/uploads/2012/03/SSRSSpatialMap.jpg" alt="" width="600" height="430" /></p>
<h2>How do we do it?</h2>
<p>Firstly, the Reporting Services map component doesn&#8217;t understand postcodes, so we have to convert postcodes to latitude and longitude.</p>
<p>There are a number of ways of doing this, some more costly and/or time consuming than others. There are datasets that you can buy, or the Ordnance Survey now provide a <a href="https://www.ordnancesurvey.co.uk/opendatadownload/products.html" target="_blank">free download of postcode and other mapping data</a>. Read <a href="http://jamiethompson.co.uk/web/2008/07/24/full-uk-postcode-database-for-free/" target="_blank">Jamie Thompson&#8217;s blog</a> (No, not the SSIS-Junkie, another one!) for details on how to use it. These datasets provide full 7 digit postcode resolution, which means you can plot at the street level (approx 2 million different locations).</p>
<p>For a number of purposes this is excessive, and using a simpler 4 digit postcode is more than adequate, which breaks the UK down into about 3000 areas. For this you can download the details <a href="http://www.freemaptools.com/download-uk-postcode-lat-lng.htm" target="_blank">here</a>.</p>
<p>We can then use the spatial functionality introduced in SQL Server 2008 (and R2) to convert the lat/long into a geography data type, which can be displayed natively in an SSRS report.</p>
<h2>Convert Postcode to Latitude Longitude</h2>
<p>Assuming you&#8217;ve loaded the dataset above into a database table, we can join this to our existing table of customers. As our customer table has a full 7 digit postcode but our postcode table only has 4 digit, we need to simplify our customer postcode down to take only those characters before the space (convert SY1 1AA into SY1). We can do this using a LEFT and CHARINDEX, as per the code below.</p>
<pre class="code">   CASE WHEN
         --If the postcode doesn't contain a space, it isn't valid
         --So ignore it and return an empty string
      CHARINDEX(' ', Postcode)=0 THEN ''
         --If it does contain a space, only take the characters
         --up to (and not including) the space
      ELSE LEFT(Postcode, CHARINDEX(' ', Postcode)-1)
   END</pre>
<p>We can then join this data to the postcode table to link the lat/long to each customer. I&#8217;ve saved the postcode list in a table called PostcodeOuter.</p>
<pre class="code">   SELECT *
   FROM Customer c
      INNER JOIN PostcodeOuter p ON p.outcode =
         CASE WHEN
            CHARINDEX(' ', c.Postcode)=0 THEN ''
            ELSE LEFT(c.Postcode, CHARINDEX(' ', c.Postcode)-1)
           END</pre>
<p>Latitude and Longitude aren&#8217;t quite enough for Reporting Services to be able to display. We need to convert them into the internal SQL Server Geography data type. We do this with the geography::STPointFromText function.</p>
<pre class="code">   geography::STPointFromText('POINT(' +
      CAST(longitude AS VARCHAR(20)) + ' ' +
      CAST(latitude AS VARCHAR(20)) + ')', 4326)</pre>
<p>Note that the 4326 is the Spatial Reference Identifier (SRID), 4326 is the default &#8211; you can just leave this as it is.</p>
<p>We now want to join all of this together, and group the results by the post code outer code, so we can count the number of customers in each code.</p>
<pre class="code">   SELECT
       p.outcode
      ,geography::STPointFromText('POINT(' +
         CAST(MAX(p.lng) AS VARCHAR(20)) + ' ' +
         CAST(MAX(p.lat) AS VARCHAR(20)) + ')', 4326) AS Geog
      ,COUNT(*) AS CustomerCount
   FROM Customer c
      INNER JOIN PostcodeOuter p ON p.outcode =
         CASE WHEN
            CHARINDEX(' ', c.Postcode)=0 THEN ''
            ELSE LEFT(c.Postcode, CHARINDEX(' ', c.Postcode)-1)
           END
   GROUP BY p.outcode</pre>
<p>Note that as we&#8217;re grouping the results, we have to aggregate the lat and long using max. We could aggregate the resulting geography data type, but doing it at the lat/long level reduces the number of geography calculations that have to be performed, improving performance. You can run this in Management Studio. Notice that you should now have an extra results tab, Spatial Results. Clicking on this will show you a preview of your data. The dots can be hard to see, so you can add .STBuffer(5000) to make the dots larger. You should end up with geography:STPointFromText(&#8230;.., 4326).STBuffer(5000) AS Geog</p>
<p><img class="aligncenter size-full wp-image-535" title="SSRSSpatialMap2" src="http://www.purplefrogsystems.com/blog/wp-content/uploads/2012/03/SSRSSpatialMap2.jpg" alt="" width="600" height="462" /></p>
<p>We can now put this query directly into SSRS and show the data on a map.</p>
<h2>Displaying Spatial Data in SSRS</h2>
<p>Firstly load up BIDS, load or create a Report Server Project, and then add a new report (don&#8217;t bother using the wizard).<br />
Add a data source to your database. Use the query above as the dataset (obviously with the fields and tables modified to suit your database!).<br />
From the Toolbox, drag a Map report item onto your report. This will start the Map wizard. Use the following details for each stage:</p>
<ul>
<li>Choose &#8216;SQL Server spatial query&#8217;</li>
<li>Select the dataset that we just created</li>
<li>Select the Geog field in the first box, and Point in the second. Also make sure that you tick the box at the bottom &#8211; &#8216;Add a Bing Maps layer&#8217;, Selecting whether you want the map layer to show Road, Aerial or Hybrid</li>
<li>We want the size of each marker to change depending on the number of customers in that postcode, so select the Bubble Map</li>
<li>Select the same dataset that we created earlier</li>
<li>Tick the &#8216;Use bubble sizes to vizualize data&#8217; option, and select [Sum(CustomerCount)]</li>
<li>Lets also change the colour of the markers to make it even clearer. Tick the &#8216;Use bubble colors to visualize data&#8217; box and select [Sum(CustomerCount] and Red-Green as the color rule</li>
</ul>
<p><img class="aligncenter size-full wp-image-542" title="SSRSSpatialMap4" src="http://www.purplefrogsystems.com/blog/wp-content/uploads/2012/03/SSRSSpatialMap4.jpg" alt="" width="400" height="293" /></p>
<p>And there you have it, you can run the report and see your map. Not that tricky huh?<br />
You can then play around with the settings to change the look and feel, a good one is to set the Map layer transparency to 50, which makes the markers stand out more. But this is up to you.</p>
<p><img class="aligncenter size-full wp-image-541" title="SSRSSpatialMap3" src="http://www.purplefrogsystems.com/blog/wp-content/uploads/2012/03/SSRSSpatialMap3.jpg" alt="" width="600" height="415" /></p>
<p>There&#8217;s plenty more information on the internet about spatial data, both on the Frog-Blog and elsewhere. I&#8217;d recommend taking a look at <a href="http://alastaira.wordpress.com/" target="_blank">Alistair Aitchison&#8217;s Spatial blog</a> for some really interesting stuff to do using spatial data.</p>
<p>Thanks to Stephen Bennett for inspiring this post.</p>
<p>Frog-Blog-Out</p>
]]></content:encoded>
			<wfw:commentRss>http://www.purplefrogsystems.com/blog/2012/03/map-postcodes-in-ssrs-reporting-services/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>SQLBits X talk &#8211; Loading Data Warehouse Dimensions</title>
		<link>http://www.purplefrogsystems.com/blog/2012/02/sqlbits-x-talk-loading-data-warehouse-dimensions/</link>
		<comments>http://www.purplefrogsystems.com/blog/2012/02/sqlbits-x-talk-loading-data-warehouse-dimensions/#comments</comments>
		<pubDate>Mon, 20 Feb 2012 14:13:38 +0000</pubDate>
		<dc:creator>Alex</dc:creator>
				<category><![CDATA[Community]]></category>
		<category><![CDATA[Conference]]></category>
		<category><![CDATA[SQLBits]]></category>
		<category><![CDATA[User Group]]></category>

		<guid isPermaLink="false">http://www.purplefrogsystems.com/blog/?p=520</guid>
		<description><![CDATA[I&#8217;m thrilled to have been asked to present another session at SQLBits X, especially so as it&#8217;s set to be the biggest and best Bits yet. It&#8217;s the official UK launch of SQL Server 2012, and as such there&#8217;s an impressive collection of Microsoft folk attending and presenting, alongside the MVPs and internationally renowned speakers [...]]]></description>
			<content:encoded><![CDATA[<p><img class="alignleft" src="http://www.sqlbits.com/images/SQLBits/IveSubmmitted.png" alt="" width="151" height="103" />I&#8217;m thrilled to have been asked to present another session at SQLBits X, especially so as it&#8217;s set to be the biggest and best Bits yet.</p>
<p>It&#8217;s the official UK launch of SQL Server 2012, and as such there&#8217;s an impressive collection of Microsoft folk attending and presenting, alongside the MVPs and internationally renowned speakers that we now come to expect at SQL Bits.</p>
<p>The Saturday is now sold out, but (as of writing this) there are still some places available on the Thursday and Friday, but these are unlikely to stay available for long so head to <a href="http://www.SQLBits.com">www.SQLBits.com</a> and get your name down asap.</p>
<h2>Loading Data Warehouse Dimensions</h2>
<p>The session that I&#8217;ll be presenting will cover different techniques of using SSIS to load data into data warehouse dimensions. Specifically a performance comparison of the methods when loading Type 2 slowly changing dimensions, as well as showing the impact of using solid state storage such as Fusion IO.</p>
<p>I&#8217;ll also be covering the details of how to use the T-SQL Merge statement to take care loading dimensions, and showing why it&#8217;s now my preferred approach.</p>
<h2>SQL User Group Community</h2>
<p>I&#8217;ll also be there representing <a href="http://www.SQLMidlands.com">SQLMidlands</a>, a SQL Server user group in Birmingham. User group leaders from around the UK will be presenting a session on Saturday lunchtime, as well as staffing the community corner.</p>
<p>If you&#8217;re interested in getting involved with your local user group, if you just want to find out more, or are at your first SQLBits and want a friendly face to chat to &#8211; find me &amp; the other UG leaders at the community corner.</p>
<p>Look forward to seeing you there!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.purplefrogsystems.com/blog/2012/02/sqlbits-x-talk-loading-data-warehouse-dimensions/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Using T-SQL Merge to load Data Warehouse dimensions</title>
		<link>http://www.purplefrogsystems.com/blog/2012/01/using-t-sql-merge-to-load-data-warehouse-dimensions/</link>
		<comments>http://www.purplefrogsystems.com/blog/2012/01/using-t-sql-merge-to-load-data-warehouse-dimensions/#comments</comments>
		<pubDate>Mon, 16 Jan 2012 17:56:05 +0000</pubDate>
		<dc:creator>Alex</dc:creator>
				<category><![CDATA[Business Intelligence]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[Data Warehouse]]></category>
		<category><![CDATA[ETL]]></category>
		<category><![CDATA[Merge]]></category>
		<category><![CDATA[SCD]]></category>
		<category><![CDATA[SSIS]]></category>

		<guid isPermaLink="false">http://www.purplefrogsystems.com/blog/?p=499</guid>
		<description><![CDATA[In my last blog post I showed the basic concepts of using the T-SQL Merge statement, available in SQL Server 2008 onwards. In this post we&#8217;ll take it a step further and show how we can use it for loading data warehouse dimensions, and managing the SCD (slowly changing dimension) process. Before we start, let&#8217;s [...]]]></description>
			<content:encoded><![CDATA[<p>In my last <a href="http://www.purplefrogsystems.com/blog/2011/12/introduction-to-t-sql-merge-basics/">blog post</a> I showed the basic concepts of using the T-SQL Merge statement, available in SQL Server 2008 onwards.</p>
<p>In this post we&#8217;ll take it a step further and show how we can use it for loading data warehouse dimensions, and managing the SCD (slowly changing dimension) process. Before we start, let&#8217;s have a quick catch up on what an SCD is&#8230;</p>
<h2>What is a Slowly Changing Dimension (SCD)?</h2>
<p>If you want a full explanation of slowly changing dimensions then you&#8217;ve come to the wrong place, I&#8217;m assuming a moderate level of experience of SCDs here, check out <a href="http://en.wikipedia.org/wiki/Slowly_changing_dimension" target="_blank">Wikipedia</a> for some background, but in short, they manage the tracking of attribute history in dimensional data warehouses.</p>
<p>Most data warehouses contain type 0, 1 and 2 SCDs, so we&#8217;ll cope with those for now.</p>
<ul>
<li>Type 0 &#8211; Ignore updates</li>
<li>Type 1 &#8211; Only keep latest version</li>
<li>Type 2 &#8211; Track history by creating a new row</li>
</ul>
<p>Type 2 is commonly stored in a fashion similar to this.</p>
<p><a href="http://www.purplefrogsystems.com/blog/wp-content/uploads/2012/01/SCDUsingMergeExample.png"><img class="aligncenter size-medium wp-image-500" title="SCDUsingMergeExample" src="http://www.purplefrogsystems.com/blog/wp-content/uploads/2012/01/SCDUsingMergeExample-720x44.png" alt="" /></a></p>
<p>Both records show the same customer but in this case Jane got married and changed her name &amp; title. We terminate the old record by setting IsRowCurrent=0 and create a new record with the new details. Each row also contains ValidFrom and ValidTo dates which allow us to identify the correct record for a particular point in time.</p>
<p>That&#8217;s enough of that, let&#8217;s get on with doing this using Merge</p>
<h2>Using Merge to load SCD</h2>
<p>The first stage is to save the output rows from the ETL process to a staging table. We can then use Merge to process these into the live dimension.</p>
<p>We saw in the <a href="/blog/2011/12/introduction-to-t-sql-merge-basics/">previous post</a> how to either insert or update a record depending on whether it already exists. We can start with this and enhance as we go. First lets figure out what logic we want to perform</p>
<ul>
<li>If the record doesn&#8217;t exist, create it</li>
<li>If the record does exist
<ul>
<li>Type 0 fields &#8211; ignore</li>
<li>Type 1 fields &#8211; update fields</li>
<li>Type 2 fields &#8211; terminate existing record, insert a new record</li>
</ul>
</li>
<li>If the record exists in the dimension, but not in the updated source file &#8211; terminate record</li>
</ul>
<p>The last option is rarely used in my experience, as it only works when you perform a full load of the dimension every time. It&#8217;s more common to process an incremental load, but I&#8217;ve included it here for completeness.</p>
<p>The main difference here, over a basic upsert, is the handling of type 2s; we need to perform two separate operations on the dimension for every incoming record. Firstly we terminate the existing row then we have to insert a new row.</p>
<p>The T-SQL Merge statement can only update a single row per incoming row, but there&#8217;s a trick that we can take advantage of by making use of the OUTPUT clause. Merge can output the results of what it has done, which in turn can be consumed by a separate INSERT statement.</p>
<p>We&#8217;ll therefore use the MERGE statement to update the existing record, terminating it, and then pass the relevant source rows out to the INSERT statement to create the new row.</p>
<p>Let&#8217;s look at an example. Download the <a href="/blog/wp-content/uploads/2012/01/using-merge-to-load-data-warehouse-dimensions.sql" target="_blank">code here</a> which will create the necessary tables and data to work on.</p>
<h2>Main Merge Statement</h2>
<p>We&#8217;ll start with a statement very similar to the previous post, with only a couple of minor amendments:</p>
<ul>
<li>We include IsRowCurrent into the joining clause. We only ever want to update the current records, not the history.</li>
<li>DoB is removed from the WHEN MATCHED clause. We&#8217;re going to treat DoB as a type 1 change, if it&#8217;s updated then we assume it&#8217;s a correction rather than a new date of birth which should be tracked. We&#8217;ll deal with this Type 1 later on</li>
<li>The UPDATE statement in the WHEN MATCHED clause doesn&#8217;t change the fields, only terminates the row by setting the IsRowCurrent and ValidTo fields (as well as LastUpdated)</li>
</ul>
<pre class="code"> MERGE Customer        AS [Target]
 USING StagingCustomer AS [Source]
    ON Target.Email        = Source.Email
   AND Target.IsRowCurrent = 1
 WHEN MATCHED AND
     (
          Target.FirstName &lt;&gt; Source.FirstName
       OR Target.LastName  &lt;&gt; Source.LastName
       OR Target.Title     &lt;&gt; Source.Title
     )
     THEN UPDATE SET
        IsRowCurrent     = 0
       ,LastUpdated      = GETDATE()
       ,ValidTo          = GETDATE()
 WHEN NOT MATCHED BY TARGET
     THEN INSERT (
         FirstName
        ,LastName
        ,Title
        ,DoB
        ,Email
        ,LastUpdated
        ,IsRowCurrent
        ,ValidFrom
        ,ValidTo
       ) VALUES (
         Source.FirstName
        ,Source.LastName
        ,Source.Title
        ,Source.DoB
        ,Source.Email
        ,GETDATE()      --LastUpdated
        ,1              --IsRowCurrent
        ,GETDATE()      --ValidFrom
        ,'9999-12-31'   --ValidTo
       )
 WHEN NOT MATCHED BY SOURCE AND Target.IsRowCurrent = 1
     THEN UPDATE SET
         IsRowCurrent = 0
        ,LastUpdated  = GETDATE()
        ,ValidTo      = GETDATE()</pre>
<p>The &#8216;When Matched&#8217; section includes extra clauses which define which fields should be treated as Type 2.</p>
<p>The &#8216;When Not Matched By Target&#8217; section deals with inserting the new records which didn&#8217;t previously exist.</p>
<p>The &#8216;When Not Matched By Source&#8217; section deals with terminating records which are no longer received from the source. Usually this section can be deleted, especially if the data is received incrementally.</p>
<p><em><strong>*** UPDATE ***</strong> Thank you to Sergey (in the comments below) for pointing out an error in this code. I&#8217;ve now corrected the &#8216;WHEN NOT MATCHED BY SOURCE&#8217; line to include &#8216;AND Target.IsRowCurrent=1&#8242;. If this is omitted then all historic (IsRowCurrent=0) records are always updated with today&#8217;s date. We only want to terminate current records, not already terminated records.</em></p>
<p>We then add an OUTPUT clause to the end of the statement</p>
<pre class="code"> OUTPUT $action AS Action
       ,Source.*</pre>
<p>The OUTPUT clause tells MERGE to generate an output dataset. This can consist of any of the Source table&#8217;s fields or the Target table&#8217;s fields. We can also specify $Action as an extra field which will identify, for each row, whether</p>
<p>it was dealt with via an INSERT, UPDATE or DELETE. For this purpose we only care about the UPDATES, so we&#8217;ll use this to filter the records later on. We also only need the Source data, not the Target, so we&#8217;ll return Source.*</p>
<p>We wrap this up within an INSERT statement which will insert the new record for the changed dimension member.</p>
<pre class="code">INSERT INTO Customer
   ( FirstName
    ,LastName
    ,Title
    ,DoB
    ,Email
    ,LastUpdated
    ,IsRowCurrent
    ,ValidFrom
    ,ValidTo
   )
SELECT
     FirstName
    ,LastName
    ,Title
    ,DoB
    ,Email
    ,GETDATE()    --LastUpdated
    ,1            --IsRowCurrent
    ,GETDATE()    --ValidFrom
    ,'9999-12-31' --ValidTo
FROM (
  MERGE Customer        AS [Target]
  USING StagingCustomer AS [Source]
     ON Target.Email        = Source.Email
    AND Target.IsRowCurrent = 1
  WHEN MATCHED AND
      (
           Target.FirstName &lt;&gt; Source.FirstName
        OR Target.LastName  &lt;&gt; Source.LastName
        OR Target.Title     &lt;&gt; Source.Title
      )
      THEN UPDATE SET
         IsRowCurrent     = 0
        ,LastUpdated      = GETDATE()
        ,ValidTo          = GETDATE()
  WHEN NOT MATCHED BY TARGET
      THEN INSERT (
          FirstName
         ,LastName
         ,Title
         ,DoB
         ,Email
         ,LastUpdated
         ,IsRowCurrent
         ,ValidFrom
         ,ValidTo
        ) VALUES (
          Source.FirstName
         ,Source.LastName
         ,Source.Title
         ,Source.DoB
         ,Source.Email
         ,GETDATE()      --LastUpdated
         ,1              --IsRowCurrent
         ,GETDATE()      --ValidFrom
         ,'9999-12-31'   --ValidTo
        )
  WHEN NOT MATCHED BY SOURCE AND Target.IsRowCurrent = 1
      THEN UPDATE SET
          IsRowCurrent = 0
         ,LastUpdated  = GETDATE()
         ,ValidTo      = GETDATE()
  OUTPUT $action AS Action
        ,[Source].*
) AS MergeOutput
WHERE MergeOutput.Action = 'UPDATE'
  AND Email IS NOT NULL
;</pre>
<p>Note that the output clause is restricted so we only return the &#8216;UPDATE&#8217; rows. As we&#8217;re using the email field as the business key, we should also ensure that we only insert records which have a valid email address.</p>
<p>So Type 2 changes have now been dealt with, by terminating the old version of the record and inserting the new version. Type 0 fields are just left out of the entire process, so are taken care of by just ignoring them. Therefore the only thing left is to manage the Type 1 fields.</p>
<p>We have two options here;</p>
<ul>
<li>Update all historical records to the new value</li>
<li>Update only the current record to the new value</li>
</ul>
<p>These are obviously only valid when there is a mix of type 1 and 2 attributes. If we&#8217;re just looking at Type 1 then there will be no historical records. In a true Type 1 scenario the first option is correct. All history (of Type 1 fields) is lost. The second option can be a valid option when it would be beneficial to keep a limited history of Type 1 fields.</p>
<p>This would mean that historical records created by Type 2 changes also keep a record of the Type 1 attribute values that were valid at the time the record was terminated. It doesn&#8217;t keep a full history of Type 1 attributes but sometimes this can be useful.</p>
<pre class="code">  UPDATE C
      SET DoB         = SC.DoB
         ,LastUpdated = GETDATE()
  FROM Customer C
     INNER JOIN StagingCustomer SC
          ON C.Email        =  SC.Email
       --AND C.IsRowCurrent =  1      --Optional
         AND C.DoB          &lt;&gt; SC.DoB</pre>
<p>This block of code updates the Type 1 attributes (in this case, DoB). The line 7 (the IsRowCurrent) check is optional depending on whether you only want to update current or all records.</p>
<p>So in one SQL statement we&#8217;ve managed the entire load process of all Type 2 SCDs, and with one more we&#8217;ve also managed all Type 1 fields.</p>
<p>I&#8217;ve been performing a large number of performance tests on loading Type 2s using various methods (another blog post to follow, as well as a talk that I&#8217;ll be presenting at <a href="http://www.SQLBits.com" target="_blank">SQL Bits X</a>), and the performance of this method is very fast. In fact there&#8217;s very little difference in performance between using this method and using the SSIS Merge Join component.</p>
<p>This is now my preferred approach to loading Type 2 SCDs, slightly faster methods may be available, but as we&#8217;ll see in later blog posts, this is such a quick method to implement, as well as being incredibly flexible as it can be controlled entirely from metadata.</p>
<p>Long live the Merge statement!</p>
<p>Frog-Blog Out</p>
]]></content:encoded>
			<wfw:commentRss>http://www.purplefrogsystems.com/blog/2012/01/using-t-sql-merge-to-load-data-warehouse-dimensions/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>Introduction to T-SQL Merge Basics</title>
		<link>http://www.purplefrogsystems.com/blog/2011/12/introduction-to-t-sql-merge-basics/</link>
		<comments>http://www.purplefrogsystems.com/blog/2011/12/introduction-to-t-sql-merge-basics/#comments</comments>
		<pubDate>Tue, 13 Dec 2011 13:47:36 +0000</pubDate>
		<dc:creator>Alex</dc:creator>
				<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[Merge]]></category>

		<guid isPermaLink="false">http://www.purplefrogsystems.com/blog/?p=470</guid>
		<description><![CDATA[A number of Frog-Blog posts over the next couple of months are going to make heavy use of the awesome SQL Server MERGE statement, introduced in SQL Server 2008. I thought it best to write an introduction post to provide the basics and groundwork for future posts. So what does the Merge statement do? Merge [...]]]></description>
			<content:encoded><![CDATA[<p>A number of Frog-Blog posts over the next couple of months are going to make heavy use of the awesome SQL Server MERGE statement, introduced in SQL Server 2008. I thought it best to write an introduction post to provide the basics and groundwork for future posts.</p>
<p><img class="aligncenter size-full wp-image-476" title="IntroductionToMerge" src="http://www.purplefrogsystems.com/blog/wp-content/uploads/2011/12/IntroductionToMerge.png" alt="" width="440" height="158" /></p>
<h2>So what does the Merge statement do?</h2>
<p>Merge upserts data into a table. If, like me, you find the word ‘upsert’ more than a little ridiculous, we can scrap it in favour of a slightly more sensible description; It both inserts and updates data at the same time. Much better.</p>
<h2>How does it do it?</h2>
<p>In a similar way in which we’d use an UPDATE statement, we need to provide a source table and a destination table. We then give it a method of matching rows between them, usually the primary key or business/natural key (this can use multiple fields).</p>
<p>We can then specify a number of actions depending on whether a match is found or not.</p>
<p>If a match is found we may want to update the existing record.</p>
<p>If a match isn’t found then it’s likely that we’ll want to insert a new record.</p>
<h2>What’s the syntax?</h2>
<p>First we set the tables and the joining key(s)</p>
<pre class="code">MERGE [Destination Table] AS Target
   USING [Source Table] AS Source
     ON Target.KeyField = Source.KeyField</pre>
<p>Then we define what we want to happen if we find a match (optional)</p>
<pre class="code">  WHEN MATCHED</pre>
<p>If you want you can specify other conditions, such as checking whether anything has changed</p>
<pre class="code">     AND  (
         Target.Field1 &lt;&gt; Source.Field1
         OR Target.Field2 &lt;&gt; Source.Field2
         )</pre>
<p>We can then perform an action. i.e. an update, delete, etc.</p>
<pre class="code">  THEN UPDATE SET
        Field1 = Source.Field1
      , Field2 = Source.Field2</pre>
<p>Then we define what we want to happen if we don’t find a matching record (optional)</p>
<pre class="code">   WHEN NOT MATCHED</pre>
<p>In this case, we want to insert the new record</p>
<pre class="code">      THEN INSERT (
             KeyField
           , Field1
           , Field2
        ) VALUES (
             Source.KeyField
           , Source.Field1
           , Source.Field2
      );</pre>
<p>As this is the last part of the statement we have to terminate the merge with a semi colon.</p>
<h2>Time for an example</h2>
<p>We’ve got a customer table, and we receive an updated set of customer details which we want to import into our table. Assuming we’ve imported the new details into a staging table, we can use merge to do the work for us.</p>
<p>Use <a href="/download/blog/IntroductionToMerge.sql" target="_blank">this script</a> to create the necessary tables &amp; data.</p>
<p>Our existing Customer table should look like this</p>
<p><img title="IntroductionToMergeCustomer" src="http://www.purplefrogsystems.com/blog/wp-content/uploads/2011/12/IntroductionToMergeCustomer1.png" alt="" width="600" height="90" /></p>
<p>And our staging table (new customer data) should look like</p>
<p><img class="aligncenter size-full wp-image-486" title="IntroductionToMergeStaging" src="http://www.purplefrogsystems.com/blog/wp-content/uploads/2011/12/IntroductionToMergeStaging.png" alt="" width="291" height="90" /></p>
<p>Firstly lets define the logic that we’re looking for.</p>
<ul>
<li>Our new customer data doesn’t include the CustomerID, so we can’t match on the primary key. Instead we’ll use the email address as the key.</li>
<li>If the customer already exists then we want to check whether the fields are different, if they are then update the customer record.</li>
<li>If the customer doesn’t exist then insert them as a new customer record.</li>
<li>The Customer table contains audit fields which must be maintained. DTInserted should contain the datetime when the record was first created. DTUpdated should be updated whenever any of the data in a row is changed.</li>
</ul>
<p>This is all performed using the following statement</p>
<pre class="code">MERGE Customer        AS [Target]
USING StagingCustomer AS [Source]
   ON Target.Email = Source.Email
WHEN MATCHED AND
    (
         Target.FirstName &lt;&gt; Source.FirstName
      OR Target.LastName  &lt;&gt; Source.LastName
      OR Target.Title     &lt;&gt; Source.Title
      OR Target.DoB       &lt;&gt; Source.DoB
    )
    THEN UPDATE SET
       FirstName = Source.FirstName
      ,LastName  = Source.LastName
      ,Title     = Source.Title
      ,DoB       = Source.DoB
      ,DTUpdated = GETDATE()
WHEN NOT MATCHED
    THEN INSERT (
        Email
       ,FirstName
       ,LastName
       ,Title
       ,DoB
       ,DTInserted
       ,DTUpdated
      ) VALUES (
        Source.Email
       ,Source.FirstName
       ,Source.LastName
       ,Source.Title
       ,Source.DoB
       ,GETDATE()
       ,GETDATE()
      );</pre>
<p>It’s a long statement and looks complex at first glance, but as soon as you break it down it’s remarkably simple and easy to write.</p>
<p>If you run this, you should see 4 rows affected – 2 new rows inserted (Jack &amp; Mary) and 2 rows updated (Phil &amp; Jane). The new data has been inserted/updated into the Customer table and the audit fields have been kept up to date.</p>
<p><img class="aligncenter size-full wp-image-489" title="IntroductionToMergeAfter1" src="http://www.purplefrogsystems.com/blog/wp-content/uploads/2011/12/IntroductionToMergeAfter1.png" alt="" width="602" height="115" /></p>
<p>Note however that (if you look at the data in the sample scripts) Sarah &amp; Andy exist in the Customer table but are missing from the new customer list in the update file. With the code above they just stay as they are in the Customer table, we assume they haven’t changed. What if we wanted to identify these customers and flag them as inactive or even delete them?</p>
<p>If we look at the ‘WHEN NOT MATCHED’ line, what this is actually doing is defaulting to ‘WHEN NOT MATCHED BY TARGET’. i.e. if you don’t find the record in the target table then do something.<br />
We can add an extra section for ‘WHEN NOT MATCHED BY SOURCE’ which will allow us to specify an action when a row in our Customer table doesn’t have a matching record in the source staging data.</p>
<p>We can add:</p>
<pre class="code">WHEN NOT MATCHED BY SOURCE
    THEN DELETE</pre>
<p>Which will simply delete the row in the Customer table, or we could update the record and terminate it using something like this.</p>
<pre class="code">WHEN NOT MATCHED BY SOURCE
    THEN UPDATE SET
        IsActive = 0</pre>
<p>Don’t forget that the entire merge statement needs to be terminated with a semi colon, and we should also update the audit field. We also only want to update the record once, so check that the customer isn&#8217;t already terminated. We therefore end up with:</p>
<pre class="code">MERGE Customer        AS [Target]
USING StagingCustomer AS [Source]
   ON Target.Email = Source.Email
WHEN MATCHED AND
    (
         Target.FirstName &lt;&gt; Source.FirstName
      OR Target.LastName  &lt;&gt; Source.LastName
      OR Target.Title     &lt;&gt; Source.Title
      OR Target.DoB       &lt;&gt; Source.DoB
    )
    THEN UPDATE SET
       FirstName = Source.FirstName
      ,LastName  = Source.LastName
      ,Title     = Source.Title
      ,DoB       = Source.DoB
      ,DTUpdated = GETDATE()
WHEN NOT MATCHED BY TARGET
    THEN INSERT (
        Email
       ,FirstName
       ,LastName
       ,Title
       ,DoB
       ,IsActive
       ,DTInserted
       ,DTUpdated
      ) VALUES (
        Source.Email
       ,Source.FirstName
       ,Source.LastName
       ,Source.Title
       ,Source.DoB
       ,1
       ,GETDATE()
       ,GETDATE()
      )
WHEN NOT MATCHED BY SOURCE
    AND Target.IsActive=1
    THEN UPDATE SET
        IsActive = 0
       ,DTUpdated = GETDATE()
      ;</pre>
<p>Which leaves our Customer table looking like this</p>
<p><img class="aligncenter size-full wp-image-496" title="IntroductionToMergeAfter2" src="http://www.purplefrogsystems.com/blog/wp-content/uploads/2011/12/IntroductionToMergeAfter2.png" alt="" width="601" height="115" /></p>
<p>There are many variations and uses for this, we’ll explore its use in data warehouse dimension loading over the next couple of posts.</p>
<p>For now, Frog-Blog out.</p>
<p><strong>[UPDATE]</strong> The next blog post in this series is now available: <a href="/blog/2012/01/using-t-sql-merge-to-load-data-warehouse-dimensions/">using T-SQL Merge to load Type 2 SCDs into a data warehouse dimension</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.purplefrogsystems.com/blog/2011/12/introduction-to-t-sql-merge-basics/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Fast record count in SQL Server using sp_spaceused</title>
		<link>http://www.purplefrogsystems.com/blog/2011/12/fast-record-count-in-sql-server-using-sp_spaceused/</link>
		<comments>http://www.purplefrogsystems.com/blog/2011/12/fast-record-count-in-sql-server-using-sp_spaceused/#comments</comments>
		<pubDate>Wed, 07 Dec 2011 14:44:36 +0000</pubDate>
		<dc:creator>Alex</dc:creator>
				<category><![CDATA[SQL Server]]></category>

		<guid isPermaLink="false">http://www.purplefrogsystems.com/blog/?p=464</guid>
		<description><![CDATA[Just a quick tidbit today, but a very useful one. Ever need to do SELECT Count(*) on a very large table? It can take ages. Instead, use sp_spaceused EXEC sp_spaceused 'MyTable' It returns you a useful result set like name rows reserved data index_size unused MyTable 19690290 13637488 KB 3001688 KB 9986416 KB 649384 KB [...]]]></description>
			<content:encoded><![CDATA[<p>Just a quick tidbit today, but a very useful one.<br />
Ever need to do SELECT Count(*) on a very large table? It can take ages.</p>
<p>Instead, use sp_spaceused</p>
<pre>EXEC sp_spaceused 'MyTable'</pre>
<p>It returns you a useful result set like</p>
<table border="1" cellpadding="5">
<tr>
<td>name</td>
<td>rows</td>
<td>reserved</td>
<td>data</td>
<td>index_size</td>
<td>unused</td>
</tr>
<tr>
<td>MyTable</td>
<td>19690290</td>
<td>13637488 KB</td>
<td>3001688 KB</td>
<td>9986416 KB</td>
<td>649384 KB</td>
</tr>
</table>
<p>Not only does it also give you extra useful information about the table, but it comes back virtually instantly.</p>
<p>Columns:<br />
&nbsp;&nbsp;name &#8211; name of the object requested<br />
&nbsp;&nbsp;rows &#8211; the number of rows in the table (or service broker queue)<br />
&nbsp;&nbsp;reserved &#8211; the total reserved space for the object<br />
&nbsp;&nbsp;data &#8211; amount of space used by the data<br />
&nbsp;&nbsp;index_size &#8211; amount of spaced used by indexes in the object<br />
&nbsp;&nbsp;unused &#8211; total space reserved for the object but not yet used</p>
<p>You can also use sp_spaceused without any parameters, and it will return you the stats for the whole database.</p>
<p>Frog-Blog Out</p>
]]></content:encoded>
			<wfw:commentRss>http://www.purplefrogsystems.com/blog/2011/12/fast-record-count-in-sql-server-using-sp_spaceused/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>PowerPivot Vs QlikView 101 &#8211; SQLBits  Video</title>
		<link>http://www.purplefrogsystems.com/blog/2011/11/powerpivot-vs-qlikview-101-sqlbits-video/</link>
		<comments>http://www.purplefrogsystems.com/blog/2011/11/powerpivot-vs-qlikview-101-sqlbits-video/#comments</comments>
		<pubDate>Thu, 10 Nov 2011 19:50:21 +0000</pubDate>
		<dc:creator>Alex</dc:creator>
				<category><![CDATA[Analysis Services]]></category>
		<category><![CDATA[Business Intelligence]]></category>
		<category><![CDATA[PowerPivot]]></category>
		<category><![CDATA[QlikView]]></category>
		<category><![CDATA[Dashboard]]></category>
		<category><![CDATA[SQLBits]]></category>

		<guid isPermaLink="false">http://www.purplefrogsystems.com/blog/?p=459</guid>
		<description><![CDATA[PowerPivot Vs QlikView 101 &#8211; SQLBits Video The video is now available from my PowerPivot and QlikView talk at SQLBits 9 in Liverpool in September 2011. You can download or watch the video here. In this 1 hour session I create interactive dashboards from scratch in both PowerPivot and QlikView, showing how to set up [...]]]></description>
			<content:encoded><![CDATA[<h1>PowerPivot Vs QlikView 101 &#8211; SQLBits  Video</h1>
<p>The video is now available from my PowerPivot and QlikView talk at SQLBits 9 in Liverpool in September 2011. <a href="http://www.sqlbits.com/Sessions/Event9/PowerPivot_and_QlikView_101">You can download or watch the video here</a>.</p>
<p><br/></p>
<p>In this 1 hour session I create interactive dashboards from scratch in both PowerPivot and QlikView, showing how to set up the data model, overcome common pitfalls and build the dashboards. I create almost identical dashboards in both systems and highllight the pros and cons of each system.</p>
<p><br/></p>
<p><a href="http://www.sqlbits.com/Sessions/Event9/PowerPivot_and_QlikView_101"><img src="http://www.purplefrogsystems.com/blog/wp-content/uploads/2011/11/SQLBits9VideoPowerPivotQlik.png" alt="PowerPivot QlikView" title="SQLBits9VideoPowerPivotQlikView" width="300" height="164" class="aligncenter size-full wp-image-460" /></a>
</p>
<p><br/></p>
<p>You can find the scripts and code to go with this video in <a href="/blog/2011/10/sqlbits-9-session-powerpivot-and-qlikview-101/">this blog post</a></p>
<p><br/></p>
<h3>SQLBits 10 is coming!!</h3>
<p>Today the SQLBits organisers have announced that SQLBits 10 will be held in London between 29th &#8211; 31st March 2012 in the Novotel London West. It&#8217;s going to be even bigger and better, so keep an eye on the website and get your tickets early.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.purplefrogsystems.com/blog/2011/11/powerpivot-vs-qlikview-101-sqlbits-video/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>SQLBits 9 Session &#8211; PowerPivot and QlikView 101</title>
		<link>http://www.purplefrogsystems.com/blog/2011/10/sqlbits-9-session-powerpivot-and-qlikview-101/</link>
		<comments>http://www.purplefrogsystems.com/blog/2011/10/sqlbits-9-session-powerpivot-and-qlikview-101/#comments</comments>
		<pubDate>Sat, 15 Oct 2011 09:48:41 +0000</pubDate>
		<dc:creator>Alex</dc:creator>
				<category><![CDATA[Business Intelligence]]></category>
		<category><![CDATA[Community]]></category>
		<category><![CDATA[PowerPivot]]></category>
		<category><![CDATA[QlikView]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[SQLBits]]></category>

		<guid isPermaLink="false">http://www.purplefrogsystems.com/blog/?p=450</guid>
		<description><![CDATA[Thank you to the SQLBits committee, speakers, helpers and attendees, for making SQLBits 9 one of the best yet. What a great example SQLBits is of the power of the SQL Server community coming together to help each other, and enjoy a few beers in the process! I was delighted to have my &#8220;PowerPivot &#38; [...]]]></description>
			<content:encoded><![CDATA[<p>Thank you to the SQLBits committee, speakers, helpers and attendees, for making SQLBits 9 one of the best yet. What a great example SQLBits is of the power of the SQL Server community coming together to help each other, and enjoy a few beers in the process!</p>
<p><img class="alignright" src="/images/blog/SQLBits9QlikViewPowerPivot.png" alt="QlikView PowerPivot" width="352" height="219" />I was delighted to have my &#8220;PowerPivot &amp; QlikView 101&#8243; session chosen by the committee, which I presented on Saturday afternoon. Thank you to all those who attended, great to see such a good number there. If you haven&#8217;t yet submitted your feedback for my session, and the event as a whole, please do take the time to do it. It helps everyone improve the quality of the event for next time.</p>
<ul>
<li><a href="www.sqlbits.com/SQLBits9Thursday">Thursday training day</a></li>
<li><a href="www.sqlbits.com/SQLBits9Friday">Friday</a></li>
<li><a href="www.sqlbits.com/SQLBits9Saturday">Saturday</a></li>
<li><a href="www.sqlbits.com/SQLBits9Feedback">Conference Feedback</a></li>
</ul>
<p>For those who atttended my session, here are the slides, with a summary of the results and code etc.</p>
<p>Slides: <a href="/download/blog/SQLBits9-PowerPivotQlikView101.pdf">Download the PowerPivot &amp; QlikView 101 slide deck here</a></p>
<p>QlikView Script: <a href="/download/blog/QlikViewScript.txt">Download the QlikView Script here</a></p>
<p>PowerPivot Workboook: <a href="/download/blog/SQLBits9PowerPivot.xlsx">Download the PowerPivot workbook here</a></p>
<p>Session Video: The session recording isn&#8217;t yet available, I&#8217;ll post a link here when it&#8217;s ready.</p>
<p><H2>QlikView Expressions</H2></p>
<p>Sales Amount Visual Cue for the green shading<br />
&nbsp;&nbsp;&nbsp;Upper: (SUM(TOTAL SalesAmount) / COUNT(TOTAL SalesTerritoryRegion))*1.2<br />
&nbsp;&nbsp;&nbsp;Lower: (SUM(TOTAL SalesAmount) / COUNT(TOTAL SalesTerritoryRegion))*0.8</p>
<p>Sales Amount blue bar Guage maximum<br />
&nbsp;&nbsp;&nbsp;max(ALL Aggr(SUM(SalesAmount),OrderMonth))</p>
<p>Sales Amount Year to Date<br />
&nbsp;&nbsp;&nbsp;rangesum(above(sum(SalesAmount),0,12))</p>
<p>Sales Amount Previous Year<br />
&nbsp;&nbsp;&nbsp;SUM({&lt;OrderYear={&#8216;$(=Max(OrderYear)-1)&#8217;}&gt;} SalesAmount)</p>
<p><H2>PowerPivot DAX Expressions</H2></p>
<p>Sales Amount Year to Date<br />
&nbsp;&nbsp;&nbsp;=TOTALYTD(SUM(FactInternetSales[SalesAmount]), DimOrderDate[FullDateAlternateKey], ALL(DimOrderDate))</p>
<p>Sales Amount Previous Year<br />
&nbsp;&nbsp;&nbsp;=CALCULATE(SUM(FactInternetSales[SalesAmount]), SAMEPERIODLASTYEAR(DimOrderDate[FullDateAlternateKey]), ALL(DimOrderDate))</p>
<p>&nbsp;</p>
<h2>PowerPivot Results</h2>
<p><a href="/images/blog/SQLBits9PowerPivot.png"><img class="aligncenter" src="/images/blog/SQLBits9PowerPivot.png" alt="PowerPivot Dashboard" width="665" height="469" /></a></p>
<p>&nbsp;</p>
<h2>QlikView Results</h2>
<p><a href="/images/blog/SQLBits9QlikView.png"><img class="aligncenter" src="/images/blog/SQLBits9QlikView.png" alt="QlikView Dashboard" width="672" height="421" /></a></p>
<p>If you have any questions about any of this, please get in touch.</p>
<p>Frog-Blog-Out</p>
]]></content:encoded>
			<wfw:commentRss>http://www.purplefrogsystems.com/blog/2011/10/sqlbits-9-session-powerpivot-and-qlikview-101/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>New UK SQL Server User Groups</title>
		<link>http://www.purplefrogsystems.com/blog/2011/09/new-uk-sql-server-user-groups/</link>
		<comments>http://www.purplefrogsystems.com/blog/2011/09/new-uk-sql-server-user-groups/#comments</comments>
		<pubDate>Sun, 25 Sep 2011 14:30:07 +0000</pubDate>
		<dc:creator>Alex</dc:creator>
				<category><![CDATA[Community]]></category>
		<category><![CDATA[SQL Server]]></category>

		<guid isPermaLink="false">http://www.purplefrogsystems.com/blog/?p=442</guid>
		<description><![CDATA[What an exciting few weeks we&#8217;ve got to look forward to! SQL Bits starts on Thursday this week which promises to be another fantastic event. I&#8217;m speaking on the Saturday, presenting a session on QlikView and PowerPivot. When I&#8217;m not speaking I&#8217;ve got a list of duties as long as my arm, from marshelling to [...]]]></description>
			<content:encoded><![CDATA[<p>What an exciting few weeks we&#8217;ve got to look forward to!</p>
<p><a href="www.SQLBits.com">SQL Bits</a> starts on Thursday this week which promises to be another fantastic event. I&#8217;m speaking on the Saturday, presenting a session on <a href="http://www.sqlbits.com/Sessions/Event9/PowerPivot_and_QlikView_101">QlikView and PowerPivot</a>. When I&#8217;m not speaking I&#8217;ve got a list of duties as long as my arm, from marshelling to room monitoring, and of course representing the Community Corner.</p>
<p>If you&#8217;re interested in SQL events local to you, then come and say hi to us on the Community Coener stand &#8211; a number of user group leaders in the UK will be on hand to provide any info you want regarding the SQL community, and point you towards events happening in your area.</p>
<p>After SQLBits, we&#8217;ve got the SQL Relay to look forward to. Every SQL Server user group in the UK is putting on an event between 3rd and 5th October 2011, with a grand finale event in London at Microsoft&#8217;s Cardinal Place on the 6th. Every event has great speakers, from MVPs to Microsoft employees, and in London we have renowned author and blogger Itzik Ben-Gan. We also have a host of fantastic prizes to give away at every event.</p>
<p>What makes all of this even better is that there are a number of new user groups launching during this week, including <strong>Maidenhead</strong>, <strong>Cambridge</strong> and <strong>Essex</strong>. There&#8217;s a group near you so head over to <a href="www.SQLServerFAQ.com">www.SQLServerFAQ.com</a> and get registered!</p>
<p>Look forward to seeing you at one of the events!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.purplefrogsystems.com/blog/2011/09/new-uk-sql-server-user-groups/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

