<?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>Boyan Penev on Microsoft BI &#187; T-SQL</title>
	<atom:link href="http://www.bp-msbi.com/tag/t-sql/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.bp-msbi.com</link>
	<description>A practical blog about Microsoft BI tools, techniques and practices written by a developer for other fellow developers.</description>
	<lastBuildDate>Sun, 29 Jan 2012 03:23:06 +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>Number of Weekdays Between Two Dates</title>
		<link>http://www.bp-msbi.com/2010/08/number-of-weekdays-between-two-dates/</link>
		<comments>http://www.bp-msbi.com/2010/08/number-of-weekdays-between-two-dates/#comments</comments>
		<pubDate>Wed, 04 Aug 2010 04:43:04 +0000</pubDate>
		<dc:creator>Boyan Penev</dc:creator>
				<category><![CDATA[T-SQL]]></category>
		<category><![CDATA[dates]]></category>
		<category><![CDATA[weekdays]]></category>

		<guid isPermaLink="false">http://www.bp-msbi.com/?p=113</guid>
		<description><![CDATA[There was an old post here describing some T-SQL code for finding the number of weekdays between two dates, which I wrote. It was working fine, so if you have implemented it you have not done anything wrong. However, Jeff Moden from SQL Server Central has written a post a while ago about this same [...]]]></description>
			<content:encoded><![CDATA[<p>There was an old post here describing some T-SQL code for finding the number of weekdays between two dates, which I wrote. It was working fine, so if you have implemented it you have not done anything wrong. However, Jeff Moden from SQL Server Central has written a post a while ago about this same problem and his implementation is a bit cleaner, and thus I would consider it better than mine. So, here is the link:</p>
<p><a href="http://www.sqlservercentral.com/articles/Advanced+Querying/calculatingworkdays/1660/">http://www.sqlservercentral.com/articles/Advanced+Querying/calculatingworkdays/1660/</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.bp-msbi.com/2010/08/number-of-weekdays-between-two-dates/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Using the DSV to its Full Potential</title>
		<link>http://www.bp-msbi.com/2010/07/using-the-dsv-to-its-full-potential/</link>
		<comments>http://www.bp-msbi.com/2010/07/using-the-dsv-to-its-full-potential/#comments</comments>
		<pubDate>Thu, 22 Jul 2010 03:09:47 +0000</pubDate>
		<dc:creator>Boyan Penev</dc:creator>
				<category><![CDATA[SSAS]]></category>
		<category><![CDATA[aggregations]]></category>
		<category><![CDATA[Analysis Services]]></category>
		<category><![CDATA[dimensions]]></category>
		<category><![CDATA[named calculations]]></category>
		<category><![CDATA[named queries]]></category>
		<category><![CDATA[T-SQL]]></category>

		<guid isPermaLink="false">http://www.bp-msbi.com/?p=97</guid>
		<description><![CDATA[The Data Source View in Analysis Services is a very powerful abstraction of the data source and it can help us overcome some scenarios in an easy and clean way. Many times we look for MDX or programmatic solutions to problems, which can be tackled best in our data. While for complex tasks we would [...]]]></description>
			<content:encoded><![CDATA[<p>The Data Source View in Analysis Services is a very powerful abstraction of the data source and it can help us overcome some scenarios in an easy and clean way. Many times we look for MDX or programmatic solutions to problems, which can be tackled best in our data. While for complex tasks we would be better off extending the ETL process, some simple ones can and should be implemented in the DSV.</p>
<p style="text-align: center;"><img class="size-full wp-image-99 aligncenter" title="dsv_solution_explorer" src="http://www.bp-msbi.com/wp-content/uploads/2010/07/dsv_solution_explorer1.png" alt="" width="299" height="300" /></p>
<p> </p>
<p>As an introduction to the topic I would like to explain briefly what the DSV actually is. It can be conceptualised as a database view on top of the data source. By default all tables which we need for building the Analysis Services database (typically dimensions and facts) are appearing in the DSV as table bindings (exactly as if we do a SELECT * FROM Table). If we have no foreign keys defined in our database, SSAS will not show us the relationships in the DSV. However, we can define logical relationships in the DSV, thus connecting the tables on related columns, which are then used for automatically determining dimension relationships to the measure groups.</p>
<p style="text-align: center;"><a href="http://www.bp-msbi.com/wp-content/uploads/2010/07/dsv_overview.png"><img class="size-large wp-image-100 aligncenter" title="dsv_overview" src="http://www.bp-msbi.com/wp-content/uploads/2010/07/dsv_overview-1024x618.png" alt="" width="614" height="371" /></a><a href="http://www.bp-msbi.com/wp-content/uploads/2010/07/dsv_solution_explorer1.png"></a></p>
<p> </p>
<p>There are two important ways to modify the DSV, which allow us to add more columns to the existing tables and to modify the way the existing columns are shown:</p>
<p><strong>Named Queries</strong></p>
<p><strong></strong> </p>
<p style="text-align: center;"> <a href="http://www.bp-msbi.com/wp-content/uploads/2010/07/dsv_new_named_query.png"><img class="size-full wp-image-101 aligncenter" title="dsv_new_named_query" src="http://www.bp-msbi.com/wp-content/uploads/2010/07/dsv_new_named_query.png" alt="" width="498" height="249" /></a></p>
<p> </p>
<p>If we right-click on a table in the DSV, we can select to replace the table with a Named Query. A Named Query is essentially a T-SQL statement, which is equivalent to a database view definition. By utilising Named Queries we can alter the way we see the tables and their column in SSAS. In example, we could concatenate columns, implement CASE logic, etc. Named Queries can be thought of as equivalent to database views.</p>
<p style="text-align: center;"><a href="http://www.bp-msbi.com/wp-content/uploads/2010/07/dsv_new_named_query_edit.png"><img class="size-full wp-image-102 aligncenter" title="dsv_new_named_query_edit" src="http://www.bp-msbi.com/wp-content/uploads/2010/07/dsv_new_named_query_edit.png" alt="" width="668" height="650" /></a></p>
<p><strong></strong> </p>
<p><strong>Named Calculations</strong></p>
<p style="text-align: center;"><a href="http://www.bp-msbi.com/wp-content/uploads/2010/07/dsv_new_named_calculation.png"><img class="size-full wp-image-103 aligncenter" title="dsv_new_named_calculation" src="http://www.bp-msbi.com/wp-content/uploads/2010/07/dsv_new_named_calculation.png" alt="" width="280" height="208" /></a></p>
<p> </p>
<p>A named calculation is a SQL statement which adds a column to a table without modifying the table binding. It gives us an easy way to define a new column without changing the whole query. The statement defining the column is in T-SQL and it behaves the same way as a new column in a Named Query (or a SELECT statement). If we just want to add one more column (e.g. Display Order, Code+Description concatenation, etc.), we can simply define a Named Calculation. Also, as the name suggests, Named Calculations can be commonly used for defining a leaf-level calculation without modifying a large fact table’s SELECT statement in a Named Query.</p>
<p style="text-align: center;"><a href="http://www.bp-msbi.com/wp-content/uploads/2010/07/dsv_new_named_calculation_edit.png"><img class="size-full wp-image-104   aligncenter" title="dsv_new_named_calculation_edit" src="http://www.bp-msbi.com/wp-content/uploads/2010/07/dsv_new_named_calculation_edit.png" alt="" width="569" height="413" /></a></p>
<p style="text-align: left;"> </p>
<p style="text-align: left;">The column we define here appears in both the DSV table and in the Dimension Designer window:</p>
<p style="text-align: center;"><a href="http://www.bp-msbi.com/wp-content/uploads/2010/07/dsv_new_named_calculation_column.png"><img class="size-full wp-image-105   aligncenter" title="dsv_new_named_calculation_column" src="http://www.bp-msbi.com/wp-content/uploads/2010/07/dsv_new_named_calculation_column.png" alt="" width="327" height="116" /></a></p>
<p> </p>
<p>These two DSV functions can be used in many scenarios. Most importantly, there are a few when they yield better performance, faster development and easier maintenance:</p>
<p><strong>Leaf-level calculations</strong></p>
<p>If we have the common requirement to perform leaf-level calculations and then aggregate this up the hierarchy, as opposed to aggregating and then calculating, the best way to do this is in a SQL statement on the fact table. Alternatively, we can do this in and MDX statement:</p>
<p> <span style="color: #993300;">SUM(DESCENDANTS(Dim.CurrentMember,,LEAVES), MeasureCalc)</span></p>
<p>However, it comes at a price. Since SSAS would have to do the calculation for each leaf and then sum this up the hierarchy, this could take a long time to perform. Also, SSAS would not be able to use pre-processed aggregations and the calculations will be done at execution time. To avoid this we could add a new column to the fact table and do the calculation there (in SQL), using the column as a new measure in the cube, which can then be aggregated by SSAS as any other measure. The performance gain is usually substantial and using a Named Query or a Named Calculation should always be the preferred option.</p>
<p><strong>Description Attributes</strong></p>
<p>Often we need to perform a concatenation between different dimension attributes, which we can use as a Description attribute while slicing the cube, or when providing reports from the SSAS database. A very easy way to achieve such a requirement is to use our DSV and concatenate the column we need in a new column in the dimension table, which we can expose as a new attribute in the dimension. A task such as concatenating an Account Code and Account Description into an Account Long Description (i.e. [Account Code] + ‘-‘ + [Account Description]) becomes very easy to implement within the DSV without modifying the ETL or any tables.</p>
<p><strong>Composite Keys</strong></p>
<p>Sometimes we need to build unique keys for attribute column in a dimension. A good example is a Date dimension, which does not have unique keys for non-leaf levels such as Month. Often developers have Month Key of 1,2,3-12. This does not make a good Month key in SSAS as it is not unique for higher levels such as Year, Quarter, etc. There are a number of ways to tackle this common scenario. While the recommended approach would be to build a concatenation between Year-Quarter-Month as a Month Key in the dimension table, we can also achieve this by either selecting all of the columns as key columns for the attribute in the dimension attribute properties. However, this would give us a concatenated key in MDX and this could sometimes be undesirable. A yet simpler and cleaner solution is to concatenate the relevant columns in the DSV by using a Named Query. Instead of the typical</p>
<p><span style="color: #993300;">SELECT col1, col2,.., MonthKey, colx, coly, coly FROM DimDate</span></p>
<p>we can write</p>
<p><span style="color: #993300;">SELECT col1, col2,…,YearKey+QuarterKey+MonthKey AS MonthKey, colx, coly, coz FROM DimDate</span></p>
<p>This way we can use the MonthKey column directly as a key for our Month attribute.</p>
<p>While this is useful for a Date dimension, it can also be useful for any other composite key definition in our dimensions.</p>
<p>Other possible applications of DSV Named Queries and Named Calculations are the implementation of</p>
<ul>
<li>Sort Order attribute, in cases when we need custom sort of the dimension attributes</li>
<li>Restricting the data which comes into the cube dynamically based on a certain condition (think of a Date dimension, which includes only relevant periods)</li>
<li>Combining tables – by a SQL join</li>
<li>Replacing 0s with NULLs (the opposite can be done automatically in SSAS) for our measures</li>
</ul>
<p>Basically, in a DSV we can “correct” our data to make it suitable for our cube without changing the ETL.</p>
<p>Last but not least, we can also transform tables to conform to a star-schema-like design. If we want to show a proof of concept on top of a normalized OLTP database, we could avoid the ETL complexities, as well as building a datamart, and use SQL to join/split tables in dimension and fact tables, which are suitable for cube development. While this could work in post-POC scenarios, it would be better to take a cautious approach to it as there are many scenarios when it would either not work, or will be too slow.</p>
<p>And a word of warning – your DSV could become slow because of over-use of complex Named Queries. This could be painful when minimising cube processing time is crucial, or when the DSV starts timing out and queries take hours to execute. Luckily, in most cases we can simply move these large queries forward – to the ETL where we have more time and better tools (e.g. SSIS).</p>
]]></content:encoded>
			<wfw:commentRss>http://www.bp-msbi.com/2010/07/using-the-dsv-to-its-full-potential/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Passing database names to SSIS stored procedures</title>
		<link>http://www.bp-msbi.com/2010/06/passing-database-names-to-ssis-stored-procedures/</link>
		<comments>http://www.bp-msbi.com/2010/06/passing-database-names-to-ssis-stored-procedures/#comments</comments>
		<pubDate>Tue, 29 Jun 2010 05:59:34 +0000</pubDate>
		<dc:creator>Boyan Penev</dc:creator>
				<category><![CDATA[SSIS]]></category>
		<category><![CDATA[dynamic SQL]]></category>
		<category><![CDATA[T-SQL]]></category>

		<guid isPermaLink="false">http://www.bp-msbi.com/?p=89</guid>
		<description><![CDATA[In the rare cases when we use dynamic SQL and want to use a database name in our code, we are better off avoiding hard-coding them. Unfortunately, I could not find an easy way to access a connection manager&#8217;s database name and on my current project the catalog name is not in the SSIS configurations XML [...]]]></description>
			<content:encoded><![CDATA[<p>In the rare cases when we use dynamic SQL and want to use a database name in our code, we are better off avoiding hard-coding them. Unfortunately, I could not find an easy way to access a connection manager&#8217;s database name and on my current project the catalog name is not in the SSIS configurations XML file. Therefore, I had to resort to a little trick to pull the database name out and pass it to a stored procedure. In brief we can do the following:</p>
<blockquote><p>1. Create a user variable <strong>database_name</strong></p>
<p>2. Create an <strong>Execute SQL Task</strong> using the connection manager we want to get the database name from, which does:</p>
<p><span style="color: #800000;"> SELECT db_name() AS database_name</span><br />
 <br />
3. Map the <strong>Single Row</strong> result set to our<strong> database_name</strong> variable </p>
<p>4. Place the task created in the previous step before any components which would be using the variable.</p>
<p>5. Pass the variable to our dynamic SQL stored procedure</p></blockquote>
<p style="text-align: center;"> <a href="http://www.bp-msbi.com/wp-content/uploads/2010/06/ssis_database_name.png"><img class="aligncenter size-large wp-image-90" title="SSIS Database Name Control Flow" src="http://www.bp-msbi.com/wp-content/uploads/2010/06/ssis_database_name-1024x655.png" alt="" width="645" height="412" /></a></p>
<p> </p>
<p>There we go &#8211; a stored procedure configured in the SSIS package configurations &#8211; a bit better than just hard-coding the name.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.bp-msbi.com/2010/06/passing-database-names-to-ssis-stored-procedures/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>SQL Server DBMS Top 1 Wish List</title>
		<link>http://www.bp-msbi.com/2009/12/sql-server-dbms-top-1-wish-list/</link>
		<comments>http://www.bp-msbi.com/2009/12/sql-server-dbms-top-1-wish-list/#comments</comments>
		<pubDate>Tue, 08 Dec 2009 03:23:00 +0000</pubDate>
		<dc:creator>Boyan Penev</dc:creator>
				<category><![CDATA[T-SQL]]></category>
		<category><![CDATA[source control]]></category>
		<category><![CDATA[version control]]></category>

		<guid isPermaLink="false">http://www.bp-msbi.com/2009/12/sql-server-dbms-top-1-wish-list/</guid>
		<description><![CDATA[As an addition to Teo Lachev&#8217;s Top 10 Wishlists (SSAS and SSRS), I would like to contribute only 1 item to a possible SQL Server DBMS wishlist: 1. Source Control. Not SourceSafe source control, but rather an automated version out-of-the-box, not relying on developers to check in/out. Rather, it should track the changes to the [...]]]></description>
			<content:encoded><![CDATA[<p>As an addition to <a href="http://prologika.com/CS/blogs/">Teo Lachev&#8217;s Top 10 Wishlists </a>(SSAS and SSRS), I would like to contribute only 1 item to a possible SQL Server DBMS wishlist:</p>
<p>1. Source Control.</p>
<p>Not SourceSafe source control, but rather an automated version out-of-the-box, not relying on developers to check in/out. Rather, it should track the changes to the code as they are made, and a full version history should be available directly in the DBMS. It should not be too hard. After all, there is a nice database available, which can store code with its version numbers just like anything else.</p>
<p>This would make a lot of developers&#8217; lives a bit less frustrating.</p>
<p>OK, a SQL code &#8220;beautifier&#8221; would also be nice, but it is not all that important&#8230;</p>
<p><a href="https://connect.microsoft.com/SQLServer/feedback/ViewFeedback.aspx?FeedbackID=518835" class="broken_link">Vote on Connect</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.bp-msbi.com/2009/12/sql-server-dbms-top-1-wish-list/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Moving writeback data in the Fact tables and avoiding problems with changing column names</title>
		<link>http://www.bp-msbi.com/2009/01/moving-writeback-data-in-fact-tables/</link>
		<comments>http://www.bp-msbi.com/2009/01/moving-writeback-data-in-fact-tables/#comments</comments>
		<pubDate>Mon, 12 Jan 2009 04:56:00 +0000</pubDate>
		<dc:creator>Boyan Penev</dc:creator>
				<category><![CDATA[SSAS]]></category>
		<category><![CDATA[Analysis Services]]></category>
		<category><![CDATA[planning]]></category>
		<category><![CDATA[T-SQL]]></category>
		<category><![CDATA[writeback]]></category>

		<guid isPermaLink="false">http://www.bp-msbi.com/2009/01/moving-writeback-data-in-the-fact-tables-and-avoiding-problems-with-changing-column-names/</guid>
		<description><![CDATA[While writeback functionality in SQL Server Analysis Services 2008 has changed significantly and writeback values are stored in the OLAP cubes, in SSAS 2005 the writeback values are stored in a relational table on the same server with the fact tables. When the writeback functionality is enabled for a partition, a new table is automatically [...]]]></description>
			<content:encoded><![CDATA[<p>While writeback functionality in SQL Server Analysis Services 2008 has changed significantly and writeback values are stored in the OLAP cubes, in SSAS 2005 the writeback values are stored in a relational table on the same server with the fact tables. When the writeback functionality is enabled for a partition, a new table is automatically created which bears a prefix of WriteTable. Its structure is fairly simple: it contains a column for each dimension and two audit fields.</p>
<p>The ROLAP nature of the writeback table makes it inefficient for storage of a large number of writeback records, and it is sometimes required to consolidate the data it contains with the fact table.</p>
<p>Normally we can write a stored procedure, which can do this for us. Because the values in the WriteTable are deltas there is a new row for each user change. In example, if we change 0 to 5, there will be one row in the writeback table, which shows 5 as a measure value. If then we change the new value of 5 to 2, there will be a new row with a measure value of -3. Therefore, it could be more efficient to perform a quick aggregation of the values in the WriteTable while moving them in the fact table. This could also be contrary to our requirements if we want to be able to trace all data changes.</p>
<p>In either case, we end up with a number of new rows and we can insert these into our fact table, after which we can truncate our WriteTable and process our cube. There is a potential pitfall here. If we do not set up properly the processing settings, we could destroy our WriteTable and have it re-created, which in turn introduces another pitfall &#8211; SSAS may change our column suffixes. In example, if we have a fact table with the following definition:</p>
<p><span style="font-family: Courier New; color: #804000;">CREATE TABLE [Fact_IndicatorAmount](<br />
[Fact_IndicatorAmount_Id] [int],<br />
[ETL_Date] [timestamp],<br />
[Indicator_Id] [int],<br />
[Region_Id] [int],<br />
[Scenario_Id] [int],<br />
[Date_Id] [datetime],<br />
[High] [float],<br />
[Low] [float],<br />
[Amount] [float]<br />
)</span></p>
<p>The WriteTable may be created like this:</p>
<p><span style="font-family: Courier New; color: #804040;">CREATE TABLE [WriteTable_Indicator Amount](<br />
[High_0] [float],<br />
[Low_1] [float],<br />
[Amount_2] [float],<br />
[Indicator_Id_3] [int],<br />
[Region_Id_4] [int],<br />
[Scenario_Id_5] [int],<br />
[Date_Id_6] [datetime],<br />
[MS_AUDIT_TIME_8] [datetime],<br />
[MS_AUDIT_USER_9] [nvarchar](255)<br />
)</span></p>
<p>Note how the column names are the same as the fact table column names, but are suffixed with _1, _2, etc. Unfortunately, these may change with the re-creation of the WriteTable. SSAS tends to assign the suffixes randomly. If that happens, our consolidation stored procedures will break.</p>
<p>The obvious step to avoid this is to set up our cube processing correctly, making sure that the WriteTable does not get re-created. To do this, we can select Use Existing writeback table in the Change Settings&#8230; dialog, which allows us to change cube processing settings:</p>
<p><a href="http://lh5.ggpht.com/_BMa6MDrkUyA/SWrNWtMY6RI/AAAAAAAABf8/dMMGpsCsvss/s1600-h/image%5B7%5D.png"><img style="border: 0px;" src="http://lh4.ggpht.com/_BMa6MDrkUyA/SWrNYeU4K1I/AAAAAAAABgA/8E0ZIvHo6Ck/image_thumb%5B3%5D.png?imgmax=800" border="0" alt="image" width="409" height="140" /></a></p>
<p>We can also script this action and use it in our automated cube processing SQL Server job.</p>
<p>Even though this is a relatively intuitive and simple solution, I have always had problems with it because of manual cube processing performed by power users, which do destroy the writeback data together with the WriteTable structure and following from that, the code in my stored procedures.</p>
<p>Through the utilisation of some dynamic SQL and SQL Server system tables information, we can write a stored procedure which does not depend on the suffixes of the column names in the writeback table:</p>
<p><span style="font-family: Courier New; color: #804040;">CREATE PROCEDURE [usp_Consolidate_WriteBack_to_Facts]<br />
AS<br />
BEGIN<br />
SET NOCOUNT ON; </span></p>
<p><span style="font-family: Courier New; color: #804040;">DECLARE @Column_High nvarchar(50),<br />
@Column_Low nvarchar(50),<br />
@Column_Amount nvarchar(50),<br />
@Column_Indicator nvarchar(50),<br />
@Column_Region nvarchar(50),<br />
@Column_Scenario nvarchar(50),<br />
@Column_Time nvarchar(50) </span></p>
<p><span style="font-family: Courier New; color: #804040;">SET @Column_High = (<br />
SELECT syscolumns.name<br />
FROM sysobjects<br />
INNER JOIN syscolumns<br />
ON sysobjects.id = syscolumns.id<br />
WHERE sysobjects.xtype=&#8217;U&#8217;<br />
AND sysobjects.name like &#8216;Write%&#8217;<br />
AND syscolumns.name like &#8216;High%&#8217;<br />
) </span></p>
<p><span style="font-family: Courier New; color: #804040;">SET @Column_Low = (<br />
SELECT syscolumns.name<br />
FROM sysobjects<br />
INNER JOIN syscolumns<br />
ON sysobjects.id = syscolumns.id<br />
WHERE sysobjects.xtype=&#8217;U&#8217;<br />
AND sysobjects.name like &#8216;Write%&#8217;<br />
AND syscolumns.name like &#8216;Low%&#8217;<br />
) </span></p>
<p><span style="font-family: Courier New; color: #804040;">SET @Column_Amount = (<br />
SELECT syscolumns.name<br />
FROM sysobjects<br />
INNER JOIN syscolumns<br />
ON sysobjects.id = syscolumns.id<br />
WHERE sysobjects.xtype=&#8217;U&#8217;<br />
AND sysobjects.name like &#8216;Write%&#8217;<br />
AND syscolumns.name like &#8216;Amount%&#8217;<br />
) </span></p>
<p><span style="font-family: Courier New; color: #804040;">SET @Column_Indicator = (<br />
SELECT syscolumns.name<br />
FROM sysobjects<br />
INNER JOIN syscolumns<br />
ON sysobjects.id = syscolumns.id<br />
WHERE sysobjects.xtype=&#8217;U&#8217;<br />
AND sysobjects.name like &#8216;Write%&#8217;<br />
AND syscolumns.name like &#8216;Indicator%&#8217;<br />
) </span></p>
<p><span style="font-family: Courier New; color: #804040;">SET @Column_Region = (<br />
SELECT syscolumns.name<br />
FROM sysobjects<br />
INNER JOIN syscolumns<br />
ON sysobjects.id = syscolumns.id<br />
WHERE sysobjects.xtype=&#8217;U&#8217;<br />
AND sysobjects.name like &#8216;Write%&#8217;<br />
AND syscolumns.name like &#8216;Region%&#8217;<br />
) </span></p>
<p><span style="font-family: Courier New; color: #804040;">SET @Column_Scenario = (<br />
SELECT syscolumns.name<br />
FROM sysobjects<br />
INNER JOIN syscolumns<br />
ON sysobjects.id = syscolumns.id<br />
INNER JOIN systypes<br />
ON syscolumns.xtype=systypes.xtype<br />
WHERE sysobjects.xtype=&#8217;U&#8217;<br />
AND sysobjects.name like &#8216;Write%&#8217;<br />
AND syscolumns.name like &#8216;Scenario%&#8217;<br />
) </span></p>
<p><span style="font-family: Courier New; color: #804040;">SET @Column_Time = (<br />
SELECT syscolumns.name<br />
FROM sysobjects<br />
INNER JOIN syscolumns<br />
ON sysobjects.id = syscolumns.id<br />
WHERE sysobjects.xtype=&#8217;U&#8217;<br />
AND sysobjects.name like &#8216;Write%&#8217;<br />
AND syscolumns.name like &#8216;Date%&#8217;<br />
) </span></p>
<p><span style="font-family: Courier New; color: #804040;">DECLARE @SQL_Command nvarchar(4000)<br />
SET @SQL_Command = (&#8216;<br />
INSERT INTO [Fact_IndicatorAmount]<br />
([High]<br />
,[Low]<br />
,[Amount]<br />
,[Indicator_Id]<br />
,[Region_Id]<br />
,[Scenario_Id]<br />
,[Date_Id])<br />
SELECT &#8216;+ @Column_High +&#8217;<br />
,&#8217;+ @Column_Low +&#8217;<br />
,&#8217;+ @Column_Amount +&#8217;<br />
,&#8217;+ @Column_Indicator +&#8217;<br />
,&#8217;+ @Column_Region +&#8217;<br />
,&#8217;+ @Column_Scenario +&#8217;<br />
,&#8217;+ @Column_Time +&#8217;<br />
FROM [WriteTable_Indicator Amount]&#8216;) </span></p>
<p><span style="font-family: Courier New; color: #804040;">EXEC (@SQL_Command) </span></p>
<p><span style="font-family: Courier New; color: #804040;">TRUNCATE TABLE [WriteTable_Indicator Amount]<br />
END</span></p>
<p>What we are effectively doing here is getting the column names from the WriteTable and then constructing an INSERT statement based on these. It is dangerous to further automate this by a while loop, as the actual column names in the WriteTable can differ from the ones in the fact table. This could happen if the dimension table key names are different to the fact table key names.</p>
<p>Moving writeback rows through this stored procedure ensures that even if the WriteTable for a partition is re-created for some reason our code can handle it.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.bp-msbi.com/2009/01/moving-writeback-data-in-fact-tables/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Spreading Non-Transactional Data Along Time</title>
		<link>http://www.bp-msbi.com/2008/12/spreading-non-transactional-data-along/</link>
		<comments>http://www.bp-msbi.com/2008/12/spreading-non-transactional-data-along/#comments</comments>
		<pubDate>Mon, 08 Dec 2008 12:18:00 +0000</pubDate>
		<dc:creator>Boyan Penev</dc:creator>
				<category><![CDATA[SSAS]]></category>
		<category><![CDATA[T-SQL]]></category>
		<category><![CDATA[dimensions]]></category>
		<category><![CDATA[planning]]></category>

		<guid isPermaLink="false">http://www.bp-msbi.com/2008/12/spreading-non-transactional-data-along-time/</guid>
		<description><![CDATA[In some cases we need to be able to analyse non-transactional data for discrete periods along a time dimension. An example of such a case is a collection of invoices, which have start and end dates for a period, but are not otherwise connected to a time axis. We may have such invoices with these [...]]]></description>
			<content:encoded><![CDATA[<p>In some cases we need to be able to analyse non-transactional data for discrete periods along a time dimension. An example of such a case is a collection of invoices, which have start and end dates for a period, but are not otherwise connected to a time axis. We may have such invoices with these properties:</p>
<blockquote><p>Invoice Id<br />
Start Date<br />
End Date<br />
Amount</p></blockquote>
<p>One of the invoices may be:</p>
<blockquote><p>Invoice Id: 34821432<br />
Start Date: 2008-10-15<br />
End Date: 2009-03-14<br />
Amount: 15,000.00</p></blockquote>
<p>and another one:</p>
<blockquote><p>Invoice Id: 34934221<br />
Start Date: 2008-12-01<br />
End Date: 2009-05-30<br />
Amount: 6,500.00</p></blockquote>
<p>If the company we are building this for is daily deducting a fee for its services (e.g. funds management, software maintenance, etc.), we may have to be able to spread the amount in smaller periods, like months or days and then aggregate the smaller amounts along a time dimension.</p>
<p>To do this we have to first store the data in a relational table and then write some SQL to do the trick for us.</p>
<p>First, we should create a table valued function which returns all the dates at a specified granularity, such as days, from the Start to the End dates and the count of all the periods in between (in our case is is a count of days):</p>
<blockquote><p>CREATE FUNCTION udf_Create_Daily_Date_Spread<br />
(   <br />
      @Start_Date datetime<br />
    , @End_Date datetime<br />
)<br />
RETURNS @Daily_Spread TABLE (<br />
      Date_Id datetime<br />
    , Count_Of_Days int<br />
)<br />
AS<br />
BEGIN<br />
    DECLARE @Count int<br />
    SET @Count = 0</p>
<p>    IF @Start_Date &gt;= @End_Date<br />
        RETURN</p>
<p>    WHILE @Start_Date &lt;= @End_Date<br />
    BEGIN<br />
        INSERT INTO @Daily_Spread(Date_Id)<br />
        SELECT @Start_Date</p>
<p>        SET @Start_Date = DATEADD(d, 1,@Start_Date)<br />
        SET @Count = @Count + 1<br />
    END</p>
<p>    UPDATE @Daily_Spread<br />
    SET   Count_Of_Days = @Count</p>
<p>    RETURN<br />
END</p></blockquote>
<p>After having created these functions, we can use the CROSS APPLY statement to create the even spread:</p>
<blockquote><p>SELECT             Invoice_Id<br />
                        ,Start_Date<br />
                        ,End_Date<br />
                        ,cdds.Date_Id<br />
                        ,Amount/cdds.Count_Of_Days<br />
FROM Invoice_Source inv<br />
CROSS APPLY udf_Create_Daily_Date_Spread(inv.Start_Date, inv.End_Date) cdds</p></blockquote>
<p>After running the sample data through this code, we will get an even spread for both invoices and we will be able to attach a time dimension to them.</p>
<p>Even though the data size may explode after such a manipulation, Analysis Services provides an excellent way of handling even the largest sets of data. If storage is a problem, we can always choose to break down our data in less periods &#8211; instead of days, weeks or months.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.bp-msbi.com/2008/12/spreading-non-transactional-data-along/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Ordering Dimensions: Recursion vs Loops in T-SQL</title>
		<link>http://www.bp-msbi.com/2008/11/ordering-dimensions-recursion-vs-loops/</link>
		<comments>http://www.bp-msbi.com/2008/11/ordering-dimensions-recursion-vs-loops/#comments</comments>
		<pubDate>Fri, 21 Nov 2008 00:25:00 +0000</pubDate>
		<dc:creator>Boyan Penev</dc:creator>
				<category><![CDATA[T-SQL]]></category>
		<category><![CDATA[recursion vs loops]]></category>
		<category><![CDATA[stored procedures]]></category>

		<guid isPermaLink="false">http://www.bp-msbi.com/2008/11/ordering-dimensions-recursion-vs-loops-in-t-sql/</guid>
		<description><![CDATA[Recently I had to build a function, which returned dimension members from a parent-child relational table. I needed to be able to order the members hierarchically and I decided to use a recursive CTE call. Usually, we have a parent-child dimension table with structure similar to this: Dim_Business Business_Skey int Parent_Business_Skey int   with a root [...]]]></description>
			<content:encoded><![CDATA[<div>Recently I had to build a function, which returned dimension members from a parent-child relational table. I <span id="SPELLING_ERROR_0" class="blsp-spelling-corrected">needed</span> to be able to order the members hierarchically and I decided to use a recursive <span id="SPELLING_ERROR_1" class="blsp-spelling-error">CTE</span> call.</div>
<div>Usually, we have a parent-child dimension table with structure similar to this:</div>
<blockquote>
<div><span class="Apple-style-span" style="font-weight: bold;">Dim_Business</span><span class="Apple-style-span" style="font-weight: bold;"><br />
</span></div>
<div><span class="Apple-style-span" style="font-weight: bold;">Business_<span id="SPELLING_ERROR_2" class="blsp-spelling-error">Skey</span> int</span></div>
<div><span class="Apple-style-span" style="font-weight: bold;">Parent_Business_<span id="SPELLING_ERROR_3" class="blsp-spelling-error">Skey</span> int<br />
</span></div>
</blockquote>
<div> </div>
<div>with a root node with <span id="SPELLING_ERROR_4" class="blsp-spelling-error">Skey</span> of -3 and Parent_Business_<span id="SPELLING_ERROR_5" class="blsp-spelling-error">Skey</span> of -3.</div>
<div>Let&#8217;s assume we have a hierarchy like this:</div>
<blockquote>
<div><span class="Apple-style-span" style="color: #000066;">All (Id = -3, P_ID = -3)</span></div>
<div><span class="Apple-style-span" style="color: #000066;">-GLOBAL (Id = 1, P_ID = -3)</span></div>
<div><span class="Apple-style-span" style="color: #000066;">&#8211;Europe (Id = 2, P_ID = 1)</span></div>
<div><span class="Apple-style-span" style="color: #000066;">&#8212;UK (Id = 3, P_ID = 2)</span></div>
<div><span class="Apple-style-span" style="color: #000066;">&#8212;France (Id = 4, P_ID = 2)</span></div>
<div><span class="Apple-style-span" style="color: #000066;">&#8212;Spain (Id = 5, P_ID = 2)</span></div>
<div><span class="Apple-style-span" style="color: #000066;">&#8211;North America (Id = 6, P_ID = 1)</span></div>
<div><span class="Apple-style-span" style="color: #000066;">&#8212;USA (Id = 7, P_ID = 6)</span></div>
<div><span class="Apple-style-span" style="color: #000066;">&#8212;Canada (Id = 8, P_ID = 6)</span></div>
<div><span class="Apple-style-span" style="color: #000066;">&#8212;Mexico (Id = 9, P_ID = 6)<br />
</span></div>
</blockquote>
<div> </div>
<div>If we do something like:</div>
<blockquote>
<div><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';">SELECT Business_<span id="SPELLING_ERROR_6" class="blsp-spelling-error">Skey</span></span></span></div>
<div><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';">FROM Dim_Business</span></span></div>
<div><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';">ORDER BY Parent_Business_<span id="SPELLING_ERROR_7" class="blsp-spelling-error">Skey</span> <span id="SPELLING_ERROR_8" class="blsp-spelling-error">ASC</span>, Business_<span id="SPELLING_ERROR_9" class="blsp-spelling-error">Skey</span> <span id="SPELLING_ERROR_10" class="blsp-spelling-error">ASC<br />
</span></span></span></div>
</blockquote>
<div> </div>
<div>We will get:</div>
<blockquote>
<div>-3 (All)</div>
<div>1 (GLOBAL)</div>
<div>2 (Europe)</div>
<div>6 (North America)</div>
<div>3 (UK)</div>
<div>4 (France)</div>
<div>5 (Spain)</div>
<div>7 (USA)</div>
<div>8 (Canada)</div>
<div>9 (Mexico)</div>
</blockquote>
<div>Obviously, this hierarchy is incorrect, because we want to see the leaf nodes under their respective parents.</div>
<div>We can recursively create order, which concatenates the parent ids:</div>
<blockquote>
<div><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';">WITH b_<span id="SPELLING_ERROR_11" class="blsp-spelling-error">ord</span>(bid, <span id="SPELLING_ERROR_12" class="blsp-spelling-error">bord</span>)</span></span></div>
<div><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';">AS</span></span></div>
<div><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';">(</span></span></div>
<div><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';">SELECT</span></span><span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';"> </span></span></span><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';"> Business_<span id="SPELLING_ERROR_13" class="blsp-spelling-error">Skey</span> AS bid</span></span></div>
<div><span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';"> </span></span></span><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';">, CONVERT(<span id="SPELLING_ERROR_14" class="blsp-spelling-error">nvarchar</span>(1000), Business_<span id="SPELLING_ERROR_15" class="blsp-spelling-error">Skey</span>) AS <span id="SPELLING_ERROR_16" class="blsp-spelling-error">bord</span></span></span></div>
<div><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';">FROM Dim_Business</span></span></div>
<div><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';">WHERE Business_<span id="SPELLING_ERROR_17" class="blsp-spelling-error">Skey</span> = -3</span></span></div>
<div><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';"><br />
</span></span></div>
<div><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';">UNION ALL</span></span></div>
<div><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';"><br />
</span></span></div>
<div><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';">SELECT</span></span><span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';"> </span></span></span><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';"> Business_<span id="SPELLING_ERROR_18" class="blsp-spelling-error">Skey</span> AS bid</span></span></div>
<div><span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';"> </span></span></span><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';">, CONVERT(<span id="SPELLING_ERROR_19" class="blsp-spelling-error">nvarchar</span>(1000), <span id="SPELLING_ERROR_20" class="blsp-spelling-error">bord</span> + &#8216;|&#8217; + CONVERT(<span id="SPELLING_ERROR_21" class="blsp-spelling-error">nvarchar</span>, Business_<span id="SPELLING_ERROR_22" class="blsp-spelling-error">Skey</span>))</span></span></div>
<div><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';">FROM Dim_Business db</span></span></div>
<div><span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';"> </span></span></span><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';">INNER JOIN b_<span id="SPELLING_ERROR_23" class="blsp-spelling-error">ord</span> <span id="SPELLING_ERROR_24" class="blsp-spelling-error">bo</span></span></span></div>
<div><span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';"> </span></span></span><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';">ON db.Parent_Business_<span id="SPELLING_ERROR_25" class="blsp-spelling-error">Skey</span> = <span id="SPELLING_ERROR_26" class="blsp-spelling-error">bo</span>.bid</span></span></div>
<div><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';">WHERE db.Business_<span id="SPELLING_ERROR_27" class="blsp-spelling-error">Skey</span> &lt;&gt; <span id="SPELLING_ERROR_28" class="blsp-spelling-error">bo</span>.bid</span></span></div>
<div><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';">)</span></span></div>
<div><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';">SELECT *</span></span></div>
<div><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';">FROM b_<span id="SPELLING_ERROR_29" class="blsp-spelling-error">ord</span></span></span></div>
<div><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';">ORDER BY <span id="SPELLING_ERROR_30" class="blsp-spelling-error">bord</span> <span id="SPELLING_ERROR_31" class="blsp-spelling-error">ASC<br />
</span></span></span></div>
</blockquote>
<div> </div>
<div>The result of the <span id="SPELLING_ERROR_32" class="blsp-spelling-error">CTE </span>query is:</div>
<blockquote>
<div>-3 -3 (All)</div>
<div>1 -3|1 (GLOBAL)</div>
<div>2 -3|1|2 (Europe)</div>
<div>3 -3|1|2|3 (UK)</div>
<div>4 -3|1|2|4 (France)</div>
<div>5 -3|1|2|5 (Spain)</div>
<div>6 -3|1|6 (North America)</div>
<div>7 -3|1|6|7 (USA)</div>
<div>8 -3|1|6|8 (Canada)</div>
<div>9 -3|1|6|9 (Mexico)</div>
</blockquote>
<div> </div>
<div>and the order is correct.</div>
<div> </div>
<div>Because the code needed to go in a function, invoked by a number of stored procedures, .NET application and various reports, I needed the code to be quick and </div>
<div>light. As some dimensions had a large number of members (50000+), which could grow with time, the code needed to implemented in a careful way. So, I decided </div>
<div>to compare the recursive <span id="SPELLING_ERROR_33" class="blsp-spelling-error">CTE</span> function to a WHILE loop and a temporary table implementation:</div>
<blockquote>
<div><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';">DECLARE @c int</span></span></div>
<div><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';">DECLARE @<span id="SPELLING_ERROR_34" class="blsp-spelling-error">num</span>_of_nodes int</span></span></div>
<div><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';"><br />
</span></span></div>
<div><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';">SET @<span id="SPELLING_ERROR_35" class="blsp-spelling-error">num</span>_of_nodes = (SELECT  COUNT(*) FROM Dim_Business)</span></span></div>
<div><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';"><br />
</span></span></div>
<div><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';">CREATE TABLE #order(</span></span></div>
<div><span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';"> </span></span></span><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';"> <span id="SPELLING_ERROR_36" class="blsp-spelling-error">skey</span> int</span></span></div>
<div><span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';"> </span></span></span><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';">, <span id="SPELLING_ERROR_37" class="blsp-spelling-error">ord</span> <span id="SPELLING_ERROR_38" class="blsp-spelling-error">nvarchar</span>(1000)</span></span></div>
<div><span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';"> </span></span></span><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';">, <span id="SPELLING_ERROR_39" class="blsp-spelling-error">lvl</span> int</span></span></div>
<div><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';">)</span></span></div>
<div><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';"><br />
</span></span></div>
<div><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';">INSERT INTO #order</span></span></div>
<div><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';">SELECT</span></span><span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';"> </span></span></span><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';"> Business_<span id="SPELLING_ERROR_40" class="blsp-spelling-error">Skey</span></span></span></div>
<div><span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';"> </span></span></span><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';">, CONVERT(<span id="SPELLING_ERROR_41" class="blsp-spelling-error">nvarchar</span>(1000), Business_<span id="SPELLING_ERROR_42" class="blsp-spelling-error">Skey</span>)</span></span></div>
<div><span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';"> </span></span></span><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';">, 1</span></span></div>
<div><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';">FROM Dim_Business</span></span></div>
<div><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';">WHERE Business_<span id="SPELLING_ERROR_43" class="blsp-spelling-error">Skey</span> = -3</span></span></div>
<div><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';"><br />
</span></span></div>
<div><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';">SET @c = 2</span></span></div>
<div><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';"><br />
</span></span></div>
<div><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';">WHILE @c &gt; 0</span></span></div>
<div><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';">BEGIN</span></span></div>
<div><span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';"> </span></span></span><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';">INSERT INTO #order</span></span></div>
<div><span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';"> </span></span></span><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';">SELECT</span></span><span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';"> </span></span></span><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';"> Business_<span id="SPELLING_ERROR_44" class="blsp-spelling-error">Skey</span></span></span></div>
<div><span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';"> </span></span></span><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';">, CONVERT(<span id="SPELLING_ERROR_45" class="blsp-spelling-error">nvarchar</span>(1000), <span id="SPELLING_ERROR_46" class="blsp-spelling-error">ord</span> + &#8216;|&#8217; + CONVERT(<span id="SPELLING_ERROR_47" class="blsp-spelling-error">nvarchar</span>, Business_<span id="SPELLING_ERROR_48" class="blsp-spelling-error">Skey</span>))</span></span></div>
<div><span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';"> </span></span></span><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';">, @c</span></span></div>
<div><span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';"> </span></span></span><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';">FROM Dim_Business db</span></span></div>
<div><span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';"> </span></span></span><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';">INNER JOIN #order o</span></span></div>
<div><span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';"> </span></span></span><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';">ON db.Parent_Business_<span id="SPELLING_ERROR_49" class="blsp-spelling-error">Skey</span> = o.<span id="SPELLING_ERROR_50" class="blsp-spelling-error">skey</span></span></span></div>
<div><span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';"> </span></span></span><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';">AND o.<span id="SPELLING_ERROR_51" class="blsp-spelling-error">lvl</span> = @c &#8211; 1</span></span></div>
<div><span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';"> </span></span></span><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';">WHERE db.Business_<span id="SPELLING_ERROR_52" class="blsp-spelling-error">Skey</span> &lt;&gt; o.<span id="SPELLING_ERROR_53" class="blsp-spelling-error">skey</span></span></span></div>
<div><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';"><br />
</span></span></div>
<div><span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';"> </span></span></span><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';">SET @c = @c + 1</span></span></div>
<div><span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';"> </span></span></span></div>
<div><span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';"> </span></span></span><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';">IF (SELECT COUNT(*) FROM #order) = @num_of_nodes</span></span></div>
<div><span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';"> </span></span></span><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';">SET @c = 0</span></span></div>
<div><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';">END</span></span></div>
<div><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';"><br />
</span></span></div>
<div><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';">SELECT</span></span><span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';"> </span></span></span><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';"> skey AS bid</span></span></div>
<div><span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';"> </span></span></span><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';">, ord AS bord</span></span></div>
<div><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';">FROM #order</span></span></div>
<div><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';">ORDER BY ord ASC</span></span></div>
<div><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';"><br />
</span></span></div>
<div><span class="Apple-style-span" style="color: #993300;"><span class="Apple-style-span" style="font-family: 'courier new';">DROP TABLE #order<br />
</span></span></div>
</blockquote>
<div> </div>
<div>After comparing the results in Client Statistics and the IO reads, the 1st recursive query performs more than 20% worse than the WHILE loop query. It also trails the non-recursive query in the count of logical reads,read-ahead reads and scan counts.</div>
<div>It seems like in SQL Server 2005 calling WITH recursively does not work as good as coding set-based operations through WHILE loops.</div>
]]></content:encoded>
			<wfw:commentRss>http://www.bp-msbi.com/2008/11/ordering-dimensions-recursion-vs-loops/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Fun with T-SQL</title>
		<link>http://www.bp-msbi.com/2008/11/fun-with-t-sql/</link>
		<comments>http://www.bp-msbi.com/2008/11/fun-with-t-sql/#comments</comments>
		<pubDate>Sat, 15 Nov 2008 02:22:00 +0000</pubDate>
		<dc:creator>Boyan Penev</dc:creator>
				<category><![CDATA[T-SQL]]></category>

		<guid isPermaLink="false">http://www.bp-msbi.com/2008/11/fun-with-t-sql/</guid>
		<description><![CDATA[As a tribute to some fairly restrictive .NET design I once fell victim to, I wrote this piece of code: Use [Database] Select [Select] From [From] Where [Where] = &#8216;Where&#8217; And [nvarchar(50)] = &#8216;nvarchar(50)&#8217; And [int] = 1 The CREATE and INSERT statements for the above command:   CREATE TABLE [dbo].[From](  [Id] [int] NOT NULL, [...]]]></description>
			<content:encoded><![CDATA[<p>As a tribute to some fairly restrictive .NET design I once fell victim to, I wrote this piece of code:</p>
<div>
<blockquote>
<div><span style="color: #993300;">Use [Database]</span></div>
<div><span class="Apple-style-span" style="color: #993300;">Select [Select]</span></div>
<div><span class="Apple-style-span" style="color: #993300;">From [From]</span></div>
<div><span class="Apple-style-span" style="color: #993300;">Where [Where] = &#8216;Where&#8217;</span></div>
<div><span class="Apple-style-span" style="color: #993300;">And [nvarchar(50)] = &#8216;nvarchar(50)&#8217;</span></div>
<div><span class="Apple-style-span" style="color: #993300;">And [int] = 1</span></div>
</blockquote>
<div>The CREATE and INSERT statements for the above command:</div>
<div> </div>
<div>
<blockquote>
<div><span class="Apple-style-span" style="color: #993300;">CREATE TABLE [dbo].[From](</span></div>
<div><span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="color: #993300;"> </span></span><span class="Apple-style-span" style="color: #993300;">[Id] [int] NOT NULL,</span></div>
<div><span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="color: #993300;"> </span></span><span class="Apple-style-span" style="color: #993300;">[Where] [nvarchar](50) NOT NULL,</span></div>
<div><span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="color: #993300;"> </span></span><span class="Apple-style-span" style="color: #993300;">[On] [nvarchar](50) NOT NULL,</span></div>
<div><span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="color: #993300;"> </span></span><span class="Apple-style-span" style="color: #993300;">[Select] [nvarchar](50) NOT NULL,</span></div>
<div><span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="color: #993300;"> </span></span><span class="Apple-style-span" style="color: #993300;">[From] [nvarchar](50) NOT NULL,</span></div>
<div><span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="color: #993300;"> </span></span><span class="Apple-style-span" style="color: #993300;">[int] [int] NOT NULL,</span></div>
<div><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-style-span" style="color: #993300;">[nvarchar(50)] [nvarchar](50) NOT NULL</span></div>
<div><span class="Apple-style-span" style="color: #993300;">)</span></div>
<div><span class="Apple-style-span" style="color: #993300;"><br />
</span></div>
<div>
<div><span class="Apple-style-span" style="color: #993300;">INSERT INTO [From] (  [Id]</span></div>
<div><span class="Apple-style-span" style="color: #993300;">, [Where]</span></div>
<div><span class="Apple-style-span" style="color: #993300;">, [On]</span></div>
<div><span class="Apple-style-span" style="color: #993300;">, [Select]</span></div>
<div><span class="Apple-style-span" style="color: #993300;">, [From]</span></div>
<div><span class="Apple-style-span" style="color: #993300;">, [int]</span></div>
<div><span class="Apple-style-span" style="color: #993300;">, [nvarchar(50)])</span></div>
</div>
<div><span class="Apple-style-span" style="color: #993300;">SELECT 1, &#8216;Where&#8217;, &#8216;On&#8217;, &#8216;:-)&#8217;, &#8216;From&#8217;, 1, &#8216;nvarchar(50)&#8217;</span></div>
</blockquote>
<p>Enjoy!</p>
<div><span class="Apple-style-span" style="color: #993300;"> </span> </div>
<div> </div>
</div>
</div>
]]></content:encoded>
			<wfw:commentRss>http://www.bp-msbi.com/2008/11/fun-with-t-sql/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>EXECUTE AS someone else &#8211; impersonation in stored procedures</title>
		<link>http://www.bp-msbi.com/2008/11/recently-i-had-to-implement-stored/</link>
		<comments>http://www.bp-msbi.com/2008/11/recently-i-had-to-implement-stored/#comments</comments>
		<pubDate>Tue, 11 Nov 2008 00:55:00 +0000</pubDate>
		<dc:creator>Boyan Penev</dc:creator>
				<category><![CDATA[T-SQL]]></category>
		<category><![CDATA[stored procedures]]></category>

		<guid isPermaLink="false">http://www.bp-msbi.com/2008/11/execute-as-someone-else-impersonation-in-stored-procedures/</guid>
		<description><![CDATA[Recently, I had to implement a stored procedure, which is executed under a service account context. It had to be invoked by an Informatica workflow and its purpose was to clean a cache database table and to update all statistics on a database through invoking the sp_updatestats. As the Informatica user did not have enough [...]]]></description>
			<content:encoded><![CDATA[<div>Recently, I had to implement a stored procedure, which is executed under a service account context. It had to be invoked by an Informatica workflow and its purpose was to clean a cache database table and to update all statistics on a database through invoking the sp_updatestats. As the Informatica user did not have enough permissions to perform these tasks, I had to use the EXECUTE AS in the stored procedure. The EXECUTE AS statement can also effectively be used to limit the execution scope through allowing limited permissions to the user account executing the stored procedure:</div>
<blockquote>
<div>CREATE PROCEDURE [dbo].[usp_Update_Statistics] </div>
<div>WITH EXECUTE AS <span class="Apple-style-span" style="font-style: italic;">user</span></div>
<div>AS</div>
<div>BEGIN</div>
<div><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-style-span" style="font-style: italic;">clear cache</span></div>
<div><span class="Apple-tab-span" style="white-space: pre;"> </span>EXEC sp_UpdateStats</div>
<div>END</div>
</blockquote>
<div>Immediately I encountered various problems with the execution of the stored procedure. After some research it turned out that a few preconditions need to be satisfied before we can execute it:</div>
<blockquote>
<div>1. TRUSTWORTHY setting on the database needs to be turned on:</div>
<div>     ALTER DATABASE <span class="Apple-style-span" style="font-style: italic;">database</span> SET TRUSTWORTHY ON;</div>
<div>2. The owner of both the context database and the master database must be the same. To set this property we need to:</div>
<div><span class="Apple-tab-span" style="white-space: pre;">   </span>2.1 Find who is the owner of master:</div>
<div><span class="Apple-tab-span" style="white-space: pre;">           </span>exec sp_helpdb</div>
<div><span class="Apple-tab-span" style="white-space: pre;">   </span>2.2 Set the context database owner to the same owner:</div>
<div><span class="Apple-tab-span" style="white-space: pre;">           </span>ALTER AUTHORIZATION ON DATABASE::<span class="Apple-style-span" style="font-style: italic;">database </span>TO <span class="Apple-style-span" style="font-style: italic;">user</span></div>
<div>3. The account under which we EXECUTE AS needs to be a database/server principal. The database and server principals can be found in the <span class="Apple-style-span" style="font-weight: bold;">sys.database_principals</span> and the <span class="Apple-style-span" style="font-weight: bold;">sys.server_principals</span> tables.</div>
<div>4. The account needs to be granted impersonate permissions:</div>
<div>     GRANT IMPERSONATE ON USER::<span class="Apple-style-span" style="font-style: italic;">user in EXECUTE AS</span> TO <span class="Apple-style-span" style="font-style: italic;">user executing the sproc</span></div>
<div>5. If a server login is specified (instead of a database user), it needs to be mapped to a database user.</div>
</blockquote>
<div>Also, if SQL Server is running under a local account/service it is not possible to use the EXECUTE AS statement.</div>
<div>The same statement can be used to execute SQL Server Agent jobs under different context &#8211; something particularly useful when trying to run them through Integration Services.</div>
]]></content:encoded>
			<wfw:commentRss>http://www.bp-msbi.com/2008/11/recently-i-had-to-implement-stored/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

