Tag Archives: #SQLServer

All about Microsoft SQL Server

#0169-SQL Server-Table/Column Aliasing does not affect query performance-Part 2


Earlier this week, I wrote a post on whether table and column aliasing affects query performance or not (http://beyondrelational.com/modules/2/blogs/77/Posts/14948/0168-sql-server-tablecolumn-aliasing-does-not-affect-query-performance.aspx) . We were able to determine that overall time-wise, both queries were performing equally well. However, one of the comments received as part of the feedback was to also include results of the following in addition to the use of simply the execution plan.

  • Statistics IO
  • Statistics Profile
  • Actual CPU times

I therefore repeated the tests after modifying the query a little bit to include the necessary SET statements and setting the buffer to a clean state.

IMPORTANT NOTE: In this script, we use 2 DBCC commands – FREEPROCCACHE and DROPCLEANBUFFERS to simulate a cold buffer condition, which is generally achieved when SQL Server restarts. Doing so indiscriminately can have severe performance implications. Please do not execute the scripts shown in this example in your production or quality assurance environments.

USE AdventureWorks2012
GO

/*************************************************************************************************************
                                      IMPORTANT WARNING
--------------------------------------------------------------------------------------------------------------
DBCC FREEPROCCACHE and DROPCLEANBUFFERS are used to simulate a cold buffer condition, 
that is generally achieved when SQL Server restarts. 

Doing so indiscriminately can have severe performance implications. 

Please do not execute the scripts shown in this example in your production or quality assurance environments.

The author, this site or Microsoft are not responsible
for any damage caused by indiscriminate use of these DBCC commands
*************************************************************************************************************/
DBCC FREEPROCCACHE WITH NO_INFOMSGS
GO
DBCC DROPCLEANBUFFERS WITH NO_INFOMSGS
GO

SET STATISTICS IO ON
SET STATISTICS PROFILE ON

SELECT 'Non-Aliased Query',
       HRE.BusinessEntityID,
       HRE.LoginID,
       HRE.NationalIDNumber,
       HRE.OrganizationNode,
       HRE.OrganizationLevel,
       HRE.JobTitle,
       HRE.BirthDate,
       HRE.MaritalStatus,
       HRE.Gender,
       HRE.HireDate,
       HRE.SalariedFlag,
       HRE.VacationHours,
       HRE.SickLeaveHours,
       HRE.CurrentFlag,
       HRE.rowguid,
       HRE.ModifiedDate,
       Dept.DepartmentID,
       Dept.ShiftID,
       Dept.StartDate,
       Dept.EndDate,
       Dept.ModifiedDate,
       HumanResources.Department.Name,
       HumanResources.Department.GroupName,
       HumanResources.Department.ModifiedDate,
       HumanResources.Shift.Name,
       HumanResources.Shift.ShiftID,
       HumanResources.Shift.StartTime,
       HumanResources.Shift.EndTime,
       HumanResources.Shift.ModifiedDate
FROM HumanResources.Employee AS HRE 
INNER JOIN HumanResources.EmployeeDepartmentHistory AS Dept ON HRE.BusinessEntityID = Dept.BusinessEntityID
INNER JOIN HumanResources.Department ON Dept.DepartmentID = HumanResources.Department.DepartmentID 
INNER JOIN HumanResources.Shift ON Dept.ShiftID = HumanResources.Shift.ShiftID
GO

DBCC FREEPROCCACHE WITH NO_INFOMSGS
GO
DBCC DROPCLEANBUFFERS WITH NO_INFOMSGS
GO

SELECT 'Aliased Query',
       EmployeeID = HRE.BusinessEntityID,
       LoginID = HRE.LoginID,
       NationalIDNumber = HRE.NationalIDNumber,
       OrganizationNode = HRE.OrganizationNode,
       OrganizationLevel = HRE.OrganizationLevel,
       JobTitle = HRE.JobTitle,
       BirthDate = HRE.BirthDate,
       MaritalStatus = HRE.MaritalStatus,
       Gender = HRE.Gender,
       HireDate = HRE.HireDate,
       SalariedFlag = HRE.SalariedFlag,
       VacationHours = HRE.VacationHours,
       SickLeaveHours = HRE.SickLeaveHours,
       CurrentFlag = HRE.CurrentFlag,
       rowguid = HRE.rowguid,
       EmployeeModifiedDate = HRE.ModifiedDate,
       DepartmentID = Dept.DepartmentID,
       ShiftID = Dept.ShiftID,
       StartDate = Dept.StartDate,
       EndDate = Dept.EndDate,
       DepartmentModifiedDate = Dept.ModifiedDate,
       DepartmentName = HumanResources.Department.Name,
       GroupName = HumanResources.Department.GroupName,
       DepartmentModifiedDate = HumanResources.Department.ModifiedDate,
       ShiftName = HumanResources.Shift.Name,
       ShiftID = HumanResources.Shift.ShiftID,
       StartTime = HumanResources.Shift.StartTime,
       EndTime = HumanResources.Shift.EndTime,
       ShiftModifiedDate = HumanResources.Shift.ModifiedDate
FROM HumanResources.Employee AS HRE 
INNER JOIN HumanResources.EmployeeDepartmentHistory AS Dept ON HRE.BusinessEntityID = Dept.BusinessEntityID
INNER JOIN HumanResources.Department ON Dept.DepartmentID = HumanResources.Department.DepartmentID 
INNER JOIN HumanResources.Shift ON Dept.ShiftID = HumanResources.Shift.ShiftID
GO

SET STATISTICS IO OFF
SET STATISTICS PROFILE OFF

Running the query gives us the following results:

  • Query output
  • After each query’s output, we receive a result set containing the performance statistics for the given query
  • In the “Messages” tab, the IO statistics for the query execution are available

Let us evaluate each one-by-one:

Statistics I/O

We started with clean buffers for both queries to avoid any impact to the I/O statistics arising out of data already being available in the buffer (i.e. a “logical” read). The statistics I/O for both queries yields the same output, which is:

Table ‘Worktable’. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

Table ‘Employee’. Scan count 1, logical reads 9, physical reads 0, read-ahead reads 7, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

Table ‘EmployeeDepartmentHistory’. Scan count 1, logical reads 4, physical reads 1, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

Table ‘Shift’. Scan count 1, logical reads 2, physical reads 1, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

Table ‘Department’. Scan count 1, logical reads 2, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

Here’s the screenshot:

Output of STATISTICS IO is the same for aliased & non-alised queries

Statistics Profile

The statistics Profile is nothing but a textual representation of the query profile information that can be obtained from the execution plan also. Besides many others, the output contains the following values of interest:

  1. The number of rows processed by each operation
  2. The number of times a particular operation was executed
  3. Operator used
  4. Estimated Rows
  5. Estimated CPU
  6. Average Row Size
  7. Total Subtree cost

To compare the result sets, I simply used Excel. You can use any file comparison tool of your choice.

Comparison of the Profiler statistics for aliased v/s non-alised queries shows that apart from a slight increase in average row size, the values are the same.

Comparing the output reveals that the two result sets are identical except for:

  1. The obvious difference in the query text
  2. A slight increase in the Average Row Size is observed for the aliased query (which can be attributed to the increased number of characters used for aliasing)
  3. The Estimated CPU time and the Total Subtree Cost remains the same

Summarizing

Combining the results of the experiments above and the observations from my previous post, we can summarize that:

  Affected By Aliasing?
Query Performance – IO No
Query Performance – CPU No
Query Performance – Avg. Row Size Possible
Query Performance – Time No
Plan Cache Size No
Plan reuse Possible

What have been your observations related to aliasing? Do leave a note as you go.

Until we meet next time,

Be courteous. Drive responsibly.

#0168-SQL Server-Table/Column Aliasing does not affect query performance


It’s interesting to know about and think over some of the old-wives tales surrounding SQL Server and it’s features. One such tale is that the use of table and column aliases affects query performance.


The very purpose of table and column aliases is to improve the read-ability of the query, and nothing else. They help in eliminating ambiguity, but do not in any way impact the internal workings of SQL Server, and today I will be proving just that.


My example will be for using column aliases. You can work out a similar example for table aliases.


The Example


Below are two straight-forward T-SQL queries, intended for nothing else but to demonstrate the theory mentioned above. The first query does not use column aliases, whereas the second query uses them.


IMPORTANT NOTE: In this script, we use 2 DBCC commands – FREEPROCCACHE and DROPCLEANBUFFERS to simulate a cold buffer condition, that is generally achieved when SQL Server restarts. Doing so indiscriminately can have severe performance implications. Please do not execute the scripts shown in this example in your production or quality assurance environments.

USE AdventureWorks2012
GO

/*************************************************************************************************************
IMPORTANT WARNING
————————————————————————————————————–
DBCC FREEPROCCACHE and DROPCLEANBUFFERS are used to simulate a cold buffer condition,
that is generally achieved when SQL Server restarts.

Doing so indiscriminately can have severe performance implications.

Please do not execute the scripts shown in this example in your production or quality assurance environments.

The author, this site or Microsoft are not responsible
for any damage caused by indiscriminate use of these DBCC commands
*************************************************************************************************************/
DBCC FREEPROCCACHE
GO
DBCC DROPCLEANBUFFERS
GO

SELECT ””””Non-Aliased Query””””,
HRE.BusinessEntityID,
HRE.LoginID,
HRE.NationalIDNumber,
HRE.OrganizationNode,
HRE.OrganizationLevel,
HRE.JobTitle,
HRE.BirthDate,
HRE.MaritalStatus,
HRE.Gender,
HRE.HireDate,
HRE.SalariedFlag,
HRE.VacationHours,
HRE.SickLeaveHours,
HRE.CurrentFlag,
HRE.rowguid,
HRE.ModifiedDate,
Dept.DepartmentID,
Dept.ShiftID,
Dept.StartDate,
Dept.EndDate,
Dept.ModifiedDate,
HumanResources.Department.Name,
HumanResources.Department.GroupName,
HumanResources.Department.ModifiedDate,
HumanResources.Shift.Name,
HumanResources.Shift.ShiftID,
HumanResources.Shift.StartTime,
HumanResources.Shift.EndTime,
HumanResources.Shift.ModifiedDate
FROM HumanResources.Employee AS HRE
INNER JOIN HumanResources.EmployeeDepartmentHistory AS Dept ON HRE.BusinessEntityID = Dept.BusinessEntityID
INNER JOIN HumanResources.Department ON Dept.DepartmentID = HumanResources.Department.DepartmentID
INNER JOIN HumanResources.Shift ON Dept.ShiftID = HumanResources.Shift.ShiftID
GO

SELECT ””””Aliased Query””””,
EmployeeID = HRE.BusinessEntityID,
LoginID = HRE.LoginID,
NationalIDNumber = HRE.NationalIDNumber,
OrganizationNode = HRE.OrganizationNode,
OrganizationLevel = HRE.OrganizationLevel,
JobTitle = HRE.JobTitle,
BirthDate = HRE.BirthDate,
MaritalStatus = HRE.MaritalStatus,
Gender = HRE.Gender,
HireDate = HRE.HireDate,
SalariedFlag = HRE.SalariedFlag,
VacationHours = HRE.VacationHours,
SickLeaveHours = HRE.SickLeaveHours,
CurrentFlag = HRE.CurrentFlag,
rowguid = HRE.rowguid,
EmployeeModifiedDate = HRE.ModifiedDate,
DepartmentID = Dept.DepartmentID,
ShiftID = Dept.ShiftID,
StartDate = Dept.StartDate,
EndDate = Dept.EndDate,
DepartmentModifiedDate = Dept.ModifiedDate,
DepartmentName = HumanResources.Department.Name,
GroupName = HumanResources.Department.GroupName,
DepartmentModifiedDate = HumanResources.Department.ModifiedDate,
ShiftName = HumanResources.Shift.Name,
ShiftID = HumanResources.Shift.ShiftID,
StartTime = HumanResources.Shift.StartTime,
EndTime = HumanResources.Shift.EndTime,
ShiftModifiedDate = HumanResources.Shift.ModifiedDate
FROM HumanResources.Employee AS HRE
INNER JOIN HumanResources.EmployeeDepartmentHistory AS Dept ON HRE.BusinessEntityID = Dept.BusinessEntityID
INNER JOIN HumanResources.Department ON Dept.DepartmentID = HumanResources.Department.DepartmentID
INNER JOIN HumanResources.Shift ON Dept.ShiftID = HumanResources.Shift.ShiftID
GO


Impact on Query Performance


A quick way to compare the performance of two queries is to use the Actual Execution Plan. I therefore set the Actual Execution Plan to ON (press Ctrl + M) and then ran the entire batch (i.e. both queries) together against the AdventureWorks2012 database. The actual execution plan showed an equal – 50/50 split between the two queries.


The Actual Execution plan shows us an equal - 50/50 split between the two test queries - one with column aliasing, the one without


Conclusion 01: Using column aliases does not affect query performance.


Impact on Query Plan Cache


Now that we have derived the conclusion that column aliases do not affect query performance, do they impact the plan cache? We know that query plans are ultimately text, and therefore a query using aliases should have a bigger plan, bloating the plan cache. The Dynamic Management Views provide a powerful way of checking this one out.


I wrote the following query in an attempt to find the query plan size in bytes for a given ad-hoc query, which is:

SELECT cp.size_in_bytes,
st.text,
qs.sql_handle,
qs.plan_handle,
cp.usecounts,
cp.refcounts,
cp.cacheobjtype,
cp.objtype
FROM sys.dm_exec_query_stats AS qs
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) AS st
INNER JOIN sys.dm_exec_cached_plans as cp ON qs.plan_handle = cp.plan_handle
WHERE cp.objtype = ””””Adhoc””””
AND st.text LIKE ””””%HumanResources.EmployeeDepartmentHistory%””””

Which gives us:


Screenshot showing the output of the DMV based query, demonstrating that aliasing does not impact the plan cache


As you can see, there is no difference in the cached plan size for both the queries.


Conclusion 02: Column aliasing does not impact the plan cache.


Conclusion 03: Last year, I wrote a piece on how writing your queries using different cases will cause SQL Server to create multiple query plans. Hence, it is advised to have a single, consistent standard towards using aliases in your product.  You can read the post at: Query Plan Re-use: Write your T-SQL queries with the proper case for plan re-use and better query performance


Summarizing


Based on the observations shared above, I made the following conclusions:















  Is affected by aliasing?
Query Performance No
Plan cache size No
Plan reuse Possible

What have been your observations with respect to aliasing? Do you use aliasing as part of your coding standards? Do leave a note as you go.


Until we meet next time,


Be courteous. Drive responsibly.

#0167-SQL Server-A tale (and Query) of using Catalog Views to get Index Maintenance (rebuild/reorganize) History


It’s been quite a while since I narrated a story to the audience. Today, I will narrate a scenario when Catalog Views helped a fellow colleague of mine – exactly the way it happened. Let’s call him Mike.


Mike: How can I check when a particular index was last rebuilt or defragmented? Are there any DMVs or system tables that can help me get this information?


Nakul: There is no direct way to get this information from SQL Server. There is one indirect way I know of, if you are interested.


Mike: Sure, I would like to know about it.


Nakul: Whenever indexes are rebuilt or reorganized, they update the statistics associated with them (vice-versa is not true). Hence, by looking at the date on which these statistics were last updated, you can determine when the index was last rebuilt/defragmented.


Mike: This is really interesting, and I will try it out. Can you share a query that would help me get this information?


Nakul: Sure, you can use a combination of the STATS_DATE() function and the sys.indexes Catalog View to achieve this. Below is a query that you may find useful:

USE AdventureWorks2008R2
GO
SELECT name AS index_name,
STATS_DATE(object_id, index_id) AS statistics_update_date
FROM sys.indexes
WHERE object_id = OBJECT_ID(‘HumanResources.Employee’);
GO

STATS_DATE() alongwith sys.indexes Catalog View can be used to find when an index was last rebuilt/reorganized


At that moment, a friend and colleague, Hardik Doshi (Blog), joined us.


Hardik: What’s up, guys?


Nakul: I was just working with Mike on a query he has. Would you like to share your thoughts on how to check when a particular index was rebuilt or reorganized?


Hardik: SQL Server does not track index rebuilt or defragmentation operation details automatically. You would need to setup trace or create DDL triggers to track these types of operations. However, I agree with your approach to look at the statistics update information to begin with.


Mike: Hmm… I will make it a point to explore DDL triggers today afternoon.


Hardik: I can help you with more detailed information. Would you be interested? The only issue is that the approach uses catalog views, which are marked for deprecation in one of the future SQL Server releases.


Us: Sure! Let’s go through your approach and see how it works out.


Hardik: Okay, here you go – this query will not only give you the last date/time when the index statistics were updated, but also gives the number & percentage of rows changed during the last operation and the associated index name.

USE AdventureWorks2008R2
GO
SELECT ss.name AS SchemaName,
st.name AS TableName,
s.name AS IndexName,
STATS_DATE(s.id, s.indid) AS ‘Statistics Last Updated’,
s.rowcnt AS ‘Row Count’,
s.rowmodctr AS ‘Number Of Changes’,
CASE WHEN s.rowmodctr > 0
THEN CAST ((CAST (s.rowmodctr AS DECIMAL (28, 8)) / CAST (s.rowcnt AS DECIMAL (28, 2)) * 100.0) AS DECIMAL (28, 2))
ELSE 0
END AS ‘% Rows Changed’
FROM sys.sysindexes AS s
INNER JOIN
sys.tables AS st
ON st.[object_id] = s.[id]
INNER JOIN
sys.schemas AS ss
ON ss.[schema_id] = st.[schema_id]
WHERE s.id > 100
AND s.indid > 0
ORDER BY SchemaName, TableName, IndexName

Using sys.sysindexes compatibility view to identify the changes made during an index rebuild/reorganize


Mike: Hardik & Nakul: Thank-you very, very much for helping me out – I think I now have enough material with me to work upon these queries.


Nakul: Hardik, as you rightly mentioned, it would not be a good idea to use catalog views – after all, they are marked for deprecation.


Hardik: I completely agree. One of these days, let’s figure out a way to replace the catalog view – sys.sysindexes used in my query.


Nakul: Deal!


References:



Until we meet next time,


Be courteous. Drive responsibly.

#0166-Developer Trainings-Importance, Benefits, Tips and follow-up on Pinal Dave’s series


One of the key challenges that the industry is facing is the unavailability of skilled human resources. Being skilled does not mean having just a stack of certifications, but it also means having an understanding about the internals of the products that you are working on – and using that knowledge to improve the efficiency & productivity at the workplace in turn resulting in better products, better consulting abilities and a happier self.


It is therefore, that after completing graduate or post-graduate school, employees often find themselves in another kind of school. This school is incorporated into the workplace, and provides a unique learning experience – one that allows you to practice things as you learn. This school provides what most organizations call “developer training”, and I am a strong supporter of this arrangement.


Developers v/s Test Engineers


There is this never ending war between developers & test engineers. Hence, let me first clarify what I mean by developer. The way I look at it, a test engineer develops test cases and scenarios and in case of automated test methodologies, they write actual code to realize these test cases. So, they are also developers in my opinion – it’s just that their work product is different.


The Benefits of Developer Trainings


Developer training has a lot of benefits – some tangible, others intangible.



  • Training benefits both – employee & employer
  • Employees who effectively use the trainings provided to them make the workplace more efficient & productive – resulting in a happier employees and efficient teams
  • Well-trained employees rise to the occasion in times of need and deal with problems independently. Employees in good morale are good for the organization
  • Training employees is not as costly as hiring new employees

This raises many questions that need to be dealt with. These include, but are not limited to:



  • Trainings are expensive, so who pays for them?
  • What are your duties and liabilities towards your organization for providing you the opportunity to learn?
  • As an organization, you cannot train all your employees at the same time – so, how do you choose who gets the training in this round, and who doesn’t?
  • Once your training has been approved, what do you need to do in order to gain the maximum benefit out of it?

Pinal Dave’s Series on Developer Trainings


Pinal Dave, a.k.a. “SQLAuthority” (B|T) tackles multiple aspects of Developer Training and answers the questions mentioned above about in the series he ran last week. Here are direct links to his posts:



  1. Developer Training – Important & Significance – http://blog.sqlauthority.com/2012/05/28/developer-training-importance-and-significance-part-1/
  2. Developer Training – Employee morals & ethics – http://blog.sqlauthority.com/2012/05/29/developer-training-employee-morals-and-ethics-part-2/
  3. Developer Training – Difficult Questions & Alternate Perspective – http://blog.sqlauthority.com/2012/05/30/developer-training-difficult-questions-and-alternative-perspective-part-3/
  4. Developer Training – Options for Maximum Benefit – http://blog.sqlauthority.com/2012/05/31/developer-training-various-options-for-maximum-benefit-part-4/
  5. Developer Training – A conclusive summary – http://blog.sqlauthority.com/2012/06/01/developer-training-a-conclusive-summary-part-5/

I would recommend that you give these posts a very detailed reading, and make maximum use of the vast amounts of Developer trainings available to you. What’s more – Pinal has made some very interesting cartoons to explain the fundamentals of developer training. Enjoy them to the fullest.


Confused by looking at the various options? Here are some quick & easy tips:


It can be overwhelming to look at the various training options available to you, as a developer. So, I thought it best to share some of the methods I use to get my dose of training:



  • Take out a couple of minutes from your daily schedule – simply taking out 10 minutes from the lunch break and 5 minutes from the coffee breaks results in 15 minutes of training time!
  • Join the local user groups! This is perhaps the easiest way to expose yourself to the latest technologies for free
  • Work with your employer to fund you for paid training opportunities

    • Refer the posts mentioned below from Pinal Dave to learn how to work with your manager for the necessary funding and arrangements

  • Make it a point to visit at least one national or international developer conference/community meet. For developers and IT professionals who work with Microsoft products, the 3-day TechEd is an ideal event to attend
  • Subscribe to on-line training. Companies like Pluralsight (http://www.pluralsight-training.net/microsoft/) provide on-line training at reasonable costs. You can take these trainings from home, when commuting to/from work – all you need is a laptop, an internet connection and a credit card!
  • There is a lot to learn from the forums – make sure to regularly follow at least one forum and attempt to answer the questions being asked by those in need. You will learn a lot, and the participants will thank-you for resolving their queries – it’s for the mutual benefit of all
  • Finally – do not forget to share whatever you have learnt! As Pinal says, always remember that : The beautiful thing about learning is that no one can take it away from you

These are just some of the tips that use. What methods do you use for getting your dose of developer training? Do leave a note as you go.


Until we meet next time,


Be courteous. Drive responsibly.

#0165-SQL Server 2012-Discontinued Features-Use of OUTER operators – *= and =*; Msg: 4147


Often, I have seen developers use code that does not conform to the ANSI standards. The reasons are many, but that is not we are discussing today. What we would discuss is that these non-standard practices need to be stopped, in some cases, by eliminating the feature all-together. The result of this effort is that the code ends up in a lot of rework because the product (in this case, Microsoft SQL Server) ends up either adopting newer standards or discontinuing support of the older, non-standardized code. 


One such coding practice is the use of non-ANSI standard OUTER operators  (*= and =*, for right outer join and left outer join respectively). Such code would typically look like:

USE Northwind
GO
SELECT EmployeeTerritories.EmployeeID,
Territories.TerritoryID,
Territories.TerritoryDescription,
Territories.RegionID
FROM EmployeeTerritories, Territories
WHERE EmployeeTerritories.TerritoryID =* Territories.TerritoryID

Output of an OUTER operator (=*) - similar to RIGHT OUTER JOIN


Effectively, the operator *= indicates that all rows from the left table need to be taken and matched with those on the right-hand side table, i.e. it is similar to a LEFT OUTER JOIN. Similarly, the operator =* is the RIGHT OUTER JOIN.


Running such code on any database with a compatibility level of 90 or higher (SQL Server 2005 or higher)  will result in the following error:


Msg 4147, Level 15, State 1, Line 3
The query uses non-ANSI outer join operators (“*=” or “=*”). To run this query without modification, please set the compatibility level for current database to 80, using the SET COMPATIBILITY_LEVEL option of ALTER DATABASE. It is strongly recommended to rewrite the query using ANSI outer join operators (LEFT OUTER JOIN, RIGHT OUTER JOIN). In the future versions of SQL Server, non-ANSI join operators will not be supported even in backward-compatibility modes.


The solution to this is to use the ANSI standard, SQL-92 syntax of LEFT and RIGHT OUTER JOIN. The query above, can therefore be represented in ANSI standard code as:

USE Northwind
GO
SELECT EmployeeTerritories.EmployeeID,
Territories.TerritoryID,
Territories.TerritoryDescription,
Territories.RegionID
FROM EmployeeTerritories
RIGHT OUTER JOIN Territories ON EmployeeTerritories.TerritoryID = Territories.TerritoryID

If yours is a product that has evolved from the days when Microsoft SQL Server was in it’s infancy, then chances are that fragments of your code still uses such non-ANSI standard code. It would be a good idea to take a couple of days to revisit the code and make it ANSI compliant to avoid further rework.


Until we meet next time,


Be courteous. Drive responsibly.