Column
SELECTions
No Need to Get ANSI about SQL 99 Join Syntax
If you're working in Oracle 9i, make sure you take advantage of this improvement.
by Damir Bersinic
6/5/2002 -- Someone once said that when it comes to change, you either need to grab on to it or fight it -- in either case, it will be a bumpy ride. Luckily, this is not always true in our industry, but change is a constant.
Generally, I have found change to be a good thing and usually am one to embrace it and see where it can take me. Some of my clients and students, on the other hand, aren't too crazy about it. Granted there is something to be said for the "If it ain't broke don't fix it" mentality, but what if it was broke to begin with? In this last category is where I believe SQL join syntax falls -- it was already broken and needed fixing. Oracle 9i finally allows the developer to make use of ANSI SQL 1999 join syntax when it comes to joins in a SELECT statement. While the standard was adopted some time ago and already embraced by other vendors' products, I suspect that Oracle wanted to be sure that there wouldn't be any changes to the standard before adopting it (plus, this gave them more time to make sure their code worked).
So, why the big hullabaloo about SQL 99 join syntax? Simple, it 's a standard! Prior to the SQL 99 standard, there was no commonality in the way that anything other than a simple equijoin or a cross join (i.e., Cartesian product) was implemented across all vendors' product lines. The only thing you could be sure of was that these two SELECT statements would work in Oracle, DB2, Sybase, Microsoft SQL Server, etc.:
SELECT CustomerID, Orders.OrderNum,
ProductId, ProductName, Qty, Price
FROM Orders, OrderDetails
WHERE Orders.OrderNum = OrderDetails.OrderNum
SELECT * FROM Customers, Orders
But what if you wanted to see all customers along with the orders they placed, even if they placed no orders, (i.e., a simple outer join)? For this, you had no commonality between relational database products. For example, the query would be written as follows in Oracle:
SELECT Customers.CustomerId, Company, OrderID
FROM Customers C, Orders O
WHERE C.CustomerID = O.CustomerID (+)
However, the same query would be written like this in Microsoft SQL Server or Sybase:
SELECT Customers.CustomerId, Company, OrderID
FROM Customers C, Orders O
WHERE C.CustomerID *= O.CustomerID
Now, thanks to ANSI SQL 99 syntax, the same query can be re-written to work in both Oracle and SQL Server (as well as DB2 and others) like so:
SELECT Customers.CustomerId, Company, OrderID
FROM Customers C LEFT OUTER JOIN Orders O
ON (C.CustomerID = O.CustomerID)
ANSI SQL 99 join syntax in general also provides an additional benefit: readability. Separating the join condition from the WHERE clause makes it really easy to determine what the elements of the join are versus what conditions have been applied to limit the data returned -- even for complex SQL statements. Take this example using classic Oracle SQL syntax and new SQL 99 syntax:
SELECT c.CourseName, s.StartDate,
i.FirstName || ' ' ||i.LastName as Instructor, l.City
FROM ScheduledClasses s, Instructors i, Courses c, Locations l
WHERE s.InstructorID = i.InstructorID
AND s.CourseNumber = c.CourseNumber
AND s.LocationId = l.LocationID
AND l.Country = ‘USA'
SELECT c.CourseName, s.StartDate,
i.FirstName || ' ' ||i.LastName as Instructor, l.City
FROM Instructors i
JOIN ScheduledClasses s ON (i.InstructorID = s.InstructorID)
JOIN Courses c, ON (s.CourseNumber = c.CourseNumber)
JOIN Locations l ON (s.LocationId = l.LocationID)
WHERE l.Country = ‘USA'
In the second code example, using SQL 99 syntax, the join elements are clearly identified and separate from the WHERE clause. This may not seem like a big deal, but I've written some SQL statements that went on and on, and even had join conditions among selectivity criteria that were hard to figure out two weeks later -- even with comments! Being able to separate join criteria from selectivity criteria is a useful thing. Now, if you're still not convinced, here are a few more reasons to use ANSI join syntax in your code instead of the classic Oracle syntax:
1) You don't need to specify the join condition in a natural join -- it is automatically built by the RDBMS. All you need to write is:
SELECT Customers.CustomerId, Company, OrderID
FROM Customers NATURAL JOIN Orders
Oracle will take that statement and determine what the common columns are in both tables (by matching column names) and create a join condition using all common columns on the fly and then return the result set. This is nice, but I would not recommend it. What if you add a column to either table with a name that exists in the other? In this case, Oracle will join all columns whose names match and make them part of the join condition -- this can be somewhat unpredictable. File this one under "nice to have but no thanks" and always use the ON clause of the join syntax. I should mention that the NATURAL JOIN does allow you to specify a USING clause that tells Oracle which column to use for the NATURAL JOIN, but if you are going to do that, why not just use the ON clause? Following is an example of the USING clause (notice that you cannot qualify the column since it must exist in both tables making up the join):
SELECT Customers.CustomerId, Company, OrderID
FROM Customers NATURAL JOIN Orders USING (CustomerID)
2) You can do more with ANSI SQL 99 join syntax than with classic Oracle syntax. For example, in ANSI syntax you can have a LEFT OUTER JOIN, RIGHT OUTER JOIN or FULL OUTER JOIN, which retrieves rows from both sides of the join condition, whether or not they match. You cannot do a FULL OUTER JOIN in classic Oracle syntax because this statement would return an error:
SELECT Customers.CustomerId, Company, OrderID
FROM Customers C, Orders O
WHERE C.CustomerID (+) = O.CustomerID (+)
However, this is perfectly fine:
SELECT Customers.CustomerId, Company, OrderID
FROM Customers C FULL OUTER JOIN Orders O
ON (C.CustomerID = O.CustomerID)
Now, it's not that you can't do a FULL OUTER JOIN in classic Oracle syntax, but it is a bit more work and requires the UNION operator, as seen here:
SELECT Customers.CustomerId, Company, OrderID
FROM Customers C, Orders O
WHERE C.CustomerID (+) = O.CustomerID
UNION
SELECT Customers.CustomerId, Company, OrderID
FROM Customers C, Orders O
WHERE C.CustomerID = O.CustomerID (+)
3) There is no performance penalty. The use of ANSI SQL 99 syntax does not slow your database down (or speed it up).
4) In the long run you will see more of it, and less of the older syntax. Getting comfortable with it now is a good thing.
5) It's portable. I know I mentioned this one already, but if you work with more than one database or develop applications that make use of more than one database, it's a big time saver).
6) It's easier to understand what is happening (or did I mention that already?).
So join me in creating portable SQL joins that are easy to read, do more than you can with Oracle syntax, and don't slow you down.
Damir Bersinic is an Infrastructure Consultant with Trecata Corporation, a systems integration consultancy in Toronto, Canada. He has more than 20 years of industry experience and has written a number of books on Oracle, Windows, SQL Server and Active Directory. His most recent book is "Portable DBA: SQL Server" from Osborne McGraw-Hill. He is also an Oracle Certified Professional (DBA), MCSE, MCDBA and MCT. He can be reached by e-mail at .
|