<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
  version="2.0">
  <channel>
    <title>AndyRoberts</title>
    <atom:link
      href="https://docs.microsoft.com/archive/blogs/andyroberts/feed.xml"
      rel="self"
      type="application/rss+xml" />
    <link>https://docs.microsoft.com/archive/blogs/andyroberts/feed.xml</link>
    <description />
    <lastBuildDate>Wed, 17 Apr 2019 03:47:41 GMT</lastBuildDate>
    <language>en-US</language>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    <item>
      <title>More Fun with SQL Server “Denali” Window Functions</title>
      <link>https://docs.microsoft.com/archive/blogs/andyroberts/more-fun-with-sql-server-denali-window-functions</link>
      <pubDate>Mon, 22 Aug 2011 08:02:00 GMT</pubDate>
      <dc:creator><![CDATA[_Andy Roberts_]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/andyroberts/2011/08/22/more-fun-with-sql-server-denali-window-functions/</guid>
      <description><![CDATA[In my last post I described scenarios where the improvements to the SQL Server &ldquo;Denali&rdquo;...]]></description>
      <content:encoded><![CDATA[<p>In my last post I described scenarios where the improvements to the SQL Server &ldquo;Denali&rdquo; windows functions could address a common scenario of getting year to date sales from a list of monthly amounts.</p>
<p>In this post we will explore a couple of other advances in the Windows functions and how they might be applied to other scenarios.</p>
<p>The first scenario uses the AdvnetureWorksDWDenali database located at <a title="http://msftdbprodsamples.codeplex.com/releases/view/55330" href="http://msftdbprodsamples.codeplex.com/releases/view/55330">http://msftdbprodsamples.codeplex.com/releases/view/55330</a></p>
<p>In this example, let&rsquo;s look at the Sales.InternetSales table. Some common reporting requests might be:</p>
<p>1) What is the year to date sales by month for a given sales territory? <br />2) What were the average sales over the last 3 and 6 months shown as moving averages? <br />3) What is a given month&rsquo;s sales as a percentage of the year&rsquo;s total sales? <br />4) What is the year to date wales by month as a percentage of the tear&rsquo;s total sales? <br />5) What is a given sales territory&rsquo;s sales as a percentage for all sales in a given month?</p>
<p>Now, prior to the advancements in the window functions, some of these queries would be extremely difficult to do against a reporting database in an efficient manner. Chances are you would need to resort to temporary tables, creating work tables, scanning the base table multiple times, or bringing huge amounts of data into a client app where you could process the data more efficiently but incur large transfer times to move the data to your application tier.</p>
<p>So, what does this look like in SQL Server &ldquo;Denali&rdquo;?</p>
<p><br /><span style="font-family: Cordia New;" face="Cordia New">WITH Sales_CTE AS ( <br />&nbsp;&nbsp;&nbsp; SELECT SalesTerritoryKey <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; , OrderYear = YEAR(orderdate) <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; , OrderMonth = MONTH(orderdate) <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; , SalesAmount = SUM(SalesAmount) <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FROM dbo.FactInternetSales <br />&nbsp;&nbsp;&nbsp;&nbsp; WHERE CurrencyKey = 100 <br />&nbsp;&nbsp;&nbsp; GROUP BY SalesTerritoryKey, year(orderdate), month(orderdate) <br />&nbsp;&nbsp;&nbsp; ) <br />&nbsp;&nbsp;&nbsp; SELECT SalesTerritoryKey <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; , OrderYear <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; , OrderMonth <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; , SalesAmount <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; , YTDSales = SUM(SalesAmount) <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; OVER ( <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PARTITION BY SalesTerritoryKey, OrderYear <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ORDER BY OrderYear, OrderMonth <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ) <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; , Last3MonthAverageSales = Avg(SalesAmount) <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; OVER ( <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PARTITION BY SalesTerritoryKey <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ORDER BY OrderYear, OrderMonth <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ROWS BETWEEN 2 PRECEDING AND CURRENT ROW <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ) <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; , Last6MonthAverageSales = Avg(SalesAmount) <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; OVER ( <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PARTITION BY SalesTerritoryKey <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ORDER BY OrderYear, OrderMonth <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ROWS BETWEEN 5 PRECEDING AND CURRENT ROW <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ) <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; , CurrentMonthPctOfYear = SUM(SalesAmount) <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; OVER ( <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PARTITION BY SalesTerritoryKey, OrderYear <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ORDER BY OrderYear, OrderMonth <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ROWS CURRENT ROW <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ) <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /&nbsp; sum(SalesAmount) <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; OVER ( <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PARTITION BY SalesTerritoryKey, OrderYear <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ) <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; , YTDPCTOfYear = SUM(SalesAmount) <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; OVER ( <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PARTITION BY SalesTerritoryKey, OrderYear <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ORDER BY OrderYear, OrderMonth <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ) <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /&nbsp; sum(SalesAmount) <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; OVER ( <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PARTITION BY SalesTerritoryKey, OrderYear <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ) <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; , SalesTerritoryAsPctOfMonthlyTotal = SUM(SalesAmount) <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; OVER ( <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PARTITION BY SalesTerritoryKey <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ORDER BY OrderYear, OrderMonth <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ROWS CURRENT ROW <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ) <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /&nbsp; sum(SalesAmount) <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; OVER ( <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PARTITION BY OrderYear, OrderMonth <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ) <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FROM Sales_CTE <br />&nbsp;&nbsp;&nbsp;&nbsp; ORDER BY OrderYear, OrdermOnth, SalesTerritoryKey </span></p>
<p>Let&rsquo;s break this statement apart&hellip;..</p>
<p>In the first section we create a common table expression that gives us a view of the orders grouped by year, month and sales territory:</p>
<blockquote>
<p><br /><span style="font-family: Cordia New;" face="Cordia New">WITH Sales_CTE AS ( <br />SELECT SalesTerritoryKey <br />, OrderYear = YEAR(orderdate) <br />, OrderMonth = MONTH(orderdate) <br />, SalesAmount = SUM(SalesAmount) <br />FROM dbo.FactInternetSales <br />WHERE CurrencyKey = 100 <br />GROUP BY SalesTerritoryKey, year(orderdate), month(orderdate) <br />) </span></p>
</blockquote>
<p>This could have been accomplished with a derived table or a view&hellip; I have gotten in the habit of using common table expressions as this is the way that I think about the problem - first, group the data; then, query the data.</p>
<p><span style="font-family: Courier New;" face="Courier New">SELECT SalesTerritoryKey <br />, OrderYear <br />, OrderMonth <br />, SalesAmount <br />&hellip; <br />&hellip; <br />&hellip; <br />FROM Sales_CTE <br />ORDER BY SalesTerritoryKey, OrderYear, OrdermOnth</span></p>
<p>Here I am just writing the bulk of the query&hellip; leaving out the fun bits so that we can talk about them 1 by 1. <br />1) Year to Date Sales by month and territory.</p>
<blockquote>
<p><span style="font-family: Courier New;" face="Courier New">, YTDSales = SUM(SalesAmount) <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; OVER (&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PARTITION BY SalesTerritoryKey, OrderYear <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ORDER BY OrderMonth <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ) <br /></span>In this example we are creating a set of windows on the data defined by SalesTerritoryKey and OrderYear. Within this window we will order the data month. What we present to the aggregate function is the set or rows in that window from the first row and the row that is being evaluated.</p>
</blockquote>
<p align="left">2) 3 and 6 Month Moving Average</p>
<blockquote>
<p><span style="font-family: Courier New;" face="Courier New">, Last3MonthAverageSales = Avg(SalesAmount)&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; OVER (&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PARTITION BY SalesTerritoryKey&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ORDER BY OrderYear, OrderMonth <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ROWS BETWEEN 2 PRECEDING AND CURRENT ROW <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ) <br />, Last6MonthAverageSales = Avg(SalesAmount) <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; OVER (&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PARTITION BY SalesTerritoryKey&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ORDER BY OrderYear, OrderMonth <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ROWS BETWEEN 5 PRECEDING AND CURRENT ROW <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ) <br /></span><span style="font-family: Times New Roman;" face="Times New Roman">In this case we would like to present a different window to the aggregate. In Year to Date calculations we want to create a window bounded by the order year, in this case, we would like to create a larger window that encompasses all of the order for the sales territory. We order that data by year and month and then return the current row + 2 or 5 months prior depending on which average we are calculating.</span></p>
</blockquote>
<p>3 and 4) Current Month and YTD Sales as a Percentage of Total Year&rsquo;s Sales</p>
<blockquote>
<p><span style="font-family: Courier New;" face="Courier New">, CurrentMonthPctOfYear = SUM(SalesAmount) <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; OVER (&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PARTITION BY SalesTerritoryKey, OrderYear <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ORDER BY OrderYear, OrderMonth <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ROWS CURRENT ROW <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; )&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; / SUM(SalesAmount) <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; OVER ( <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PARTITION BY SalesTerritoryKey, OrderYear&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ) <br />, YTDPCTOfYear = SUM(SalesAmount) <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; OVER ( <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PARTITION BY SalesTerritoryKey, OrderYear <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ORDER BY OrderYear, OrderMonth <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; )&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; / SUM(SalesAmount) <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; OVER (&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PARTITION BY SalesTerritoryKey, OrderYear <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ) <br /></span>In this case you can see that the numerator and the denominator of these calculations are both sums of sales amounts just over different windows.</p>
</blockquote>
<p>5) Sales Territory Sales as a Pertcentage of the Current Month&rsquo;s sales</p>
<blockquote>
<p><span style="font-family: Courier New;" face="Courier New">, SalesTerritoryAsPctOfMonthlyTotal = SUM(SalesAmount) <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; OVER (&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PARTITION BY SalesTerritoryKey&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ORDER BY OrderYear, OrderMonth <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ROWS CURRENT ROW&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; )&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; / sum(SalesAmount) <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; OVER ( <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PARTITION BY OrderYear, OrderMonth <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ) <br /></span><span style="font-family: Times New Roman;" face="Times New Roman">This calculation acts orthogonally to the other calculations in the list! All the other calculations looked at the calculations within a sales territory, this looks at all of the sales territories. Notice that the denominator of this calculation partitions by OrderYear and OrderMonth while all the other functions partition by SalesTerritoryKey. </span></p>
<p></p>
</blockquote>
<p><span style="font-family: Courier New;" face="Courier New"></span></p>
<blockquote></blockquote>
<p>This is great for running totals which work for sales &ndash; but what about account balances? How do I find the opening or closing balance of a period? What if I would like to see year to date growth based on current balance compared to the year&rsquo;s opening balance? For this, we will shift to the dbo.FactFinance table that tracks account balances for AdventureWorks.</p>
<p>In this query we will look at the AdventureWorks Northeast Organization&rsquo;s Cash Account Balances.</p>
<p><span style="font-family: Courier New; font-size: x-small;" face="Courier New" size="2">SELECT Date <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; , Amount <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; , YTDGrowth = (Amount <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; / FIRST_VALUE(Amount)&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-family: Courier New; font-size: x-small;" face="Courier New" size="2">OVER (&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PARTITION BY YEAR(Date)&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ORDER BY MONTH(Date)&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ) <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ) - 1 <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; , Last3MonthGrowth = (Amount&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; / FIRST_VALUE(Amount)&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; OVER (&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PARTITION BY AccountKey&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ORDER BY YEAR(Date), MONTH(Date)&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ROWS BETWEEN 2 PRECEDING AND CURRENT ROW <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ) <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ) - 1 <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; , Last6MonthGrowth = (Amount&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; / FIRST_VALUE(Amount)&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; OVER (&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PARTITION BY AccountKey&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ORDER BY YEAr(Date), MONTH(Date)&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ROWS BETWEEN 5 PRECEDING AND CURRENT ROW&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; )&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ) - 1 <br />&nbsp; FROM dbo.FactFinance <br />WHERE AccountKey =&nbsp; 4 <br />&nbsp;&nbsp; AND OrganizationKey = 3</span></p>
<p>Here we use the FIRST_VALUE function to obtain the first cash balance for the year (in year to date growth) or the first balance in a window of 3 or 6 months for the other calculations.</p>
<p>There are other new window functions lag, lead and last_value that we did not cover today, but I hope that you start to see the patterns around how to use these functions and the general usage pattern to apply them to the problems you face today.</p>]]></content:encoded>
    </item>
    <item>
      <title>Re-summarizing data using UNPIVOT, ROW_NUMBER and CTEs–Denali Style</title>
      <link>https://docs.microsoft.com/archive/blogs/andyroberts/re-summarizing-data-using-unpivot-row_number-and-ctesdenali-style</link>
      <pubDate>Mon, 22 Aug 2011 07:30:00 GMT</pubDate>
      <dc:creator><![CDATA[_Andy Roberts_]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/andyroberts/2011/08/22/re-summarizing-data-using-unpivot-row_number-and-ctesdenali-style/</guid>
      <description><![CDATA[&nbsp;
Last month I did a write up on a common scenario that I have seen with my customers where...]]></description>
      <content:encoded><![CDATA[<p>&nbsp;</p>
<p>Last month I did a write up on a common scenario that I have seen with my customers where data needs to be re-summarized and the customer would like to avoid writing data into intermediate tables. For example, they may receive a flat file from an external source that has sales amount by month &ndash; but instead of each row containing a month&rsquo;s worth of data, each row contains a year&rsquo;s worth of data with one month in each column.</p>
<p>The first portion of the post we talked about unpivoting the data. This can be done in many ways &ndash; the most common using either SSIS or T-SQL&rsquo;s unpivot function. This would be similar in SQL Server Denali.</p>
<p>What has changed is our approach to re-summarizing the data. What if we wanted to look at YTD sales or 3 Month rolling totals. SQL Server has expanded the capabilities of the window functions beyond the partition and order clauses that we had in SQL Server 2008 R2.</p>
<p>To start with, create a table and populate that table with data as we did in the <a href="http://blogs.msdn.com/b/andyroberts/archive/2011/07/13/re-summarizing-data-using-unpivot-row-number-and-ctes.aspx">previous post</a>.</p>
<p>In SQL Server 2008 R2 we created a CTE that unpivoted the data, and then we joined that CTE on itself multiple times to create the offsets that we needed. This works OK for small-medium sized sets of data but we really need to do a lot of work to get these results.</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/45/96/metablogapi/6507.image_0A261066.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-45-96-metablogapi/6507.image_5F00_0A261066.png"><img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border-width: 0px;" title="image" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/45/96/metablogapi/4530.image_thumb_1000B3FF.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-45-96-metablogapi/4530.image_5F00_thumb_5F00_1000B3FF.png" width="893" height="363" /></a></p>
<p>This is a bit of an eye-chart. The intent here is not to go over every step of the execution plan but to show that there is a LOT of work that needs to take place to generate the results we need. Specifically, the base table is accessed, filtered and aggregated 5 times.</p>
<p>So, what improvements have been made in the next release of SQL Server and how is it going to help us here?</p>
<p>The next version of SQL Server includes a &ldquo;row or range&rdquo; clause to the <a href="http://msdn.microsoft.com/en-us/library/ms189461(v=SQL.110).aspx">&ldquo;over&rdquo; clause</a> of a select statement.</p>
<p>The new statement looks like this:</p>
<blockquote>
<p><span style="font-family: courier new,courier; font-size: xx-small;"><span face="Lucida Console" size="2">WITH unpivoted_cte as ( <br />&nbsp;&nbsp;&nbsp; SELECT VendorId = VendorId <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; , MonthAbbr = MonthAbbr <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; , Amount = Amount <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; , MonthId = row_number() over(partition by VendorId order by MonthAbbr) <br />&nbsp;&nbsp;&nbsp; FROM <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (SELECT VendorId <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; , _01_Jan = Jan <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; , _02_Feb = Feb <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; , _03_Mar = Mar <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; , _04_Apr = Apr <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; , _05_May = May <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; , _06_Jun = Jun <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; , _07_Jul = Jul <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; , _08_Aug = Aug <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; , _09_Sep = Sep <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; , _10_Oct = Oct <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; , _11_Nov = Nov <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; , _12_Dec = Dec <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FROM dbo.MonthlyData) AS p <br />&nbsp;&nbsp;&nbsp; UNPIVOT <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (Amount FOR MonthAbbr IN <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (_01_Jan, _02_Feb, _03_Mar, _04_Apr, _05_May, _06_Jun <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; , _07_Jul, _08_Aug, _09_Sep, _10_Oct, _11_Nov, _12_Dec <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ) <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; )AS unpvt <br />&nbsp;&nbsp;&nbsp; )&nbsp; <br />SELECT VendorId <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; , MonthId <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; , Amount <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; , Total_Ytd = SUM(Amount) OVER ( <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PARTITION BY VendorId <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ORDER BY MonthId&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ROWS UNBOUNDED PRECEDING <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ) <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; , Total_Rolling3 = SUM(Amount) OVER ( <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PARTITION BY VendorId&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span face="Lucida Console" size="2">ORDER BY MonthId&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ROWS BETWEEN 2 PRECEDING AND CURRENT ROW <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; )&nbsp; <br />&nbsp;&nbsp; FROM unpivoted_cte&nbsp; <br />&nbsp; WHERE VendorId = 999 </span></span></p>
</blockquote>
<p>The execution plan of this statement?</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/45/96/metablogapi/0638.image_327542BA.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-45-96-metablogapi/0638.image_5F00_327542BA.png"><img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border-width: 0px;" title="image" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/45/96/metablogapi/0310.image_thumb_7FA53C45.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-45-96-metablogapi/0310.image_5F00_thumb_5F00_7FA53C45.png" width="746" height="53" /></a></p>
<p>OK, Still a fair number of steps but it is a much more linear plan and the base table is only accessed the one time.</p>
<p>If I remove the where clause (where vendorId = 999) this query returns 120,000 rows based on the population script from last month. The old plan executes 6x slower than the new plan.</p>
<p>In future posts we will dive into some of the details of how this query works and look at some other improvements to the window functions that we can take advantage of in SQL Server Denali!</p>]]></content:encoded>
    </item>
    <item>
      <title>Re-summarizing data using UNPIVOT, ROW_NUMBER and CTEs</title>
      <link>https://docs.microsoft.com/archive/blogs/andyroberts/re-summarizing-data-using-unpivot-row_number-and-ctes</link>
      <pubDate>Wed, 13 Jul 2011 11:49:54 GMT</pubDate>
      <dc:creator><![CDATA[_Andy Roberts_]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/andyroberts/2011/07/13/re-summarizing-data-using-unpivot-row_number-and-ctes/</guid>
      <description><![CDATA[I had a question recently where a customer was presented a set of tables summarized by month and he...]]></description>
      <content:encoded><![CDATA[<p>I had a question recently where a customer was presented a set of tables summarized by month and he was asked to re-summarize the data giving year to date summaries. </p>  <p>This was a “one time” request so we wanted to get results quickly and then refine the process once the customer decided how/if they wanted to report on the data more frequently. In my quest to eliminate cursors, loops and temp tables form my set-based life I wanted to present a solution that was contained in one SQL Query. As we will see below, there are some drawbacks to my approach, but I think this is a good illustration of using <a href="http://msdn.microsoft.com/en-us/library/ms190766.aspx">common table expressions</a>, <a href="http://msdn.microsoft.com/en-us/library/ms189798.aspx">ranking functions</a> and the <a href="http://msdn.microsoft.com/en-us/library/ms177410.aspx">UNPIVOT</a> operator to solve a fairly common problem. </p>  <p>The structure of the source table was similar to: </p>  <blockquote>   <pre class="csharpcode"><span class="kwrd">CREATE</span> <span class="kwrd">TABLE</span> dbo.MonthlyData (
    VendorId <span class="kwrd">int</span>
    , Jan <span class="kwrd">int</span>
    , Feb <span class="kwrd">int</span>
    , Mar <span class="kwrd">int</span>
    , Apr <span class="kwrd">int</span>
    , May <span class="kwrd">int</span>
    , Jun <span class="kwrd">int</span>
    , Jul <span class="kwrd">int</span>
    , Aug <span class="kwrd">int</span>
    , Sep <span class="kwrd">int</span>
    , Oct <span class="kwrd">int</span>
    , Nov <span class="kwrd">int</span>
    , <span class="kwrd">Dec</span> <span class="kwrd">int</span>
    <span class="kwrd">CONSTRAINT</span> pkc_MonthlyData <span class="kwrd">PRIMARY</span> <span class="kwrd">KEY</span> <span class="kwrd">CLUSTERED</span> (VendorId)
    );</pre>
</blockquote>

<p>Let’s populate some sample data: </p>

<blockquote>
  <pre class="csharpcode"><span class="kwrd">SET</span> NOCOUNT <span class="kwrd">ON</span>
<span class="kwrd">DECLARE</span> @i <span class="kwrd">INT</span> = 0
<span class="kwrd">WHILE</span> @i &lt; 10000
<span class="kwrd">BEGIN</span>
    INSERT <span class="kwrd">INTO</span> dbo.MonthlyData <span class="kwrd">VALUES</span> (@i, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1)
    <span class="kwrd">SELECT</span> @i = @i + 1    
    
<span class="kwrd">END</span>
<span class="kwrd">GO</span></pre>
</blockquote>


<p>The first step is to break out the data by month and vendor. We can use the UNPIVOT operator to flip the month’s columns to rows.</p>

<blockquote>
  <pre class="csharpcode"><span class="kwrd">SELECT</span> *
  <span class="kwrd">FROM</span> (
<span class="kwrd">SELECT</span> VendorId
            , _01_Jan = Jan
            , _02_Feb = Feb
            , _03_Mar = Mar
            , _04_Apr = Apr
            , _05_May = May
            , _06_Jun = Jun
            , _07_Jul = Jul
            , _08_Aug = Aug
            , _09_Sep = Sep
            , _10_Oct = Oct
            , _11_Nov = Nov
            , _12_Dec = <span class="kwrd">Dec</span>
    <span class="kwrd">FROM</span> dbo.MonthlyData
    ) <span class="kwrd">AS</span> p
<font color="#0000ff">UNPIVOT</font>
    (Amount <span class="kwrd">FOR</span> MonthAbbr <span class="kwrd">IN</span> 
        (_01_Jan, _02_Feb, _03_Mar, _04_Apr, _05_May, _06_Jun
         , _07_Jul, _08_Aug, _09_Sep, _10_Oct, _11_Nov, _12_Dec
         ) 
    ) a
        </pre>
</blockquote>


<p>We have prefixed each month name with a sortable number so that we can order the results when we calculate our totals. The result of the above looks like: </p>

<table border="1" cellspacing="0" cellpadding="2" width="400"><tbody>
    <tr>
      <td valign="top" width="133">VendorId</td>

      <td valign="top" width="133">Amount</td>

      <td valign="top" width="133">MonthAbbr</td>
    </tr>

    <tr>
      <td valign="top" width="133">1</td>

      <td valign="top" width="133">1</td>

      <td valign="top" width="133">_01_Jan</td>
    </tr>

    <tr>
      <td valign="top" width="133">2</td>

      <td valign="top" width="133">1</td>

      <td valign="top" width="133">_02_Feb</td>
    </tr>

    <tr>
      <td valign="top" width="133">3</td>

      <td valign="top" width="133">1</td>

      <td valign="top" width="133">_03_Mar</td>
    </tr>

    <tr>
      <td valign="top" width="133">4</td>

      <td valign="top" width="133">1</td>

      <td valign="top" width="133">_04_Apr</td>
    </tr>

    <tr>
      <td valign="top" width="133">…</td>

      <td valign="top" width="133">…</td>

      <td valign="top" width="133">…</td>
    </tr>
  </tbody></table>

<p>We can now use this query as the input to a common table expression: </p>

<blockquote>
  <pre class="csharpcode"><span class="kwrd">WITH</span> unpivoted_cte <span class="kwrd">as</span> (
    <span class="kwrd">SELECT</span> VendorId
            , MonthAbbr
            , Amount 
            , MonthId = row_number() <span class="kwrd">over</span>(partition <span class="kwrd">by</span> VendorId <span class="kwrd">order</span> <span class="kwrd">by</span> MonthAbbr)
    <span class="kwrd">FROM</span> 
        (<span class="kwrd">SELECT</span> VendorId
                , _01_Jan = Jan
                , _02_Feb = Feb
                , _03_Mar = Mar
                , _04_Apr = Apr
                , _05_May = May
                , _06_Jun = Jun
                , _07_Jul = Jul
                , _08_Aug = Aug
                , _09_Sep = Sep
                , _10_Oct = Oct
                , _11_Nov = Nov
                , _12_Dec = <span class="kwrd">Dec</span>
        <span class="kwrd">FROM</span> dbo.MonthlyData) <span class="kwrd">AS</span> p
    UNPIVOT
        (Amount <span class="kwrd">FOR</span> MonthAbbr <span class="kwrd">IN</span> 
            (_01_Jan, _02_Feb, _03_Mar, _04_Apr, _05_May, _06_Jun
             , _07_Jul, _08_Aug, _09_Sep, _10_Oct, _11_Nov, _12_Dec
         )
    )<span class="kwrd">AS</span> unpvt
    )
<span class="kwrd">SELECT</span> base.MonthId
        , base.VendorId
        , base.Amount
  <span class="kwrd">FROM</span> unpivoted_cte base</pre>
</blockquote>


<p>The MonthAbbr is converted to an ID of 1-12. </p>

<p>Now we can calculate a year to date and a rolling three month calculation for a given vendor…</p>

<p>&#160;</p>

<blockquote>
  <pre class="csharpcode"><span class="kwrd">WITH</span> unpivoted_cte <span class="kwrd">as</span> (
    <span class="kwrd">SELECT</span> VendorId
            , MonthAbbr
            , Amount 
            , MonthId = row_number() <span class="kwrd">over</span>(partition <span class="kwrd">by</span> VendorId <span class="kwrd">order</span> <span class="kwrd">by</span> MonthAbbr)
    <span class="kwrd">FROM</span> 
        (<span class="kwrd">SELECT</span> VendorId
                , _01_Jan = Jan
                , _02_Feb = Feb
                , _03_Mar = Mar
                , _04_Apr = Apr
                , _05_May = May
                , _06_Jun = Jun
                , _07_Jul = Jul
                , _08_Aug = Aug
                , _09_Sep = Sep
                , _10_Oct = Oct
                , _11_Nov = Nov
                , _12_Dec = <span class="kwrd">Dec</span>
        <span class="kwrd">FROM</span> dbo.MonthlyData) <span class="kwrd">AS</span> p
    <font color="#0000ff">UNPIVOT</font>
        (Amount <span class="kwrd">FOR</span> MonthAbbr <span class="kwrd">IN</span> 
            (_01_Jan, _02_Feb, _03_Mar, _04_Apr, _05_May, _06_Jun
             , _07_Jul, _08_Aug, _09_Sep, _10_Oct, _11_Nov, _12_Dec
         )
    )<span class="kwrd">AS</span> unpvt
    )
<span class="kwrd">SELECT</span> base.MonthId
        , base.VendorId
        , base.Amount
        , YTD = ytd.Amount
        , Rolling3 = rolling3.Amount
  <span class="kwrd">FROM</span> unpivoted_cte base
  <font color="#0000ff">LEFT</font> <span class="kwrd">JOIN</span> (
        <span class="kwrd">SELECT</span> base.VendorId
                , base.MonthId 
                , Amount = <span class="kwrd">sum</span>(compare.amount)
          <span class="kwrd">FROM</span> unpivoted_cte base
          <span class="kwrd">JOIN</span> unpivoted_cte compare
            <span class="kwrd">ON</span> base.vendorId = compare.vendorId
           <span class="kwrd">AND</span> base.monthId &gt;=     compare.monthId
         <span class="kwrd">GROUP</span> <span class="kwrd">BY</span> base.VendorId
                    , base.MonthId
        ) ytd
    <span class="kwrd">ON</span> base.VendorId = ytd.VendorId
   <span class="kwrd">AND</span> base.MonthId = ytd.MonthId
  <font color="#0000ff">LEFT </font><span class="kwrd">JOIN</span> (
        <span class="kwrd">SELECT</span> base.VendorId
                , base.MonthId 
                , Amount = <span class="kwrd">sum</span>(compare.amount)
          <span class="kwrd">FROM</span> unpivoted_cte base
          <span class="kwrd">JOIN</span> unpivoted_cte compare
            <span class="kwrd">ON</span> base.vendorId = compare.vendorId
           <span class="kwrd">AND</span> compare.monthId <span class="kwrd">between</span> base.MonthId - 2 <span class="kwrd">and</span> base.MonthId
         <span class="kwrd">GROUP</span> <span class="kwrd">BY</span> base.VendorId
                    , base.MonthId
        ) rolling3
    <span class="kwrd">ON</span> base.VendorId = rolling3.VendorId
   <span class="kwrd">AND</span> base.MonthId = rolling3.MonthId
 <span class="kwrd">WHERE</span> base.VendorId = 999
 <span class="kwrd">ORDER</span> <span class="kwrd">BY</span> base.VendorId, base.MonthId

<span class="kwrd">GO</span></pre>
</blockquote>

<p>Here we add two left joins that each access the CTE. In the case of YTD we have: </p>

<blockquote>
  <pre class="csharpcode"> <span class="kwrd">LEFT</span> <span class="kwrd">JOIN</span> (
        <span class="kwrd">SELECT</span> base.VendorId
                , base.MonthId 
                , Amount = <span class="kwrd">sum</span>(compare.amount)
          <span class="kwrd">FROM</span> unpivoted_cte base
          <span class="kwrd">JOIN</span> unpivoted_cte compare
            <span class="kwrd">ON</span> base.vendorId = compare.vendorId
           <span class="kwrd">AND</span> base.monthId &gt;=     compare.monthId
         <span class="kwrd">GROUP</span> <span class="kwrd">BY</span> base.VendorId
                    , base.MonthId
        ) ytd
    <span class="kwrd">ON</span> base.VendorId = ytd.VendorId
   <span class="kwrd">AND</span> base.MonthId = ytd.MonthId</pre>
</blockquote>


<p>We use the CTE as a base and join the CTE to itself where the “compare” month is less than the base month. So, for MondId 4 in the “base” we will return months 1, 2, 3 and 4. We then sum the amounts grouping by vendor and base month. </p>

<p>The result of this query is: </p>

<table border="1" cellspacing="0" cellpadding="2" width="400"><tbody>
    <tr>
      <td valign="top" width="80">MonthId</td>

      <td valign="top" width="80">VendorId</td>

      <td valign="top" width="80">Amount</td>

      <td valign="top" width="80">YTD</td>

      <td valign="top" width="80">Rolling3</td>
    </tr>

    <tr>
      <td valign="top" width="80">1</td>

      <td valign="top" width="80">999</td>

      <td valign="top" width="80">1</td>

      <td valign="top" width="80">1</td>

      <td valign="top" width="80">1</td>
    </tr>

    <tr>
      <td valign="top" width="80">2</td>

      <td valign="top" width="80">999</td>

      <td valign="top" width="80">1</td>

      <td valign="top" width="80">2</td>

      <td valign="top" width="80">2</td>
    </tr>

    <tr>
      <td valign="top" width="80">3</td>

      <td valign="top" width="80">999</td>

      <td valign="top" width="80">1</td>

      <td valign="top" width="80">3</td>

      <td valign="top" width="80">3</td>
    </tr>

    <tr>
      <td valign="top" width="80">4</td>

      <td valign="top" width="80">999</td>

      <td valign="top" width="80">1</td>

      <td valign="top" width="80">4</td>

      <td valign="top" width="80">3</td>
    </tr>

    <tr>
      <td valign="top" width="80">…</td>

      <td valign="top" width="80">…</td>

      <td valign="top" width="80">…</td>

      <td valign="top" width="80">…</td>

      <td valign="top" width="80">…</td>
    </tr>
  </tbody></table>

<p>There are a couple of open issues with this approach: </p>

<ul>
  <li>We are assuming that there is only one per vendor. If we have more than one year we would need to modify the unpivot and use the year in the rolling 3 month and the YTD calculations. 
    <br /></li>

  <li>This is great for a one time re-summarization; however, there are other approaches including using SSIS to unpivot if you are loading data from a flat file and breaking the query into multiple steps and populating a work table that holds the unpivoted data. </li>
</ul>

<p>In my opinion, if you find yourself performing these types of queries in the context of an application or reporting system and not just for one time data analysis then you should look closely at your data structures and update your schema to give you access to the data in a more usable form. That said, this may be a good stop gap as you are re-architecting an existing solution or you have encountered a solution where you cannot change these data structures. </p>]]></content:encoded>
    </item>
    <item>
      <title>Finding holes in number sequences using CTEs and Ranking functions</title>
      <link>https://docs.microsoft.com/archive/blogs/andyroberts/finding-holes-in-number-sequences-using-ctes-and-ranking-functions</link>
      <pubDate>Wed, 13 Jul 2011 10:05:00 GMT</pubDate>
      <dc:creator><![CDATA[_Andy Roberts_]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/andyroberts/2011/07/13/finding-holes-in-number-sequences-using-ctes-and-ranking-functions/</guid>
      <description><![CDATA[I have had a number of customers looking to manage number sequences for various scenarios. An...]]></description>
      <content:encoded><![CDATA[<p>I have had a number of customers looking to manage number sequences for various scenarios. An example might be purchase order numbers where multiple systems need to &ldquo;reserve&rdquo; batches of PO Numbers. For example, an application may need to run a print job to print 10,000 paper purchase orders and an online application may need to reserve one PO at a time as they are entered into the system. Both systems may feed the same legacy system that puts an upper limit on the PO Number. We can assume that there is a process that &ldquo;frees&rdquo; POs when they have been fulfilled or archived.</p>
<p>Each time a print job needs to run they may need to find a &ldquo;hole&rdquo; in the list of reserved POs. I have seen a number of methods to accomplish this mostly using temp tables, table variables or cursors. In SQL Server Code Name "Denali&rdquo; there is a solution using a the new <a href="http://msdn.microsoft.com/en-us/library/ff878058(v=SQL.110).aspx">Sequence</a> construct that will help out here.</p>
<p>I thought this might be a good topic to show a use of SQL Server ranking functions and common table expressions. <a href="http://msdn.microsoft.com/en-us/library/ms189798(v=SQL.90).aspx">Ranking functions</a> were introduced in SQL Server 2005 and include a function <a href="http://msdn.microsoft.com/en-us/library/ms186734(v=SQL.105).aspx">ROW_NUMBER</a> that can be used to assign a row number to the result of a query. <a href="http://msdn.microsoft.com/en-us/library/ms190766(v=SQL.90).aspx">Common Table Expressions</a> were also introduced in SQL Server 2005 and may be thought of as a temporary result set that is scoped to a single query.</p>
<p>If we simplify this problem, we have a table called ListOfNumber with some number that we will call a TrackingNumber</p>
<blockquote>
<pre class="csharpcode"><span class="rem">-- </span>
<span class="rem">-- Create a table with a list of numbers. </span>
<span class="rem">-- </span>
<span class="kwrd">CREATE</span> <span class="kwrd">TABLE</span> ListOfNumber (TrackingNumber <span class="kwrd">int</span> <span class="kwrd">PRIMARY</span> <span class="kwrd">KEY</span> <span class="kwrd">CLUSTERED</span>) 

<span class="rem">-- </span>
<span class="rem">-- Insert numbers into the list &hellip; add some holes </span>
<span class="rem">-- </span>
<span class="kwrd">SET</span> NOCOUNT <span class="kwrd">ON</span> 
<span class="kwrd">DECLARE</span> @i <span class="kwrd">int</span> = 1 
<span class="kwrd">WHILE</span> @i &lt; 1000 
<span class="kwrd">BEGIN</span> 
    <span class="kwrd">IF</span> (rand()  &lt; 0.25) 
    <span class="kwrd">BEGIn</span> 
        INSERT <span class="kwrd">INTO</span> ListOfNumber <span class="kwrd">VALUES</span> (@i) 
    <span class="kwrd">END</span> 
    <span class="kwrd">SELECT</span> @i = @i + 1 
<span class="kwrd">END</span> 

 </pre>
</blockquote>
<p>So, at this point we have a sparsely populated table with values between 1 and 1000.</p>
<p>Maybe something that looks like:</p>
<table style="width: 156px;" border="1" cellspacing="0" cellpadding="2">
<tbody>
<tr>
<td valign="top" width="154">TrackingNumber</td>
</tr>
<tr>
<td valign="top" width="154">4</td>
</tr>
<tr>
<td valign="top" width="154">5</td>
</tr>
<tr>
<td valign="top" width="154">25</td>
</tr>
<tr>
<td valign="top" width="154">50</td>
</tr>
<tr>
<td valign="top" width="154">&hellip;</td>
</tr>
</tbody>
</table>
<p>If we can join the table to itself, and offset the rows by one we would have:</p>
<table style="width: 309px;" border="1" cellspacing="0" cellpadding="2">
<tbody>
<tr>
<td valign="top" width="153">TrackingNumber</td>
<td valign="top" width="154">TrackingNumber</td>
</tr>
<tr>
<td valign="top" width="153">&nbsp;</td>
<td valign="top" width="154">4</td>
</tr>
<tr>
<td valign="top" width="153">4</td>
<td valign="top" width="154">5</td>
</tr>
<tr>
<td valign="top" width="153">5</td>
<td valign="top" width="154">25</td>
</tr>
<tr>
<td valign="top" width="153">25</td>
<td valign="top" width="154">50</td>
</tr>
<tr>
<td valign="top" width="153">50</td>
<td valign="top" width="154">&hellip;</td>
</tr>
<tr>
<td valign="top" width="153">&hellip;</td>
<td valign="top" width="154">&nbsp;</td>
</tr>
</tbody>
</table>
<p>And we see that we have holes from 0-4, 5-25. The numbers 4-5 are adjacent so we do not want to report this as a hole.</p>
<p>The following query gets us part way there. Here we get an ordered list of numbers and assign a row number to each row returned. We join the list to itself offsetting the rownum by one to shift the join by one row.</p>
<blockquote>
<pre class="csharpcode">;<span class="kwrd">WITH</span> List_CTE <span class="kwrd">AS</span> (
    <span class="kwrd">SELECT</span> *, rownum = row_number() <span class="kwrd">OVER</span>(<span class="kwrd">ORDER</span> <span class="kwrd">BY</span> TrackingNumber)
      <span class="kwrd">FROM</span> ListOfNumber
    )
<span class="kwrd">SELECT</span> s.RowNum, s.TrackingNumber, e.RowNum, e.TrackingNumber
  <span class="kwrd">FROM</span> List_Cte s
  <span class="kwrd">JOIN</span> List_cte e
    <span class="kwrd">ON</span> s.rownum = e.rownum - 1</pre>
</blockquote>
<table style="width: 400px;" border="1" cellspacing="0" cellpadding="2">
<tbody>
<tr>
<td valign="top" width="100">
<p style="margin-right: 0px;" dir="ltr" align="left">RowNum</p>
</td>
<td valign="top" width="100">TrackingNumber</td>
<td valign="top" width="100">RowNum</td>
<td valign="top" width="100">TrackingNumber</td>
</tr>
<tr>
<td valign="top" width="100">1</td>
<td valign="top" width="100">4</td>
<td valign="top" width="100">2</td>
<td valign="top" width="100">5</td>
</tr>
<tr>
<td valign="top" width="100">2</td>
<td valign="top" width="100">5</td>
<td valign="top" width="100">3</td>
<td valign="top" width="100">25</td>
</tr>
<tr>
<td valign="top" width="100">3</td>
<td valign="top" width="100">25</td>
<td valign="top" width="100">4</td>
<td valign="top" width="100">50</td>
</tr>
</tbody>
</table>
<p>The problem here is that we are missing two holes: 1-4 and the largest number in the table (let&rsquo;s say it is 50) to 1000. We can modify our CTE to add a start and end that are one number outside the bounds of our sequence. We will also filter our results to remove the adjacent numbers (such as 4-5).</p>
<blockquote>
<pre class="csharpcode">;<span class="kwrd">WITH</span> List_CTE <span class="kwrd">AS</span> (    
    <span class="kwrd">SELECT</span> *, rownum = row_number() <span class="kwrd">OVER</span>(<span class="kwrd">ORDER</span> <span class="kwrd">BY</span> TrackingNumber)
      <span class="kwrd">FROM</span> (
      <span class="kwrd">SELECT</span> 0 <span class="kwrd">AS</span> TrackingNumber
      <span class="kwrd">UNION</span> 
      <span class="kwrd">SELECT</span> TrackingNumber <span class="kwrd">FROM</span> ListOfNumber
      <span class="kwrd">UNION</span> 
      <span class="kwrd">SELECT</span> 1001 <span class="kwrd">AS</span> TrackingNumber
      ) ListOfNumber
    )
<span class="kwrd">SELECT</span> s.RowNum, s.TrackingNumber, e.RowNum, e.TrackingNumber
  <span class="kwrd">FROM</span> List_Cte s
  <span class="kwrd">JOIN</span> List_cte e
    <span class="kwrd">ON</span> s.rownum = e.rownum - 1
 <span class="kwrd">WHERE</span> s.TrackingNumber &lt;&gt; e.TrackingNumber - 1</pre>
</blockquote>
<p>&nbsp;</p>
<table style="width: 402px;" border="1" cellspacing="0" cellpadding="2">
<tbody>
<tr>
<td valign="top" width="100">RowNum</td>
<td valign="top" width="100">TrackingNumber</td>
<td valign="top" width="100">RowNum</td>
<td valign="top" width="100">TrackingNumber</td>
</tr>
<tr>
<td valign="top" width="100">1</td>
<td valign="top" width="100">0</td>
<td valign="top" width="100">2</td>
<td valign="top" width="100">4</td>
</tr>
<tr>
<td valign="top" width="100">3</td>
<td valign="top" width="100">5</td>
<td valign="top" width="100">4</td>
<td valign="top" width="100">25</td>
</tr>
<tr>
<td valign="top" width="100">4</td>
<td valign="top" width="100">25</td>
<td valign="top" width="100">5</td>
<td valign="top" width="100">50</td>
</tr>
<tr>
<td valign="top" width="100">5</td>
<td valign="top" width="100">50</td>
<td valign="top" width="100">6</td>
<td valign="top" width="100">1001</td>
</tr>
</tbody>
</table>
<p>We now have accounted for the gaps at the start and end of our sequence and removed the non-holes caused by adjacent numbers. We can put it all together with:</p>
<blockquote>
<pre class="csharpcode">;<span class="kwrd">WITH</span> List_CTE <span class="kwrd">AS</span> (    
    <span class="kwrd">SELECT</span> *, rownum = row_number() <span class="kwrd">OVER</span>(<span class="kwrd">ORDER</span> <span class="kwrd">BY</span> TrackingNumber)
      <span class="kwrd">FROM</span> (
      <span class="kwrd">SELECT</span> 0 <span class="kwrd">AS</span> TrackingNumber
      <span class="kwrd">UNION</span> 
      <span class="kwrd">SELECT</span> TrackingNumber <span class="kwrd">FROM</span> ListOfNumber
      <span class="kwrd">UNION</span> 
      <span class="kwrd">SELECT</span> 1001 <span class="kwrd">AS</span> TrackingNumber
      ) ListOfNumber
    )
<span class="kwrd">SELECT</span> StartOfHole = s.TrackingNumber
        , EndOfHole = e.TrackingNumber
        , SizeOfHole = (e.TrackingNumber - s.TrackingNumber) - 1
  <span class="kwrd">FROM</span> List_Cte s
  <span class="kwrd">JOIN</span> List_cte e
    <span class="kwrd">ON</span> s.rownum = e.rownum - 1
 <span class="kwrd">WHERE</span> s.TrackingNumber &lt;&gt; e.TrackingNumber - 1</pre>
</blockquote>
<p>&nbsp;</p>
<table style="width: 370px;" border="1" cellspacing="0" cellpadding="2">
<tbody>
<tr>
<td valign="top" width="133">StartOfHole</td>
<td valign="top" width="121">EndOfHole</td>
<td valign="top" width="114">SizeOfHole</td>
</tr>
<tr>
<td valign="top" width="144">0</td>
<td valign="top" width="128">4</td>
<td valign="top" width="119">3</td>
</tr>
<tr>
<td valign="top" width="146">5</td>
<td valign="top" width="130">25</td>
<td valign="top" width="122">19</td>
</tr>
<tr>
<td valign="top" width="146">25</td>
<td valign="top" width="131">50</td>
<td valign="top" width="123">24</td>
</tr>
<tr>
<td valign="top" width="145">50</td>
<td valign="top" width="131">1001</td>
<td valign="top" width="124">950</td>
</tr>
</tbody>
</table>
<p>What if the values start and end of the valid range are consumed? In this case, because we pegged our query at 1 less than the min and 1 greater than the max of the range we would have adjacencies at the start and end of the range that would drop out. The result would simply be:</p>
<table style="width: 370px;" border="1" cellspacing="0" cellpadding="2">
<tbody>
<tr>
<td valign="top" width="133">StartOfHole</td>
<td valign="top" width="121">EndOfHole</td>
<td valign="top" width="114">SizeOfHole</td>
</tr>
<tr>
<td valign="top" width="144">1</td>
<td valign="top" width="128">4</td>
<td valign="top" width="119">2</td>
</tr>
<tr>
<td valign="top" width="146">5</td>
<td valign="top" width="130">25</td>
<td valign="top" width="122">19</td>
</tr>
<tr>
<td valign="top" width="146">25</td>
<td valign="top" width="131">50</td>
<td valign="top" width="123">24</td>
</tr>
<tr>
<td valign="top" width="145">50</td>
<td valign="top" width="131">1000</td>
<td valign="top" width="124">949</td>
</tr>
</tbody>
</table>
<p>You can make adjustments if you would only like to see only gaps that are larger than a certain size, your query for the &ldquo;ListOfNumber&rdquo; looks differently than what is shown, or you need to parameterize these values. There may be alternatives to showing the gaps that appear at the start and end of the sequence but I have found that this approach works for many of the cases where I have needed it.</p>
<p>I hope that this helps both give a good solution for finding gaps in number sequences and demonstrates a use of ranking functions and common table expressions.</p>]]></content:encoded>
    </item>
  </channel>
</rss>