DATABASE

SQL Server 2008 : T-SQL Tips and Tricks (part 2) - Using CONTEXT_INFO & Working with Outer Joins

5/17/2011 4:05:08 PM

Using CONTEXT_INFO

Although SQL Server enables you to define local variables within a T-SQL batch or stored procedure, local variables do not retain values between batches or stored procedures. Unfortunately, SQL Server 2008 does not enable you to create user-defined global variables. However, you can simulate global variables by using the CONTEXT_INFO setting, which allows you to store information in the context_info column in the sys.sysprocesses catalog view. A row in sys.sysprocesses exists for every connection to SQL Server, so the data remains there until you disconnect from SQL Server.

The context_info column is a binary (128) column. You can store any data value in it with the SET CONTEXT_INFO command, but you have to deal with hexadecimal data when retrieving it. If you are handy at manipulating hexadecimal data, you can store multiple values in the context_info column. The following example stores the average price from the titles table in the context_info column:

declare @avg_price money
select @avg_price = avg(price) from dbo.titles
set context_info @avg_price

You can retrieve the value stored in context_info by using a SELECT statement. You need to convert the binary data back to money when you retrieve it. Because avg(price) is the only value stored in context_info, you can retrieve it by performing a substring on the first 8 bytes of the context_info column. (The money data type is 8 bytes in size.) Because SQL Server assigns a unique server process ID (SPID) to each connection, you use the @@SPID function to retrieve the information for the current connection:

select convert(money, substring(context_info, 1, 8)) as AVG_PRICE
from master..sysprocesses
where spid = @@spid
go

AVG_PRICE
---------------------
0.3751

If you don’t use a substring to specify only the first 8 bytes of the context_info column, SQL Server assumes that the money data is stored in the last 8 bytes and returns a result of 0:

select convert(money, context_info) as AVG_PRICE
from master..sysprocesses
where spid = @@spid
go

AVG_PRICE
---------------------
0.00

Because money can be implicitly converted to binary, you don’t need to convert it when setting context_info. For some other data types, such as char or datetime, you need to explicitly convert the data to binary because implicit conversions from those data types to binary is not supported. In the following example, you append a datetime value to the average price value already stored in context_info. You explicitly convert the datetime value to binary and append it to the 8 bytes you have already stored in context_info:

declare @max_date datetime,
@context_info binary(128)
select @max_date = max(pubdate) from dbo.titles
select @context_info = substring(context_info, 1, 8)
+ convert(binary(8), @max_date)
from master..sysprocesses
where spid = @@spid
set context_info @context_info

You now have two values stored in context_info. Using the appropriate substring, you can retrieve either the average price or the maximum pubdate from context_info:

declare @avg_price money,
@max_pubdate datetime
select @avg_price = substring(context_info, 1, 8),
@max_pubdate = substring(context_info, 9, 8)
from master..sysprocesses
where spid = @@spid

select @avg_price as 'Avg Price', @max_pubdate as 'Max PubDate'
go

Avg Price Max PubDate
--------------------- -----------------------
0.3751 2009-05-31 00:00:00.000

Note that the binary data converts implicitly to money and datetime.

Working with Outer Joins

An outer join is used to return all the rows from the specified outer table (specified with LEFT OUTER, RIGHT OUTER, or FULL OUTER), even if the other table has no match. Rows returned from the outer table that have no corresponding match in the inner table display the value NULL for any columns retrieved from the inner table. For example, you might want to display the names of all authors along with the average royalty paid, if available:

select au_lname, au_fname, avg(royaltyper) as avg_royalty
from dbo.authors a left outer join dbo.titleauthor ta on a.au_id = ta.au_id
group by au_lname, au_fname
order by 3
go

au_lname au_fname avg_royalty
---------------------------------------- -------------------- -----------
Greene Morningstar NULL
Greenfield Tom NULL
McBadden Heather NULL
Smith Meander NULL
Stringer Dirk NULL
Gringlesby Burt 30
O'Leary Michael 35
Ringer Anne 37
Yokomoto Akiko 40
MacFeather Stearns 42
Hunter Sheryl 50
Dull Ann 50
Bennet Abraham 60
Green Marjorie 70
DeFrance Michel 75
Karsen Livia 75
Ringer Albert 75
Panteley Sylvia 100
White Johnson 100
Straight Dean 100
Locksley Charlene 100
Carson Cheryl 100
Blotchet-Halls Reginald 100
del Castillo Innes 100



Note that no real difference exists between left and right outer joins except for specifying which table on which side of the join condition is to be the controlling, or outer, table. For example, the previous query would provide the same result if you reversed the tables in the join clause and made it a right outer join:

select au_lname, au_fname, avg(royaltyper)
from dbo.titleauthor ta right outer join dbo.authors a on ta.au_id = a.au_id
group by au_lname, au_fname
order by 3



A full outer join returns all matching rows from both tables, along with all rows from each table, without a corresponding match in the other table:

select a.au_fname, p.pub_name
from dbo.authors a full outer join dbo.publishers p on a.state = p.state
go

au_fname pub_name
-------------------- ----------------------------------------
James A. NULL
Francine du Plessix Kumquat Technical Publishing
Jean NULL
E.L. Nordome Titles
Justin Algodata Infosystems
April Sidney's Books and More
Ron Algodata Infosystems
Jack NULL
Matt NULL
Josef NULL
...
Albert Ramona Publishers
NULL Gooseberry Titles
NULL Binnet & Hardley
NULL GGG&G
NULL Lucerne Publishing
NULL Tomato Books
NULL Significant Titles Company



OUTER JOIN Versus WHERE Clause Matching

With the ANSI join syntax, you specify the join condition in the FROM clause. Additional search conditions can be specified in either the JOIN clause or WHEREON clause work differently than search conditions in the WHERE clause. It’s important to remember that in a left or right outer join, search conditions specified in the clause:

  • Search conditions in the WHERE clause always exclude nonmatching rows.

  • Search conditions in the ON clause sometimes exclude rows and sometimes do not.

Needless to say, it can be somewhat confusing to figure out which rows and data values will be returned. To demonstrate the use of search conditions in the WHERE clause versus the ON clause, the examples presented in this section make use of the two views shown in Listing 3.

Listing 3. CREATE VIEW Statements for Outer Join Examples
CREATE VIEW dbo.STORES_V1 AS
SELECT STOR_ID, CITY FROM DBO.STORES WHERE STOR_ID BETWEEN 'A001' AND 'A005'
go
CREATE VIEW dbo.STORES_V2 AS
SELECT STOR_ID, STATE FROM DBO.STORES WHERE STOR_ID BETWEEN 'A002' AND 'A007'
UNION ALL
SELECT 'A004', 'MA'
go

select * from dbo.stores_v1
select * from dbo.stores_v2

STOR_ID CITY
------- --------------------
A001 Dublin
A002 Oakland
A003 Bisbee
A004 White Plains
A005 Thomaston

(5 row(s) affected)

STOR_ID STATE
------- -----
A002 NJ
A003 AZ
A004 NY
A005 GA
A006 CA
A007 CA
A004 MA



Listing 4 provides an example of a left outer join query that includes the search condition in the ON clause. All rows are returned from STORES_V1, and NULL values are returned for STORES_V2 where there isn’t a matching STOR_ID or where there is a matching STOR_ID but STATE is not equal to 'GA'.

Listing 4. Specifying the Search Condition in the ON Clause
SELECT * FROM DBO.STORES_V1 V1
LEFT OUTER JOIN DBO.STORES_V2 V2
ON V1.STOR_ID = V2.STOR_ID
AND V2.STATE <> 'GA'
ORDER BY V1.STOR_ID ,V2.STATE
go

STOR_ID CITY STOR_ID STATE
------- -------------------- ------- -----
A001 Dublin NULL NULL
A002 Oakland A002 NJ
A003 Bisbee A003 AZ
A004 White Plains A004 MA
A004 White Plains A004 NY
A005 Thomaston NULL NULL

Now, if you put the search condition in the WHERE clause, as shown in Listing 5, the nonmatching rows from STORES_V2 are filtered out because the NULL value returned for STATE does not match the WHERE clause search criteria WHERE V2.STATE <> 'GA'. (Remember that NULL values are neither equal to nor not equal to other values.)

Listing 5. Specifying a Search Condition in a WHERE Clause
SELECT * FROM DBO.STORES_V1 V1
LEFT OUTER JOIN DBO.STORES_V2 V2
ON V1.STOR_ID = V2.STOR_ID
WHERE V2.STATE <> 'GA'
ORDER BY V1.STOR_ID ,V2.STATE
go

STOR_ID CITY STOR_ID STATE
------- -------------------- ------- -----
A002 Oakland A002 NJ
A003 Bisbee A003 AZ
A004 White Plains A004 MA
A004 White Plains A004 NY

To include the nonmatching rows from STORES_V2, you need to add a check for NULL to the WHERE clause, as shown in Listing 6.

Listing 6. Including Nonmatching Inner Table Rows That Do Not Match a WHERE Clause Search Condition
SELECT * FROM DBO.STORES_V1 V1
LEFT OUTER JOIN DBO.STORES_V2 V2
ON V1.STOR_ID = V2.STOR_ID
WHERE (V2.STATE <> 'GA' or V2.STATE is NULL)
ORDER BY V1.STOR_ID ,V2.STATE
go

STOR_ID CITY STOR_ID STATE
------- -------------------- ------- -----
A001 Dublin NULL NULL
A002 Oakland A002 NJ
A003 Bisbee A003 AZ
A004 White Plains A004 MA
A004 White Plains A004 NY

Notice that the row where STOR_ID = 'A005' is still not included in the result set. The query shown in Listing 6 also demonstrates another difference between specifying a search condition in the WHERE clause and the ON clause. In this case, the outer join is performed first, so that all rows and selected column values are returned from STORES_V2, including the row where STOR_ID = 'A005', without considering the WHERE clause condition:

SELECT * FROM DBO.STORES_V1 V1
LEFT OUTER JOIN DBO.STORES_V2 V2
ON V1.STOR_ID = V2.STOR_ID
go

STOR_ID CITY STOR_ID STATE
------- -------------------- ------- -----
A001 Dublin NULL NULL
A002 Oakland A002 NJ
A003 Bisbee A003 AZ
A004 White Plains A004 NY
A004 White Plains A004 MA
A005 Thomaston A005 GA

After the join result is returned, because the STATE value is equal to 'GA', the search condition in the WHERE clause (V2.STATE <> 'GA') filters out that row.

In a left or right outer join, a search condition in the ON clause also works differently, depending on whether it refers to a field in the inner or outer table. If the ON clause search condition refers to a field in the outer table, it determines whether the related row finds a match. (The outer row is returned regardless.) For example, Listing 7 demonstrates a search condition in the ON clause that restricts which rows in STORES_V1 (the outer table) join to STORES_V2. The join is performed only for those rows in STORES_V1 where CITY is greater than N. However, all rows from STORES_V1 are still returned.

Listing 7. Specifying an ON Clause Search Condition on the Outer Table
SELECT * FROM DBO.STORES_V1 V1
LEFT OUTER JOIN DBO.STORES_V2 V2
ON V1.STOR_ID = V2.STOR_ID
AND V1.CITY > 'N'
ORDER BY V1.STOR_ID ,V2.STATE
go

STOR_ID CITY STOR_ID STATE
------- -------------------- ------- -----
A001 Dublin NULL NULL
A002 Oakland A002 NJ
A003 Bisbee NULL NULL
A004 White Plains A004 MA
A004 White Plains A004 NY
A005 Thomaston A005 GA

If the ON clause search condition refers to a field in the inner table, it determines whether the related row matches the join. Listing 8 shows an example of specifying the ON clause search condition on the inner table. Again, notice that all rows from STORES_V1 are returned, but only the matching rows are returned from STORES_V2, where STOR_ID is less than A004.

Listing 8. Specifying an ON Clause Search Condition on the Inner Table
SELECT * FROM DBO.STORES_V1 V1
LEFT OUTER JOIN DBO.STORES_V2 V2
ON V1.STOR_ID = V2.STOR_ID
AND V2.STOR_ID < 'A004'
ORDER BY V1.STOR_ID ,V2.STATE
go

STOR_ID CITY STOR_ID STATE
------- -------------------- ------- -----
A001 Dublin NULL NULL
A002 Oakland A002 NJ
A003 Bisbee A003 AZ
A004 White Plains NULL NULL
A005 Thomaston NULL NULL

When you perform a left or right outer join, a search condition against the outer table in the ON clause works differently from the same search condition specified in the WHERE clause. As shown in Listing 8, when the search condition in the ON clause filters on a field in the outer table, the outer row is returned, regardless, with no matching rows returned from the inner table. However, if the search condition on the OUTER table is specified in the WHERE clause, the outer row is eliminated from the result set, as shown in Listing 9.

Listing 9. Specifying an Outer Table Search Condition in the WHERE Clause
SELECT * FROM DBO.STORES_V1 V1
LEFT OUTER JOIN DBO.STORES_V2 V2
ON V1.STOR_ID = V2.STOR_ID
WHERE V1.CITY > 'N'
ORDER BY V1.STOR_ID ,V2.STATE
go

STOR_ID CITY STOR_ID STATE
------- -------------------- ------- -----
A002 Oakland A002 NJ
A004 White Plains A004 MA
A004 White Plains A004 NY
A005 Thomaston A005 GA

Nested Outer Joins

When using the ANSI JOIN syntax, you must be careful about mixing outer joins with inner joins because the query may not generate the desired result if the outer join is not continued down to the lowest level. For example, consider the query shown in Listing 10. The intent of this query is to list all authors whose state equals NE and, for those authors, display the titles of their associated books, if any.

Listing 10. An Outer Join Nested in an Inner Join
select left(au_lname, 20) as au_lname, au_fname, left(title, 20) as title
from dbo.authors a
left join dbo.titleauthor ta on a.au_id = ta.au_id
join dbo.titles t on ta.title_id = t.title_id
where a.state = 'NE'
go
au_lname au_fname title
-------------------- -------------------- --------------------
Patterson Richard North The Lasko Tangent
Patterson Richard North The Outside Man
Patterson Richard North Private Screening
Patterson Richard North Eyes of a Child
Patterson Richard North Degree of Guilt
Patterson Richard North Escape the Night

However, because the outer join is not carried to the titles table, the author with no matching row in the titleauthor table ends up getting filtered from the result set. The reason is that when the outer join is performed between authors and titleauthor, because no matching row is found, NULL is returned for title_id. Because a normal inner join is performed on the titles table, the NULL value for title_id does not match any rows in the titles table, so the author with no matching rows in titleauthor ends up getting filtered out. To see those authors, you need to modify the query in Listing 10 to carry the outer join down to the join between titleauthor and authors:

select left(au_lname, 20) as au_lname, au_fname, left(title, 20) as title
from dbo.authors a
left join dbo.titleauthor ta on a.au_id = ta.au_id
left join dbo.titles t on ta.title_id = t.title_id
where a.state = 'NE'
go

au_lname au_fname title
-------------------- -------------------- --------------------
Patterson Richard North The Lasko Tangent
Patterson Richard North The Outside Man
Patterson Richard North Private Screening
Patterson Richard North Eyes of a Child
Patterson Richard North Degree of Guilt
Patterson Richard North Escape the Night
McBadden Heather NULL

Working with Full Outer Joins

A full outer join selects rows from both tables and joins those rows that match on the join fields. In addition to the matching rows, one copy of each nonmatching row from each table is returned. Listing 11 shows an example of a full outer join.

Listing 11. Full Outer Join Example
SELECT * FROM DBO.STORES_V1 V1
FULL OUTER JOIN DBO.STORES_V2 V2
ON V1.STOR_ID = V2.STOR_ID
ORDER BY V1.STOR_ID ,V2.STOR_ID ,V2.STATE
go
STOR_ID CITY STOR_ID STATE
------- -------------------- ------- -----
NULL NULL A006 CA
NULL NULL A007 CA
A001 Dublin NULL NULL
A002 Oakland A002 NJ
A003 Bisbee A003 AZ
A004 White Plains A004 MA
A004 White Plains A004 NY
A005 Thomaston A005 GA

As you can see from the results in Listing 11, all rows are returned from both STORES_V1 and STORES_V2.

In a full outer join, a search condition in the ON clause is handled differently from a search condition in the WHERE clause in that it never results in a row being excluded from the result set. All it does is categorize the input row as being either matching or nonmatching. In Listing 12, a search condition (V1.STOR_ID > 'A003') is specified in the ON clause. As you can see, any rows that do not meet that search condition are returned as nonmatching rows.

Listing 12. Specifying a Search Condition in a Full Outer Join ON Clause
SELECT * FROM DBO.STORES_V1 V1
FULL OUTER JOIN DBO.STORES_V2 V2
ON V1.STOR_ID = V2.STOR_ID
AND V1.STOR_ID > 'A003'
ORDER BY V1.STOR_ID ,V2.STOR_ID ,V2.STATE
go

STOR_ID CITY STOR_ID STATE
------- -------------------- ------- -----
NULL NULL A002 NJ
NULL NULL A003 AZ
NULL NULL A006 CA
NULL NULL A007 CA
A001 Dublin NULL NULL
A002 Oakland NULL NULL
A003 Bisbee NULL NULL
A004 White Plains A004 MA
A004 White Plains A004 NY
A005 Thomaston A005 GA

As you can see, when you work with full outer joins, search arguments in the ON clause serve only to define which rows are used as matching rows for a join. Search arguments specified in the WHERE clause define which rows are returned. It’s important to note that the WHERE clause conditions behave as if the join is an inner join and are applied after the join is done. Listing 13 shows an example.

Listing 13. Specifying a Search Condition in a Full Outer Join WHERE Clause
SELECT * FROM dbo.STORES_V1 V1
FULL OUTER JOIN dbo.STORES_V2 V2
ON V1.STOR_ID = V2.STOR_ID
WHERE V1.STOR_ID > 'A003'
ORDER BY V1.STOR_ID ,V2.STOR_ID ,V2.STATE
go

STOR_ID CITY STOR_ID STATE
------- -------------------- ------- -----
A004 White Plains A004 MA
A004 White Plains A004 NY
A005 Thomaston A005 GA

Other  
  •  SQL Server 2008 : General T-SQL Performance Recommendations
  •  SQL Server 2008 : General T-SQL Coding Recommendations (part 2) - Avoid SQL Injection Attacks When Using Dynamic SQL & Comment Your T-SQL Code
  •  SQL Server 2008 : General T-SQL Coding Recommendations (part 1) - Provide Explicit Column Lists & Qualify Object Names with a Schema Name
  •  SQL Server 2008 : Advanced Stored Procedure Programming and Optimization - Using Extended Stored Procedures
  •  SQL Server 2008 : Advanced Stored Procedure Programming and Optimization - Installing and Using .NET CLR Stored Procedures
  •  SQL Server 2008 : Advanced Stored Procedure Programming and Optimization - Using Dynamic SQL in Stored Procedures
  •  SQL Server 2008 : Advanced Stored Procedure Programming and Optimization - Stored Procedure Performance
  •  SQL Server 2008 : Using Remote Stored Procedures
  •  SQL Server 2008 : Using Temporary Tables in Stored Procedures
  •  SQL Server 2008 : Advanced Stored Procedure Programming and Optimization - Nested Stored Procedures
  •  
    Video
    Top 10
    SG50 Ferrari F12berlinetta : Prancing Horse for Lion City's 50th
    The latest Audi TT : New angles for TT
    Era of million-dollar luxury cars
    Game Review : Hearthstone - Blackrock Mountain
    Game Review : Battlefield Hardline
    Google Chromecast
    Keyboards for Apple iPad Air 2 (part 3) - Logitech Ultrathin Keyboard Cover for iPad Air 2
    Keyboards for Apple iPad Air 2 (part 2) - Zagg Slim Book for iPad Air 2
    Keyboards for Apple iPad Air 2 (part 1) - Belkin Qode Ultimate Pro Keyboard Case for iPad Air 2
    Michael Kors Designs Stylish Tech Products for Women
    REVIEW
    - First look: Apple Watch

    - 3 Tips for Maintaining Your Cell Phone Battery (part 1)

    - 3 Tips for Maintaining Your Cell Phone Battery (part 2)
    Popular Tags
    Video Tutorail Microsoft Access Microsoft Excel Microsoft OneNote Microsoft PowerPoint Microsoft Project Microsoft Visio Microsoft Word Active Directory Exchange Server Sharepoint Sql Server Windows Server 2008 Windows Server 2012 Windows 7 Windows 8 Adobe Flash Professional Dreamweaver Adobe Illustrator Adobe Photoshop CorelDRAW X5 CorelDraw 10 windows Phone 7 windows Phone 8 Iphone