Introduction

sp_CascadingDataViewer has one basic premise: it tells you how many rows in your database are dependant on a row (or set of rows) in a table. Or to put it another way, how many rows would you have to delete from your database in order to be able to delete a given row (or set of rows). All you need to do is specify.
  • A database
    • A schema in that database
      • A table in that schema
        • A set of columns in that table
        • A set of values, one for each specified column respectively
and sp_CascadingDataViewer will tell you all the rows dependant on the specified rows and optionally show you all of those dependant rows as well.

To understand the intracasies of sp_CascadingDataViewer, exactly what it does, how it does it & also what it does not do head to Behaviour, capabilities and limitations.

Here is a video of sp_CascadingDataViewer in action:

A simple example

Take the following database diagram taken from the AdventureWorksDW2008 sample database. It shows [AdventureWorksDW2008].[dbo].[DimProductSubCategory] and all the tables that are dependant on that table as determined by declared referential integrity (i.e. foreign key constraints) :
AdventureWorksDW2008.dbo.DimProductSubCategory_DependantTables.PNG
Here is some sample code to tell us how many rows are dependant on the Sub-Category called "Mountain Bikes":

--The most basic use of *sp_CascadingDataViewer*, return the number of rows dependant on a single row in a given table!
DECLARE	@vNumberOfRows	INT;
EXEC	[master].[dbo].[sp_CascadingDataViewer]
			@pDatabaseName	=	'AdventureWorksDW2008'
		,	@pSchemaName	=	'dbo'
		,	@pTableName	=	'DimProductSubCategory'
		,	@pFilterCols	=	'EnglishProductSubCategoryName'
		,	@pFilterValues	=	'Mountain Bikes'	/*    <-In other words, how many rows in our database are dependant on the row 
								         WHERE [dbo].[DimProductSubCategory].[EnglishProductSubCategoryName]='Mountain Bikes'*/
		,	@pNumberOfRows	=	@vNumberOfRows	OUTPUT;
SELECT	@vNumberOfRows AS [NumberOfRows];
and this screenshot shows us the answer:
AdventureWorksDW2008.dbo.DimProductSubCategory_execoutput.png
Note that sp_CascadingDataViewer doesn't just limit you to specifying a single column. To learn how to specify multiple columns head to Specify multiple columns

Show me the data (@pShowData=1)

sp_CascadingDataViewer offers the user the chance to see all of the dependant rows. It has a parameter called @pShowData which muct be set to '1' in order to show all that data. If we take the example from above and add that new parameter we get the following SQL statement:

DECLARE	@vNumberOfRows	INT;
EXEC	[master].[dbo].[sp_CascadingDataViewer]
			@pDatabaseName	=	'AdventureWorksDW2008'
		,	@pSchemaName	=	'dbo'
		,	@pTableName	=	'DimProductSubCategory'
		,	@pFilterCols	=	'EnglishProductSubCategoryName'
		,	@pFilterValues	=	'Mountain Bikes'
		,	@pNumberOfRows	=	@vNumberOfRows	OUTPUT
		,	@pShowData	=	1;	--Show me the data!!!
SELECT	@vNumberOfRows AS [NumberOfRows];

The screenshot below shows the results following execution of that code. Notice that we have a resultset from each of the tables shown in the database diagram near the top of this page. Also notice that, for clarity, the resultset contains the name of the table:

AdventureWorksDW2008.dbo.DimProductSubCategory_ShowData.PNG
Also, just to prove that the returned NumberOfRows is correct, the total number of rows returned in all the resultsets to SSMS is 17358 (one more than NumberOfRows=17357 because that final returned resultset that contains NumberOfRows is counted also).

What if I don't want to see everything? (@pDependantLevels)

Often you may not want to know about all rows in the database that are dependant on the specified rows in @pFilterCols/@pFilterValues and with this in mind sp_CascadingDataViewer provides a parameter @pDependantLevels that limits the algorithm to only considering tables that are a given number of levels away from the specified table.
This may be bettter explained by looking at an example. Consider the diagram that I showed in the first example at the top of this page:

AdventureWorksDW2008.dbo.DimProductSubCategory_DependantTables.PNG
If we did not want to consider rows in [FactInternetSalesReason] then we can set @pDependantLevels=2. This means that [DimProduct] will be considered (because it is one level away from the starting table [DimProductSubCategory]) as will [FactInternetSales] & [FactResellerSales] (because they are two levels away) but [FactInternetSalesReason] will not (because it is three levels away).

Here's the code:
DECLARE	@vNumberOfRows	INT;
EXEC	[master].[dbo].[sp_CascadingDataViewer]
			@pDatabaseName		=	'AdventureWorksDW2008'
		,	@pSchemaName		=	'dbo'
		,	@pTableName		=	'DimProductSubCategory'
		,	@pFilterCols		=	'EnglishProductSubCategoryName'
		,	@pFilterValues		=	'Mountain Bikes'
		,	@pNumberOfRows		=	@vNumberOfRows	OUTPUT
		,	@pShowData		=	1
		,	@pDependantLevels	=	2;      --Note the new parameter @pDependantLevels
SELECT	@vNumberOfRows AS [NumberOfRows];  

and the result:
AdventureWorksDW2008.dbo.DimProductSubCategory_DependantLevels.png
As we would expect, reducing the number of tables we consider also reduces the actual result (i.e. the number of dependant rows).

Show distribution of all values in the specified column(s)

Knowing how many rows are dependant on a single filter condition is useful but it becomes much more useful still when compared to all other values in the specified columns. For example, I know from the basic example above that there are 17537 rows dependant on 'Mountain Bikes' in [AdventureworksDW2008].[dbo].[DimProductSubCategory].[EnglishProductSubCategoryName] but I don't know whether that is high or low compared to all other values in that field.
This realisation gave rise to another stored procedure sp_CascadingDataViewerTableWrapper. This second stored procedure calls sp_CascadingDataViewer on every distinct value defined by @vFilterCols.

Observe. We tell sp_CascadingDataViewerTableWrapper which column to work on. It calls sp_CascadingDataViewer on every distinct value for the columns listed in @pFilterCols and returns the results in an ordered resultset.

Here's the code:
exec	[dbo].[sp_CascadingDataViewerTableWrapper]
		@pDatabaseName	= 'AdventureWorksDW2008'
,		@pTableName	= 'DimProductSubCategory'
,		@pSchemaName	= 'dbo'
,		@pFilterCols	= 'EnglishProductSubCategoryName';

We're calling sp_CascadingDataViewerTableWrapper on the same table &column that we used in previous examples, the difference in this case is that we don't need to specify @pFilterValues because sp_CascadingDataViewerTableWrapper will call sp_CascadingDataViewer for every possible value. Here are the results:

AdventureWorksDW2008.dbo.DimProductSubCategory_TableWrapper.png
This is much much more useful than calling sp_CascadingDataViewer in isolation. We can now discover exactly which of our SubCategories in [DimProductSubCategory] has the most dependant rows and that is very useful information to know. We have at-a-glance information about which are our most popular categories and, of even more value, the difference in popularity too! This is BI over our raw data!

Note that sp_CascadingDataViewerTableWrapper supports @pDependantLevels as you would expect and also has an additional parameter, @pAscending, that defines whether or not the results are returned in ascending or descending order.

Putting it all together

Executing SQL statements and such like is all very useful but this information really starts to become value when it gets visualised and for that I provide a SQL Server Reporting Services report (a .rdl file). The report provides parameters allowing the user to specify:
  • Database
  • schema.table
  • column(s)
The report passes those values to sp_CascadingDataViewerTableWrapper and visualises the returned resultset in a bar graph. The screenshot below shows the chart displaying the same values that we saw in our earlier example call to sp_CascadingDataViewerTableWrapper (see above):
AdventureWorksDW2008.dbo.DimProductSubCategory_Chart.png

Last edited Aug 13, 2010 at 7:24 AM by jamiet, version 76