PeterAvila – Interface Technical Training https://www.interfacett.com Wed, 21 Jun 2017 19:26:18 +0000 en-US hourly 1 How to Display the Elapsed Processing Time of a Report in SSRS https://www.interfacett.com/blogs/how-to-display-the-elapsed-processing-time-of-a-report-in-ssrs/ https://www.interfacett.com/blogs/how-to-display-the-elapsed-processing-time-of-a-report-in-ssrs/#respond Mon, 28 Sep 2015 18:15:01 +0000 http://www.interfacett.com/blogs/?p=?p=21581 The amount of time it takes to process a report can be useful information for performance auditing. Although there isn’t a built-in field in SQL Server Reporting Services that holds the elapsed processing time, it’s easy to create this functionality using the ExecutionTime built-in field and the Now VB.Net function in SSRS. For instructor-led SQL … Continue reading How to Display the Elapsed Processing Time of a Report in SSRS

The post How to Display the Elapsed Processing Time of a Report in SSRS appeared first on Interface Technical Training.

]]>
The amount of time it takes to process a report can be useful information for performance auditing. Although there isn’t a built-in field in SQL Server Reporting Services that holds the elapsed processing time, it’s easy to create this functionality using the ExecutionTime built-in field and the Now VB.Net function in SSRS.

For instructor-led SQL Server Training see our complete course schedule.  

The ExecutionTime is the moment when report processing starts. When used in the expression of a textbox, the Now function returns the time when the report is rendered. Rendering comes right after report processing. This means we can easily calculate the processing time simply by subtracting Now from ExecutionTime.

Function Implementation

One way to implement the logic is to put it into a custom function and then call the function from the expression of a textbox. This is clean, the code is easy to read and so it’s also easy to maintain. The downside is that you have more than one component to the solution; so, if you want to copy this functionality into other reports, you’ll have to copy and paste both the function and the textbox. My preference would be for this approach because “clean and easy to maintain” usually leads to smoother running programs!

If you’ve never created custom code in SSRS before, read my article on Using Custom Code in SSRS to see how it’s done. Then create the following function that will both calculate the elapsed processing time and return it as a string in the format mm:ss.

Function ProcessingTime(StartTime As Date, EndTime as Date) As String

Dim ElapsedMins As Integer = DateDiff(DateInterval.Minute, StartTime, EndTime) mod 60 
Dim ElapsedSecs As Integer = DateDiff(DateInterval.Second, StartTime, EndTime) mod 60

Return ElapsedMins.ToString() & ":" & Right("0" & ElapsedSecs.ToString(), 2)

End Function

Single Expression Implementation

Another way to implement the logic is to use a single expression in a textbox (right-click the textbox and select Expression…) that does everything. This is a bit messier, the expression has a whole bunch of things piled into it making it harder to read and maintain, but all you have to do to put it into another report is copy just that one textbox containing the following expression, which also displays the results in the format mm:ss.

=DateDiff(DateInterval.Minute, Globals!ExecutionTime, Now()) mod 60 & ":" & Right("0" & 
DateDiff(DateInterval.Second, Globals!ExecutionTime,  Now()) mod 60, 2)

 

Enjoy!

Peter Avila
SQL Server Instructor – Interface Technical Training
Phoenix, AZ

The post How to Display the Elapsed Processing Time of a Report in SSRS appeared first on Interface Technical Training.

]]>
https://www.interfacett.com/blogs/how-to-display-the-elapsed-processing-time-of-a-report-in-ssrs/feed/ 0
Optional Parameters in SSRS https://www.interfacett.com/blogs/optional-parameters-in-ssrs/ https://www.interfacett.com/blogs/optional-parameters-in-ssrs/#comments Mon, 27 Jul 2015 16:13:22 +0000 http://www.interfacett.com/blogs/?p=?p=21139 Let’s say you have a report, like a sales report that shows sales orders, and some people who run that report need it to show only the one sales order that they specify while others need to see all sales orders. In this article, I’ll show you how to create an optional parameter that allows … Continue reading Optional Parameters in SSRS

The post Optional Parameters in SSRS appeared first on Interface Technical Training.

]]>
Let’s say you have a report, like a sales report that shows sales orders, and some people who run that report need it to show only the one sales order that they specify while others need to see all sales orders. In this article, I’ll show you how to create an optional parameter that allows you to implement this type of flexibility.

To accomplish this, two things need to be done. The first is to create an optional parameter, or a parameter that does not stop SSRS from running the report if no value for it is supplied. The second is to tell the report to select all the data when a value for the parameter is not supplied and to select only some of the data when a value is supplied.

Create an Optional Parameter

  1. With the report open and the Design tab selected, right-click the Parameters folder in the Report Data pane and select Add Parameter…

Add a Parameter in SSRS SQL Server Reporting Services

  1. In the Report Parameter Properties window, make the following entries and selections. Notice that Allow null value is checked.

Allow Null values feature in in SSRS SQL Server Reporting Services

When the parameter does not allow nulls, SSRS will not run the report if a value for the parameter is not supplied. By telling the report to allow nulls, SSRS will run the report regardless.

  1. Click OK when done.

The next step is to wire up the parameter to the data selection process of the report and to tell the report what to do if no value is supplied.

Configure the Report for the Optional Parameter

Our parameter will be used in the WHERE clause of the query in the dataset, though the same approach applies if you use it in a filter. (I’m putting the query directly in the dataset of the report to simplify this article, but you should always consider using stored procedures when developing reports; among the many benefits that stored procedures offer are that they are faster and keep data access organized and manageable. The same strategy shown in this article can be applied in a stored procedure.)

  1. Right-click the dataset in the Report Data pane and select Dataset Properties.

Selecting Dataset property in SSRS SQL Server Reporting Services

  1. Add a WHERE clause to the query that restricts the data in the report to the value of the parameter. Include an OR clause that returns True if the parameter is null; that way, the WHERE clause will be True for every row of the query when the parameter is not used.

Adding a WHERE Claues in in SSRS SQL Server Reporting Services

Now, when you run your report, you can either supply a value for the parameter or just ignore it and the report will know what to do in each case.

Enjoy!

Peter Avila
SQL Server Instructor – Interface Technical Training
Phoenix, AZ

The post Optional Parameters in SSRS appeared first on Interface Technical Training.

]]>
https://www.interfacett.com/blogs/optional-parameters-in-ssrs/feed/ 3
A Real-World Example of a Non-Correlated SELECT Subquery and Cross Join https://www.interfacett.com/blogs/real-world-example-non-correlated-select-subquery-cross-join/ https://www.interfacett.com/blogs/real-world-example-non-correlated-select-subquery-cross-join/#respond Wed, 08 Apr 2015 17:04:12 +0000 http://www.interfacett.com/blogs/?p=?p=20118 I recently developed a solution to a problem that is a good example of a non-correlated subquery in a SELECT clause that can also be written as a cross join. I’ll describe the requirement, the data, and then the solution with a non-correlated subquery and another with a cross join. To keep my client and … Continue reading A Real-World Example of a Non-Correlated SELECT Subquery and Cross Join

The post A Real-World Example of a Non-Correlated SELECT Subquery and Cross Join appeared first on Interface Technical Training.

]]>
I recently developed a solution to a problem that is a good example of a non-correlated subquery in a SELECT clause that can also be written as a cross join. I’ll describe the requirement, the data, and then the solution with a non-correlated subquery and another with a cross join. To keep my client and their data anonymous, I’ve translated the problem to the AdventureWorks database.

The Requirement

The client wanted to see, for each sales person who placed orders over a time period, their total sales and their “productivity” over that time period. They defined productivity as the total sales divided by the number of business days in that time period. Here is the output that meets the requirement and that our solution will have to produce. This is for the time period of March, 2004, given the data in the AdventureWorks database.

001-non-Correlated-SELECT-Subquery-and-Cross-Join

The Data

The AdventureWorks database contains a table called Sales.SalesOrderHeader that holds sales orders. There are three columns in that table that are of interest to us: SalesPersonID, OrderDate, and TotalDue. Here is a sample of the data in those columns, formatted for a clean presentation, and including only the data for the time period of March, 2004 that I will use to test the solution, though not all rows in that time period are shown:

USE AdventureWorks

SELECT SalesPersonID
, CONVERT(VARCHAR, OrderDate, 101) AS OrderDate
, '$' + CONVERT(VARCHAR, TotalDue, 1) AS TotalDue
FROM Sales.SalesOrderHeader

002-non-Correlated-SELECT-Subquery-and-Cross-Join

In addition, I have created a table called DimDate that is similar to the one my client has in their database. DimDate is a dimension table in a data warehouse. (As we see in the Data Warehouses course, it is useful to store dates in a data warehouse in a date dimension table in which each row represents a different date). The IsBusinessDay column is a bit data type in which a 1 means that the date is a business day and a zero means that it is not. To simplify things for this example, I have created dates only for the date range I will be using in this example—March, 2004—and flagged only weekend dates as non-business days. Here is the script I used to create the DimDate table and to show its contents:

-- Create the DimDate table

USE AdventureWorks

IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'dbo' AND  TABLE_NAME = 'DimDate')
	DROP TABLE dbo.DimDate

CREATE TABLE dbo.DimDate
(
  OrderDate DATE NOT NULL
, IsBusinessDay BIT NOT NULL
)

--Populate the DimDate table

DECLARE @StartDate DATE = '3/1/2004', @EndDate DATE = '3/31/2004'
DECLARE @CurrDate DATE = @StartDate

WHILE @CurrDate <= @EndDate
   BEGIN
      INSERT INTO dbo.DimDate
	     VALUES (@CurrDate, CASE WHEN DATEPART(WEEKDAY, @CurrDate) NOT IN (1,7) THEN 1 ELSE 0 END)
	  SET @CurrDate = DATEADD(DAY, 1, @CurrDate)
   END 

SELECT *
FROM dbo.DimDate

003-non-Correlated-SELECT-Subquery-and-Cross-Join

The Solution

Step 1: Create the Base Query that Aggregates by the Salespersonid

The output you saw earlier that meets the requirement shows one row for every sales person, along with a sum of the TotalDue column for each sales person and the number of business days. Even though the number of business days will be the same for each salesperson, it needs to be included on every row so that we can divide each salesperson’s sum of total due by the number of business days.

We’ll start with the following query that finds the sum of the total due for each employee for the test time period of March, 2004. Notice that we group by the SalesPersonID to get one row per sales person and then we sum the TotalDue for each sales person. Notice also that in this phase we are not yet doing any formatting of the output, because we need the TotalDue to remain a numeric value for when we divide it by the number of business days in a subsequent phase.

SELECT SalesPersonID
, SUM(TotalDue) AS TotalDue
FROM Sales.SalesOrderHeader
WHERE OrderDate BETWEEN '3/1/2004' AND '3/31/2004'
GROUP BY SalesPersonID

004-non-Correlated-SELECT-Subquery-and-Cross-Join

By the way, notice that there is a NULL SalesPersonID. A SalesPersonID is NULL when the order is taken  electronically over the company’s website. Let’s clean that up a bit. We’ll use the ISNULL() function to display “Internet Sale” when the SalesPersonID is NULL. There’s just one small problem with that approach, though. The ISNULL() function requires that both of its arguments be the same data type, and our two arguments are not; SalesPersonID is an integer and “Internet Sale” is character. If we do nothing and leave it up to SQL Server, it will attempt to perform an implicit conversion. In an implicit conversion, the lower data type (character, in this case) is converted up to the higher data type (integer, in this case), and that would fail because “Internet Sale” does not resemble an integer! So, we will need to perform an explicit conversion and cast the SalesPersonID down to a character. Here is the revised query:

SELECT ISNULL(CAST(SalesPersonID AS VARCHAR), 'Internet Sale') AS SalesPersonID
, SUM(TotalDue) AS TotalDue
FROM Sales.SalesOrderHeader
WHERE OrderDate BETWEEN '3/1/2004' AND '3/31/2004'
GROUP BY SalesPersonID

005-non-Correlated-SELECT-Subquery-and-Cross-Join

Step 2: Include the Number of Business Days

We can very easily find the number of business days for a time period by filtering the DimDate table to return only days where IsBusinessDay = 1 within the time period we want. Here is a query that does just that for our time period of March, 2004 (there were 23 business days in March, 2004):

SELECT COUNT(*) NumBusDays
FROM dbo.DimDate
WHERE OrderDate BETWEEN '3/1/2004' AND '3/31/2004'
  AND IsBusinessDay = 1

006-non-Correlated-SELECT-Subquery-and-Cross-Join

Notice that this query returns a single row and a single column. A query that returns a single value like this is referred to as a scalar query. Because the query above is scalar, it can be used as a column in a SELECT clause. Just one problem: The GROUP BY clause in our base table won’t allow us to do that, because the GROUP BY demands that the only two things in the SELECT clause be what is in the GROUP BY clause and aggregate functions. We can get around this in a couple of ways.

One way is by making our base query a derived table subquery and including the scalar query in the outer query.

In the query below, BaseQuery is the base query we developed earlier, and BusDays is the scalar query that returns the number of business days in our time period. NumBusDays is a subquery in the SELECT clause; it occupies a column position in the outer SELECT. The outer query displays all the columns in the base query (BaseQuery.*) and the row count returned by NumBusDays.

SELECT BaseQuery.*, (SELECT COUNT(*) As TotBusDays
                     FROM dbo.DimDate
                     WHERE OrderDate BETWEEN '3/1/2004' AND '3/31/2004'
                       AND IsBusinessDay = 1
                    ) AS NumBusDays
FROM (SELECT ISNULL(CAST(SalesPersonID AS VARCHAR), 'Internet Sales') AS SalesPersonID
      , SUM(TotalDue) AS TotalDue
      FROM Sales.SalesOrderHeader
      WHERE OrderDate BETWEEN '3/1/2004' AND '3/31/2004'
      GROUP BY SalesPersonID
     ) AS BaseQuery

007-non-Correlated-SELECT-Subquery-and-Cross-Join

The other way to accomplish the same results is by using a cross join. A cross join joins two tables by matching up every row in one table with every row in the other table. Since we want the single row returned by our scalar query to appear on every row of our aggregate query, a cross join would also work (any query that uses a non-correlated subquery in a Select clause can also be written as a cross join).

SELECT BaseQuery.*, TotBusDays.NumBusDays.
FROM (SELECT ISNULL(CAST(SalesPersonID AS VARCHAR), 'Internet Sales') AS SalesPersonID
      , SUM(TotalDue) AS TotalDue
      FROM Sales.SalesOrderHeader
      WHERE OrderDate BETWEEN '3/1/2004' AND '3/31/2004'
      GROUP BY SalesPersonID
     ) AS BaseQuery
     CROSS JOIN 
     (SELECT COUNT(*) As NumBusDays
      FROM dbo.DimDate
      WHERE OrderDate BETWEEN '3/1/2004' AND '3/31/2004'
        AND IsBusinessDay = 1
     ) AS BusDays

008-non-Correlated-SELECT-Subquery-and-Cross-Join

You can run both queries with the actual execution plan on to see if one is more efficient than the other. In this example, they are both the same—neither approach is faster or more efficient than the other, though you should not always count on this being the case; it’s always a good idea to compare both execution plans as there are many factors that can effect the outcome.

Step 3: Calculate the Productivity and Format the Output

At this point, we have everything we need on one row to allow us to divide the sum of the TotalDue column by the number of business days. All we have to do now is add another column to the outer query that performs that division; but in the subquery approach, if we do it that way, we’d have to repeat the scalar query as the denominator. To avoid that, we can make a derived table out of everything we have so far and then use the column aliases, instead. We can also format things nicely in the new outer query. Much easier!

Compare the results of this query with the results shown in the Requirements section above; they are the same.

SELECT SalesPersonID
, '$' + CONVERT(VARCHAR, TotalDue, 1) AS SumTotalDue
, NumBusDays
, '$' + CONVERT(VARCHAR, TotalDue/NumBusDays, 1) AS Productivity
FROM (SELECT BaseQuery.*, (SELECT COUNT(*) As TotBusDays
                           FROM dbo.DimDate
                           WHERE OrderDate BETWEEN '3/1/2004' AND '3/31/2004'
                             AND IsBusinessDay = 1
                          ) AS NumBusDays
      FROM (SELECT ISNULL(CAST(SalesPersonID AS VARCHAR), 'Internet Sales') AS SalesPersonID
            , SUM(TotalDue) AS TotalDue
            FROM Sales.SalesOrderHeader
            WHERE OrderDate BETWEEN '3/1/2004' AND '3/31/2004'
            GROUP BY SalesPersonID
           ) AS BaseQuery
     ) EverythingWeNeed
ORDER BY Productivity DESC

009-non-Correlated-SELECT-Subquery-and-Cross-Join

With the cross join, the new outer query is not even necessary:

SELECT BaseQuery.SalesPersonID
, '$' + CONVERT(VARCHAR, BaseQuery.TotalDue, 1) As SumTotalDue
, BusDays.NumBusDays
, '$' + CONVERT(VARCHAR, BaseQuery.TotalDue/BusDays.NumBusDays, 1) AS Productivity

FROM (SELECT ISNULL(CAST(SalesPersonID AS VARCHAR), 'Internet Sales') AS SalesPersonID
      , SUM(TotalDue) AS TotalDue
	  FROM Sales.SalesOrderHeader
	  WHERE OrderDate BETWEEN '3/1/2004' AND '3/31/2004'
	  GROUP BY SalesPersonID
	 ) AS BaseQuery
	 CROSS JOIN 
	 (SELECT COUNT(*) As NumBusDays
	  FROM dbo.DimDate
	  WHERE OrderDate BETWEEN '3/1/2004' AND '3/31/2004'
	 	AND IsBusinessDay = 1
	 ) AS BusDays
ORDER BY BaseQuery.TotalDue/BusDays.NumBusDays DESC

010-non-Correlated-SELECT-Subquery-and-Cross-Join

Enjoy!

Peter Avila
SQL Server Instructor – Interface Technical Training
Phoenix, AZ

The post A Real-World Example of a Non-Correlated SELECT Subquery and Cross Join appeared first on Interface Technical Training.

]]>
https://www.interfacett.com/blogs/real-world-example-non-correlated-select-subquery-cross-join/feed/ 0
Repeating and Freezing Column Headers in SSRS Tables https://www.interfacett.com/blogs/repeating-freezing-column-headers-ssrs-tables/ https://www.interfacett.com/blogs/repeating-freezing-column-headers-ssrs-tables/#comments Mon, 09 Mar 2015 22:08:25 +0000 http://www.interfacett.com/blogs/?p=?p=19909 In this article I’ll show you how to configure a table tablix to both repeat column headers at the top of every page and freeze them while scrolling. While tablixes have properties for these things, they only work in matrix tablixes but not in tables. I’ll show you a way to accomplish these things using … Continue reading Repeating and Freezing Column Headers in SSRS Tables

The post Repeating and Freezing Column Headers in SSRS Tables appeared first on Interface Technical Training.

]]>
In this article I’ll show you how to configure a table tablix to both repeat column headers at the top of every page and freeze them while scrolling. While tablixes have properties for these things, they only work in matrix tablixes but not in tables. I’ll show you a way to accomplish these things using an approach that does work; however, be warned: the way to do this is clunky, counter-intuitive, and requires maintenance if you make certain changes to your tables.

First, let me show you the properties that don’t work in a table. For the approach I’ll be showing you here to work properly, those properties have to be set to False.

Press F4 to display the Properties Window. Then, in the dropdown at the top of the Properties window, select the tablix in your report for which you want to control column headers.

001-Repeating-and-Freezing-Column-Headers-in-SSRS-Tables

There are two properties you’re looking for. FixedColumnHeaders will prevent column headers in a matrix from scrolling off the top of the page and RepeatColumnHeaders will make sure that column headers in a matrix appear at the top of every page in the tablix. When working with tables, you want to make sure these properties are both set to False.

002-FixedColumnHeaders-Column-Headers-in-SSRS-Tables

With both of these properties set to False, we’re ready to configure the tablix to repeat and freeze the column headers. Start by putting the group panel at the bottom of the report design screen into advanced mode. Do this by selecting Advanced Mode from the dropdown off to the right side of the groups panel.

003-FixedColumnHeaders-Column-Headers-in-SSRS-Tables

Don’t be alarmed by what you see next! This is a notoriously confusing layout that many people struggle to understand. The good news is that you don’t need to understand it in order to use it for what we’re doing as long as you keep in mind that you always work with the first static item in the list. Select the first Static item and press F4 to display its properties in the Properties window.

004-Properties-Repeating-and-Freezing-Column-Headers-in-SSRS-Tables

You’ll need to set two properties to make the column headers repeat on every page: KeepWithGroup must be set to After and RepeatOnNewPage must be set to True. To prevent scrolling, set FixedData to True.

(A note of caution: Keep in mind that this approach works as long as these properties are always set on the first static item. If you change the table in a way that changes what the first row was when you set these properties, what used to be the first static item will no longer be the first one. You will have to find where that static item ended up, undo the property changes on it, and reset those properties on the new first static item.)

Alas, we are not quite out of the woods, yet. After you set the FixedData property to True, preview your report and try scrolling. Notice that the column header stays put, but because its background is transparent by default, things look messy.

005-fixedData-Properties-Repeating-and-Freezing-Column-Headers-in-SSRS-Tables

To fix this, just change the background color of the cells in the header row to something other than “No Color.” Select the top row of the table, press F4 to show the Properties window, click the dropdown of the BackgroundColor property, and chose a color, such as White or any other color (if you choose a dark color for the background, it may be difficult to read the column headers, and you may have to change the Color property, which controls the foreground color, to something light).

006-BackgroundColor-Properties-Repeating-and-Freezing-Column-Headers-in-SSRS-Tables

Enjoy!

Peter Avila
SQL Server Instructor – Interface Technical Training
Phoenix, AZ

The post Repeating and Freezing Column Headers in SSRS Tables appeared first on Interface Technical Training.

]]>
https://www.interfacett.com/blogs/repeating-freezing-column-headers-ssrs-tables/feed/ 6
How to Name Worksheets When Exporting SSRS reports to Excel https://www.interfacett.com/blogs/name-worksheets-exporting-ssrs-reports-excel/ https://www.interfacett.com/blogs/name-worksheets-exporting-ssrs-reports-excel/#comments Wed, 18 Feb 2015 17:05:22 +0000 http://www.interfacett.com/blogs/?p=?p=19821 Let’s say we have the following report that shows total sales by product category by territory: When we export this report to Excel, we’d like each territory to appear in its own worksheet and each worksheet named after its territory: How do we make this work? Easy! 1) Put every group on its own page, … Continue reading How to Name Worksheets When Exporting SSRS reports to Excel

The post How to Name Worksheets When Exporting SSRS reports to Excel appeared first on Interface Technical Training.

]]>
Let’s say we have the following report that shows total sales by product category by territory:

001-SQL-server-SSRS-report-to-excel

When we export this report to Excel, we’d like each territory to appear in its own worksheet and each worksheet named after its territory:

002-SQL-server-SSRS-report-to-excel

How do we make this work? Easy! 1) Put every group on its own page, and 2) name each page using the same field the group uses.

Step 1: Put each group on its own page

To put each group on its own page, open the group’s property window.

003-SQL-server-SSRS-report-to-excel

Then, in the Page Breaks category, put a check mark in the Between each instance of a group check box.

004-SQL-server-SSRS-report-to-excel

Click OK to complete this step.

Step 2: Name the pages of the group

With the group selected in the Row Groups panel, press F4 to open the Properties window.

005-SQL-server-SSRS-report-to-excel

Next, expand the Group property and look for the Page Name sub-property. From its dropdown, select <Expression…>.

006-SQL-server-SSRS-report-to-excel

In the Expression dialog, select the Fields category and then double-click on the same field the group uses; in this case that would be the Territory field.

007-SQL-server-SSRS-report-to-excel

A reference to the field appears in the window at the top of the Expression dialog.

008-SQL-server-SSRS-report-to-excel

Click OK and that’s it! Now, when you export the report to Excel, the worksheet names will match the group names!

Enjoy!

Peter Avila
SQL Server Instructor – Interface Technical Training
Phoenix, AZ

The post How to Name Worksheets When Exporting SSRS reports to Excel appeared first on Interface Technical Training.

]]>
https://www.interfacett.com/blogs/name-worksheets-exporting-ssrs-reports-excel/feed/ 2
Using Custom Code in SSRS https://www.interfacett.com/blogs/using-custom-code-ssrs/ https://www.interfacett.com/blogs/using-custom-code-ssrs/#comments Mon, 15 Dec 2014 16:17:52 +0000 http://www.interfacett.com/blogs/?p=?p=19512 While expressions allow you to use limited VB.Net to create advanced dynamic functionality in your SQL Server Reporting Services (SSRS) reports, custom code allows you to leverage much more of the power of VB.Net. The focus of this article is not on writing VB.Net code, but on how to create and use code in SSRS. … Continue reading Using Custom Code in SSRS

The post Using Custom Code in SSRS appeared first on Interface Technical Training.

]]>
While expressions allow you to use limited VB.Net to create advanced dynamic functionality in your SQL Server Reporting Services (SSRS) reports, custom code allows you to leverage much more of the power of VB.Net.

The focus of this article is not on writing VB.Net code, but on how to create and use code in SSRS. As such, this article uses an example with very simple VB.Net code that I assume you understand. Of course, the more VB.Net you know, the more you will be able to do with what you learn here.

Our example uses the territory sales report shown below to conditionally format the total sales so that it’s red if it’s below 5 million; blue if it’s below 10 million; green if it’s below 15 million; and black otherwise.

001-Using-Custom-Code-in-SSRS

Here’s what the report will look like when we’re done:

002-Using-Custom-Code-in-SSRS

One way to implement this is with an expression in the color property of the textbox that displays the total sales. The expression would use nested Immediate-If functions (IIF()) to decide what color to use based on the value of the total sales. Here it is:

003-Using-Custom-Code-in-SSRS

The 1-line limitation of expressions often forces us to nest functions. Nesting can make expressions difficult to write, understand, and maintain. Our expression nests IIF() functions to implement Else-If logic that is implemented much more clearly as a block structure in code (a block structure is one that occupies more than one line of code).

To write code in a report, select the Report menu and then Report Properties…

004-Using-Custom-Code-in-SSRS

Create code in the Code section. In the example shown below, we created a function called GetColor that receives as input the total sales in a parameter called Val2Color with a data type of Single. The function returns as output the color as a String value. You can create as many functions and sub-procedure methods as you need.

005-Using-Custom-Code-in-SSRS

To use the function, just call it from the Code object. Here is a call to the function from the same place where we had the expression earlier—in the color property of the textbox that displays the total sales.

006-Using-Custom-Code-in-SSRS

Don’t worry about the red squiggly. There is no error, here. SSRS is just a little confused; this won’t prevent the report from running.

Enjoy using code in SSRS!

Peter Avila
SQL Server Instructor – Interface Technical Training
Phoenix, AZ

The post Using Custom Code in SSRS appeared first on Interface Technical Training.

]]>
https://www.interfacett.com/blogs/using-custom-code-ssrs/feed/ 2
How to Display Blanks as Zeros in a SSRS Report https://www.interfacett.com/blogs/display-blanks-zeros-ssrs-report/ https://www.interfacett.com/blogs/display-blanks-zeros-ssrs-report/#comments Wed, 12 Nov 2014 16:51:14 +0000 http://www.interfacett.com/blogs/?p=?p=19432 Some of the cells in this SSRS matrix report are blank: To display zeros instead, we will use two functions: 1. [crayon-594b47be53ea9650774416/] 2. [crayon-594b47be53eb0074619634/]   In the design of the matrix, right-click on the textbox containing the values in question, then select Expression… to edit the expression underlying the textbox. Type the following expression in … Continue reading How to Display Blanks as Zeros in a SSRS Report

The post How to Display Blanks as Zeros in a SSRS Report appeared first on Interface Technical Training.

]]>
Some of the cells in this SSRS matrix report are blank:

001-How-to-Display-Blanks-as-Zeros-in-a-SSRS-Report

To display zeros instead, we will use two functions:

1.

IsNothing(<Val2Check>) returns a True of Val2Check is Null; otherwise, it returns False

2.

IIF(<Test>, <IfTrue>, <IfFalse>) return <IfTrue> if <Test> is True; otherwise, it returns <IfFalse>

 

In the design of the matrix, right-click on the textbox containing the values in question, then select Expression… to edit the expression underlying the textbox.

002-How-to-Display-Blanks-as-Zeros-in-a-SSRS-Report

Type the following expression in the Expression dialog:

003-How-to-Display-Blanks-as-Zeros-in-a-SSRS-Report

The expression says that if SUM(Fields!SubTotal.Value) is Null (the IsNothing() function that it is in returns a True if it is Null and a Fales if it isn’t), then a zero is returned by the expression; otherwise the SUM(Fields!SubTotal.Value) is used.

Click OK and preview the report again. Notice that zeros are now present in what used to be the blank cells.

004-How-to-Display-Blanks-as-Zeros-in-a-SSRS-Report

Enjoy!

Peter Avila
SQL Server Instructor – Interface Technical Training
Phoenix, AZ

The post How to Display Blanks as Zeros in a SSRS Report appeared first on Interface Technical Training.

]]>
https://www.interfacett.com/blogs/display-blanks-zeros-ssrs-report/feed/ 1
Multiple Joins Work just like Single Joins https://www.interfacett.com/blogs/multiple-joins-work-just-like-single-joins/ https://www.interfacett.com/blogs/multiple-joins-work-just-like-single-joins/#comments Mon, 18 Aug 2014 18:36:41 +0000 http://www.interfacett.com/blogs/?p=?p=18926 Before reading this article, you should have a good understanding of single joins between two tables and be clear on the differences between inner and outer joins. Check out my previous post A Primer on Joins to help you accomplish this. Have you ever looked at a query like the one below and wondered how to read it, … Continue reading Multiple Joins Work just like Single Joins

The post Multiple Joins Work just like Single Joins appeared first on Interface Technical Training.

]]>
Before reading this article, you should have a good understanding of single joins between two tables and be clear on the differences between inner and outer joins. Check out my previous post A Primer on Joins to help you accomplish this.

Have you ever looked at a query like the one below and wondered how to read it, how the different joins work together, and what aliens on what planet wrote such a thing?

001-Multiple-Joins-Work-just-like-Single-Joins

Queries with multiple joins like this one often lead to confusion, such as the one behind this question that I have often heard from students: “There seems to be three tables joined to the Employee table in this query—two are inner joins and the other is an outer join. How can the same table have its non-matching rows eliminated and preserved at the same time in the same query?”

In this article, I will show you that confusions like this one arise from a syntax that encourages us to misunderstand joins, and I’ll offer another way of looking at multiple-join queries that makes questions like the one above melt away.

So, what leads to the confusion? Initially, it might seem that every table that is joined to this query is joined to a previous table; the ON clause suggests this with its references to columns in previous tables.

002-ON-clause-Multiple-Joins-Work-just-like-Single-Joins

But this is not what actually happens in a multi-join query, and so looking at things in this way will lead to head-scratching.

So, what does a multi-join query actually do? It actually does something very simple. It performs a series of incremental, single joins between two tables at a time (while this article refers only to tables for simplicity sake, joins can be between tables, views, table valued functions, CTEs, and derived table subqueries). Each single join produces a single derived table (DT) that is then joined to the next table and so on. Like this:

multiple-joins-work-like-single-joins

JOIN 1: Inner join between Employee and Contact resulting in a derived table, DT1. Because this is an inner join, rows in Employee are excluded if they don’t match any rows in Contact, and vice-versa.

JOIN 2: Outer join between DT1 and JobCandidate resulting in a derived table, DT2. Because this is a left outer join, all rows in DT1 are preserved.

JOIN 3: Inner join between DT2 and SalesPerson resulting in a derived table, DT3. Because this is an inner join, rows in DT2 are excluded if they don’t match any rows in SalesPerson, and vice-versa.

JOIN 4: Outer join between DT3 and SalesOrderHeader resulting in a derived table, DT4. Because this is a left outer join, all rows in DT3 are preserved.

JOIN 5: Outer join between DT4 and SalesTerritory resulting in a derived table, DT5. Because this is a left outer join, all rows in DT4 are preserved. DT5 is the final result of the query.

So, what about that confusion arising from the ON clause? With this new way of looking at multiple-join queries, we can now see that the proper way to read an ON clause is not that it joins the new table to a single table that came before it in the query! The only join that does that is the first one; all subsequent ones join a new table to the derived table that is a result of all the joins before it. If an ON clause includes a table alias, that is only to identify the column properly to the query. Table aliases are only required when there is ambiguity—when two or more columns have the same name in the derived table that precedes the current join because they came from different tables.

004-derived-table-DT-Multiple-Joins-Work-just-like-Single-Joins

I hope this new way of looking at multiple-join queries helps make it easier and more productive for you to work with joins.

Have fun!

Peter Avila
SQL Server Instructor – Interface Technical Training
Phoenix, AZ

The post Multiple Joins Work just like Single Joins appeared first on Interface Technical Training.

]]>
https://www.interfacett.com/blogs/multiple-joins-work-just-like-single-joins/feed/ 14
A Primer on Joins https://www.interfacett.com/blogs/a-primer-on-joins/ Mon, 04 Aug 2014 22:45:03 +0000 http://www.interfacett.com/blogs/?p=?p=18714 The golden rule of OLTP database design is that every table should represent one, and only one, entity-type. Without this rule, databases can exhibit three data anomalies (insertion, deletion, and update anomalies) that undermine data integrity. But adherence to the rule comes at a price of greater overhead (though it is a price well worth … Continue reading A Primer on Joins

The post A Primer on Joins appeared first on Interface Technical Training.

]]>
The golden rule of OLTP database design is that every table should represent one, and only one, entity-type. Without this rule, databases can exhibit three data anomalies (insertion, deletion, and update anomalies) that undermine data integrity. But adherence to the rule comes at a price of greater overhead (though it is a price well worth paying for data integrity). Data are spread out among more tables and so queries will have to do more work to bring together (or join) all the tables it needs (this overhead can be greatly reduced by running queries against an OLAP database instead, such as a data warehouse as we see in the data warehouse course 10777: Implementing a Data Warehouse with SQL Server 2012 SSIS).

So, what are joins, how do they work, and when do we use them? I’ll answer these questions with a few examples. If you want to follow along by doing, run the following script in your SQL Server Management Studio. It’ll create a database with two tables, SalesPerson and SalesOrder, related to each other by a foreign key in the SalesOrder table. When you are done with this article, you can issue a DROP DATABASE SalesRecords command and your server will be back to normal.

USE master

CREATE DATABASE SalesRecords

USE SalesRecords

CREATE TABLE SalesPerson
(SPID int Identity(1,1) Primary Key
,SPName varchar(35) Not Null
,DeptID int NULL
,ContactID int NULL)

CREATE TABLE SalesOrder
(SOID int Identity(1,1) Primary Key
,OrderDate datetime Not Null
,ShipperID int NULL
,SPID int Not Null
	FOREIGN KEY REFERENCES SalesPerson(SPID))

INSERT INTO SalesPerson
VALUES	('Marla Faulk', 5, 26),
		('Chris Sheppard', 4, 53),
		('Jess Weinstein', 5, 257),
		('Haydee Dias', 5, 93),
		('François Roubichaud', 2, 155)

INSERT INTO SalesOrder
VALUES	('5/4/2014 14:23', 3, 3),
		('5/5/2014 10:38', 4, 2),
		('5/5/2014 13:52', 3, 2),
		('6/3/2014 09:13', 2, 2),
		('6/5/2014 15:04', 2, 3),
		('6/6/2014 13:01', 1, 1),
		('6/8/2014 09:22', 3, 2)

 

Feel the Need

Joins are necessary in queries that must use data from more than one entity-type. To see how this is, let’s have a look at our two tables.

SELECT * FROM SalesPerson
SELECT * FROM SalesOrder

002-new-Joins-are-necessary-in-queries-A-Primer-on-Joins

Let’s say we need an answer to this question: “What departments have used shipper 2 for their orders?” Department ids are in one table and shipper ids are in the other table. A query that answers this question will need to join the two tables.

How Does a Join Work?

A join is an operation of a query that brings together two table-type objects so that data in both can be used by the query.

A join has two inputs and one output. Inputs can be any table-type objects, including tables, views, table-valued functions, and other queries (though I’ll refer to the inputs of a join as tables in this article for simplicity). The two inputs must have a common column—usually a primary key/foreign key pair.

If we’re going to join the SalesPerson and SalesOrder tables, they’ll need to have a common column, and they do. The SPID in the SalesPerson table identifies a sales person and the SPID in the SalesOrder table does too—the sales person who placed the order. A value in one refers to the same thing that the same value in the other does.

003-A-Primer-on-Joins

Here’s a query with a join that brings the two tables together on the common column, SPID (though our example uses two columns with the same name, common columns can have different names). This statement also creates aliases for the two tables, sp and so, that are then used throughout the query as needed.

004-A-Primer-on-Joins

The join operation has a single output, which is a derived table consisting of all columns from both of the tables being joined but only matching rows from both of the tables. The join operation matches rows using the common column; rows from both tables with the same value in the common column are considered a match. Here is the derived table that is the output of the join above.

005-A-Primer-on-Joins

We can now select only the columns we want and refine the query so that we answer our original question about which departments used shipper 2. As you can see in the result of this next query, departments 4 and 5 both used shipper 2.

SELECT DISTINCT ShipperID, DeptID
FROM SalesPerson As sp
JOIN SalesOrder As so
	ON so.SPID = sp.SPID
WHERE ShipperID = 2

006-new-A-Primer-on-Joins

Types of Joins

Every join is either an inner join, an outer join, or a cross join.

Inner Join

An inner join eliminates rows from both inputs that don’t have matching rows in the other input. The example we just worked through is an inner join. Notice that SPID 4, Haydee Dias and 5, François Roubichaud, in the SalesPerson table have no sales orders (no matching rows in the SalesOrder table) and so they did not appear in the resulting derived table (the result of the query before the previous one). Those rows in the SalesPerson table were eliminated by the inner join.

Inner joins have a tendency of eliminating rows. Don’t be surprised if you see a reduction in rows when you use an inner join to add a table to your query!

By the way, the inner join can be written to include the optional world, INNER. The following query is identical to the one we wrote earlier without the word INNER:

SELECT *
FROM HumanResources.Employee As e
INNER JOIN Person.Contact As c
	ON c.ContactID = e.ContactID

007-new-INNER-A-Primer-on-Joins

The result of an inner join between two tables is a derived table that includes all the columns from both tables and only the matching rows from both tables.

Outer Join

The outer join is similar to the inner join but it doesn’t eliminate rows from one or both tables; at least one of the tables is preserved. To specify which table(s) to preserve, use the words LEFT, RIGHT, or FULL in the join statement; the word OUTER is optional. If LEFT JOIN (or LEFT OUTER JOIN) is specified, then the table on the left side of the JOIN keyword will be preserved. If RIGHT JOIN (or RIGHT OUTER JOIN) is specified, then the table on the right side of the JOIN keyword will be preserved. If FULL JOIN (or FULL OUTER JOIN) is specified, then both tables will be preserved.

Let’s say the sales manager wants to find out which sales people do not have sales orders. In the following query, the SalesPerson table is preserved and the result now includes the two sales people with no orders—the ones with SPIDs 4 and 5 that were eliminated by the inner join we wrote earlier. Notice the NULLs in the columns of the table where there are no matches.

 

SELECT *
FROM SalesPerson As sp
LEFT OUTER JOIN SalesOrder As so
	ON so.SPID = sp.SPID

008-new-INNER-A-Primer-on-Joins

The left outer join preserves the table on the left side of the JOIN operation; all rows in that table are included in the derived table even if they don’t have matching rows in the table on the right side. If they don’t have matching rows, then NULLs are used.

Finally, to answer the sales manager’s question about which sales people do not have sales orders, we need only filter our outer join to show only those rows with a NULL in the primary key column of the SalesOrder table, since the only way that a primary key can have a NULL in it is if there is no match! We’ll also ask the query to display only the SPID and SPName columns.

SELECT sp.SPID
, sp.SPName SalesPerson
FROM SalesPerson As sp
LEFT OUTER JOIN SalesOrder As so
	ON so.SPID = sp.SPID
WHERE SOID Is Null

009-new-A-Primer-on-Joins

This type of exception question, “Which rows in this table have no matching rows in that table,” is a typical application for an outer join. Questions such as, “Which employees do not have resumes on file?”, “Which customers have not placed orders in the last six months?” and others can all be answered with outer joins.

But perhaps an even more common use for the outer join is when we need optional data. Optional data are data from another table that would be nice to have in a query but are not necessary to include if they don’t exist. In other words, if any rows in our query do not have matches in the new table, we don’t want that to be a reason to eliminate those rows that are already in our query. As an example, the previous outer join could have been used to fulfill the following request, too: “Show me all sales people and include their orders if they have any.”

Cross Join

The cross join is being mentioned here only for the sake of completeness. It is not a common join and should actually be avoided if possible, because it has a tendency to suck up resources. A cross join is a join that matches every row in one input to every row in the other input. For example, let’s say we want to match up every customer in the Customer table with every promotion in the Promotion table. The following cross join can accomplish that, assuming the two tables, Customer and Promotion, exist in the database:

SELECT *
FROM Customer
CROSS JOIN Promotion

I hope this article helps you get more from SQL.
Enjoy!
Peter Avila
SQL Server Instructor – Interface Technical Training
Phoenix, AZ

The post A Primer on Joins appeared first on Interface Technical Training.

]]>
Exploring Join Paths; The Key to Building Multiple-Table Joins https://www.interfacett.com/blogs/exploring-join-paths-the-key-to-building-multiple-table-joins/ https://www.interfacett.com/blogs/exploring-join-paths-the-key-to-building-multiple-table-joins/#respond Fri, 30 May 2014 16:58:09 +0000 http://www.interfacett.com/blogs/?p=?p=17851 As we see in the SQL 100 and SQL 250 courses, and as we further explore in this article, joins are used in queries that need to access data stored in more than one table (served up either by the table itself, views, or functions). This article introduces the concept of the “join path” and … Continue reading Exploring Join Paths; The Key to Building Multiple-Table Joins

The post Exploring Join Paths; The Key to Building Multiple-Table Joins appeared first on Interface Technical Training.

]]>
As we see in the SQL 100 and SQL 250 courses, and as we further explore in this article, joins are used in queries that need to access data stored in more than one table (served up either by the table itself, views, or functions). This article introduces the concept of the “join path” and then uses the AdventureWorks database to explore join paths as a way to identify the tables to include in a query. This article also makes the case that, when there is more than one join path between two tables, each one can produce a different query result, so it’s important to choose carefully to maintain the intent of the query.

Tables are not the only things that can be joined in a query. Views and functions can also be joined. This article keeps the focus on tables for simplicity, but every time we join a table in this article, we could be joining a view or function, too.

1.       What’s a Join Path?

First, it’s important to distinguish between joins and relationships. Relationships between tables are defined in the structure of the tables themselves using primary and foreign keys, whereas joins are operations in a query that bring together related tables; joins are usually performed among related tables unless there is a good reason to do otherwise (here’s an example of a query that does otherwise: How to Use Values from Previous or Next Rows in a SQL Server Query and we’ll also get a chance to see a simpler example in this article).

So, what’s a join path? A join path brings two tables together in a query by using one or more joins. There are two types of join paths—direct and indirect. If the two tables are directly related to each other (one table has a foreign key that references the primary key in the other table), then a direct join path is available that consists of a single join between the two tables. An indirect join path is one that consists of two or more joins through other tables. As you’ll see in this article, an indirect join path must be used when there is no direct join path, or when a direct join path exists but produces results that are not consistent with the intent of the query.

Let’s work through a few examples of join paths to get a better handle on all this.

2.       Direct Join Paths

A direct join path is one that goes from one table to another without any additional tables needed.

Example 2.1. A Direct Join Path

Let’s say that a query needs to list the names of all salaried employees from the AdventureWorks database. Here is a sample of the result of the query (there is also a SalariedFlag column that is used to filter the query so that only salaried employees are selected; the SalariedFlag column is not listed in the result, but it is still used in the query):

001-SQL-Server-Join-Paths-query

What tables are needed to produce this result? Well, employee’s names are in the Contact table and their salaried flags are in the Employee table, so we’ll need those two tables. A direct join path is available in this case, because the two tables are directly related to each other.

002-SQL-Server-Join-Paths-query

The direct join path can be used in this query because the results it produces are consistent with the intent of the query (this will become clearer in the next example when we have a choice between more than one join path).

003-SQL-Server-Join-Paths-query

Example 2.2. Multiple Direct Join Paths

Can there be more than one direct join path between two tables? Sure. Let’s look at a query that lists the sales order number, the order date, and the city of the bill-to address. Here’s a sample of the query result:

004-SQL-Server-Join-Paths-query

What tables are needed to produce this result? Well, the sales order numbers and order dates will come from the SalesOrderHeader table and the bill-to addresses will come from the Address table, so we’ll need those tables. Are the tables directly related to each other like in the previous example? Not only are they related, but they are related twice. The SalesOrderHeader table has two foreign keys in it, each of which references the primary key in the Address table:

005-SQL-Server-Join-Paths-query

With two direct relationships between these tables, there are two different direct join paths available. Which one should we use? Does it matter?

It does matter. The two join paths will produce different results. The direct join path that uses the BillToAddressID foreign key will show the cities of the bill-to addresses, and the one that uses the ShipToAddressID foreign key will show the cities of the ship-to addresses. Because it is the cities of bill-to addresses that our query intends to show, we must use the direct join path that uses the BillToAddressID foreign key:

006-SQL-Server-Join-Paths-query

Example 2.3. Using Two Join Paths in the Same Query

There is nothing that prevents us from showing the cities of both the bill-to and ship-to addresses in this case. We just need to join the Address table a second time using the other direct join path. What’s important to keep in mind here is, again, that different join paths produce different results, so if we include two join paths between the same tables, each one will give us something different.

007-SQL-Server-Join-Paths-query

3.       Indirect Join Paths

Direct join paths are relatively simple, but they’re not always available or they don’t always produce results that are consistent with the intent of the query. When we can’t use a direct join path, we must find an indirect one. An indirect join path is one that joins two tables together through other tables.

Example 3.1. Indirect Join Paths

Let’s say that we want to write a query that shows, for each product, all the sales people who have sold that product. We want the query to list the product name, the sales person’s ID, and the salesperson’s YTD sales. Here is a sample of the query result:

008-SQL-Server-Join-Paths-query

What tables are needed to produce this result? Well, the product names are in the Product table and the salespersons’ IDs and YTD sales are in the SalesPerson table, so we’ll need those two tables. Since the two tables are not directly related to each other, there is no direct join path available in this case. We’ll have to find a join path through other tables.

When we examine the relationships in the AdventureWorks database, we can see that there are two relationship paths that can be used to join the Product and SalesPerson tables. Notice in the diagram below that the green relationship path goes through the SalesOrderHeader table while the red one goes through the PurchaseOrderHeader table:

0091-SQL-Server-Join-Paths-query

A join path created along either the green or red relationship lines would be an indirect join path because it would join the SalesPerson and Product tables through other tables. Notice that, even though we don’t use data from any of those other tables in our query, we still need to include them in the query to select the relevant rows from the two tables from which we do want data.

Again, which path do we choose? As we’ve seen, different join paths can produce very different query results, so we have to choose the path that is consistent with the intent of the query. The red path would show all sales people who have purchase orders—not necessarily sales—for each product. The green path would show all sales people who have sales for each product. The green path is the one that is consistent with the intent of the query.

We’ll use the green path, but with one simplification; there is a table in it that we really don’t need. To see this clearly, let’s have a closer look at the green path. We’ll start with the SalesPerson table (we could start from either end since these would all be inner joins). The SalesPerson table would be joined to the SalesOrderHeader table on SalesPersonID (that is the foreign key in the SalesOrderHeader table that references the primary key in the SalesPerson table), resulting in a virtual table in which every salesperson is matched up with every one of their orders. Next, that virtual table would be joined to the SalesOrderDetail table on SalesOrderID, resulting in another virtual table that matches up every salesperson with every product on each of their orders.

All we’d need now would be the product name that is two tables over. Do we really need to go through the SpecialOfferProduct table? If we do, we would be using the ProductID in the SalesOrderDetail table to look up a row in the SpecialOfferProduct table so that we could get a ProductID (the same one we started with) from the SpecialOfferProduct table to allow us to join the Product table. But why look up something we already have? We’ll just bypass the SpecialOfferProduct table and join the SalesOrderDetail table directly to the Product table using each table’s ProductID (this is another example of when we can use a join that is not along relationship lines). Here’s our query:

010-SQL-Server-Join-Paths-query

Example 3.2. More Indirect Join Paths

This next example further highlights how different join paths can produce very different results. In the partial diagram below of the same AdventureWorks database, we see three paths—shown in red, green, and blue—between the Employee and Address tables.

011-SQL-Server-Join-Paths-query

A query that uses the red join path will show employees’ home addresses. A query that uses the green join path will show the ship-to address on orders in which an employee acted as the salesperson (or bill-to address depending on which foreign key in the SalesOrderHeader table is used, a minor but also significant fork in the path). And a query that uses the blue join path will show the ship-to (or bill-to) address on orders in which employees acted as contacts.

4.       Direct Join Paths Are Not Always the Best Choice Over Indirect Join Paths

What if there are direct and indirect join paths available between two tables we want to join? Can we say that we should always use the direct join path over an indirect one? After all, direct paths are simpler and they don’t weigh the query down so much with other tables. But remember, join paths should be chosen based on how consistent their results are with the intent of the query; it will do you no good to have a simple join if it gives you apples when you want oranges. Have a look at this:

Example 4.1. Direct Join Paths Are Not Always the Best Choice

Let’s say we want to write a query that shows all customers who have orders with total due values over $1,000. The query needs to list the customer’s name, the sales order number, and the total due amount. Here is a sample of the query result:

012-SQL-Server-Join-Paths-query

What are the tables needed to produce this result? Well, the sales order numbers and total due amounts are in the SalesOrderHeader table, and the customer names are in the Contact table, so we’ll need those two tables. And, like in the previous example, the SalesOrderHeader and Contact tables are directly related to each other (the ContactID foreign key in the SalesOrderHeader table references the ContactID primary key in the Contact table):

013-SQL-Server-Join-Paths-query

Looks like we’re on easy street again! All we have to do is join the two tables on the ContactID and we’re done, right? Wrong.

Let’s recall the intent of the query. We want a query that lists customer names, not contact names. If we join the two tables on ContactID, the result will show the names of the contacts on the order and not the names of the customers. The direct join path is not consistent with the intent of the query.

Now look carefully at the SalesOrderHeader table in the figure above. There is a CustomerID column just above the ContactID column. Is there another path between the two tables that would allow us to join on that CustomerID, instead? There is. In the figure below, the blue path goes through the Individual table. The Individual table contains both a CustomerID and a ContactID, effectively associating each CustomerID with a corresponding ContactID. We can look up the customer ID in the individual table and use the corresponding contact ID to find their names in the Contact table.

014-SQL-Server-Join-Paths-query

For the same reason as in example 3.1, we will bypass the Customer table and join the SalesOrderHeader table directly to the Individual table on the CustomerID (if we don’t bypass it, the CustomerID in the SalesOrderHeader table would be used to look up a Customer in the Customer table so that the query could get a CustomerID to find the related row in the Individual table; but, since we already start out with a CustomerID, that lookup (join) is unnecessary). Here’s our query:

015-SQL-Server-Join-Paths-query

5.       In Summary…

Join paths provide a useful tool to help us identify the tables we need to include in a query. When more than one join path exists between tables—whether direct, indirect, or both—they can each produce very different query results. The join path you choose must be consistent with the intent of your query if you don’t want unintended results!

Enjoy!
Peter Avila
SQL Server Instructor – Interface Technical Training
Phoenix, AZ

The post Exploring Join Paths; The Key to Building Multiple-Table Joins appeared first on Interface Technical Training.

]]>
https://www.interfacett.com/blogs/exploring-join-paths-the-key-to-building-multiple-table-joins/feed/ 0