|
YOUR FEEDBACK
Did you read today's front page stories & breaking news?
SYS-CON.TV SYS-CON.TV WEBCASTS |
TOP COLDFUSION LINKS Tips & Tricks Writing Queries for Oracle vs SQL Server
Utilize the database in CF apps
By: I-Lin Kuo
Feb. 25, 2004 12:00 AM
Have you ever written an application using one database only to have it break when you switched to a different database? How often have you decided to limit the functionality of your database by writing only basic SQL because you just weren't sure if your queries would work on a different database? Or maybe you've written ColdFusion code to manipulate query results without realizing that the database could do the same manipulation quicker and more efficiently. In general, the database tends to be seriously underutilized in most applications. This article aims to provide a quick overview of some important SQL differences between the two most popular commercial databases - Oracle9i and SQL Server 2000 - to help the CF developer write more portable and database-smart applications. Sequences and Identity Columns SQL Server: Another difference is that SQL Server allows you to conveniently retrieve the auto-generated ID from the insert via the following code: SQL Server: Warning: there are variants of the above code that retrieve the ID using @@IDENTITY rather than SCOPE_IDENTITY. @@IDENTITY is not as good as SCOPE_IDENTITY because it will give an incorrect result if you have a trigger on the table that inserts into another table. In Oracle, because you're managing the sequence explicitly, you would retrieve the sequence value before you do the insertion rather than retrieving the value after insertion. Oracle: However, these advantages of the SQL Server approach to sequential primary keys come with a price - you cannot insert a value into an IDENTITY column. Thus, if you try to bulk import rows from another table into a table with an IDENTITY column, you will have to omit the original column corresponding to the IDENTITY column. This becomes especially painful to manage properly if the omitted column is the foreign key value by which other tables reference your original table. For this reason, despite its ease of use, it is often a good idea to avoid the use of the IDENTITY column in SQL Server. Derived Tables and Top N Queries To illustrate our next examples, we'll be using the small Employees table shown in Table 1. The SQL to create the table is located at the end of the article. Using the sample employees table as an example, I would run the following queries in SQL Server to retrieve the names and salaries of the three highest-paid employees: SQL Server: In Oracle, it's slightly more complicated. We have to make use of a derived table to sort the results first (a derived table is a select query that is used in place of a table). Then we use the ROWNUM pseudocolumn to select the first 3 results (a psuedocolumn is a column that doesn't actually exist in the table that nonetheless has a value that can be used in a query). Oracle: Note that since I want the three highest salaries, I have to order my results in descending order of salary. The ROWNUM condition on the outer query restricts the results to the first three. Name salary For the sake of consistency, the ORDER BY clauses in top N queries are not optional. Without an ORDER BY clause, the database is free to return results in any order it chooses, so the top N may be very different from one minute to the next depending on the internal state of the database, even if the data itself does not change. Derived Tables and Results by Pages In SQL Server, to retrieve the first N through M results, we must select the top M results in a derived table, reverse the order, and then select the top M-N+1 results of the reversed derived table. As an example, the following query selects the third through seventh highest paid employees. SQL Server: Now, how would we get the same results with Oracle? You might think that by modifying the previous Oracle query that the following would work: SELECT name, salary FROM ( Unfortunately, there are some oddities with the use of Oracle's ROWNUM pseudocolumn that cause the above query to return zero results. The query that actually works requires the use of a derived table within a derived table: Oracle:
![]() Note: it is very important that the ROWNUM in the derived table is given an alias, otherwise the query will return zero results. Both queries return:
NULL behavior is an extremely tricky issue. To the average CF developer, NULLs are often dealt with unobtrusively - a column with a value of NULL returns an empty string. For example, the following CF code results in "Shaya likes []". <cfquery name="myQuery"> Things get trickier when there are numeric expressions involving NULL. ANSI standards dictate that NULL should propagate during a calculation. In layman's terms, this means that any expression involving a NULL should evaluate to a NULL, thus 8+NULL and cos(NULL) should both evaluate to a NULL, which to CF looks like an empty string. Both SQL Server and Oracle are compliant in this respect. However, with string expressions involving NULL, we see noncompliant behavior from Oracle. According to ANSI standards, concatenating a string to a NULL should also produce a NULL. In Oracle, a NULL will automatically be converted to a zero-length string for concatenation. For SQL Server, the default ANSI-compliant behavior can be overridden by disabling the concat null yields null setting of sp_dboption. Oracle: Furthermore, Oracle states that this noncompliant behavior may change in future versions. Nonetheless, in my opinion, this noncompliant behavior is more user-friendly. Fortunately, though, there is a uniform way to be compliant as well as user-friendly in both databases using the COALESCE() function. The COALESCE() function takes a string of arguments and returns the first non-NULL argument. Thus, COALESCE(column_name,'') returns the column value if it is not NULL, and returns the second argument, the empty string, if the column is NULL. Rewriting the above queries as Oracle: returns the same result "Shaya likes []" in Oracle and SQL Server regardless of future versions or of the setting of CONCAT_NULL_YIELDS_NULL. However, there's one more wrinkle to Oracle's noncompliance. In Oracle's own words, "Oracle currently treats a character value with a length of zero as null. However, this may not continue to be true in future releases, and Oracle recommends that you do not treat empty strings the same as nulls." Consequently, when a row is inserted or updated with an empty string, what actually gets stored in Oracle is a NULL. By ANSI standards, however, a NULL and an empty string should be distinct objects. In our Employees table example this means that the statement INSERT INTO Employees(ID,Name,Salary,reportsTo,hobbies) actually inserts a NULL into the hobbies column for Jean rather than an empty string. As a result, the following two selects SELECT count(*) FROM Employees WHERE hobbies IS NULL; will return 1 and 1 in SQL Server but 2 and 0 in Oracle, respectively. Regrettably, this is one of those issues for which there is no satisfactory workaround, and it's just something to be aware of when developing for different database platforms. CASE and Relabeling Results SELECT name, salary, returns
Another question that comes up in the ColdFusion mail list is: How do I return the results in an order that I define, which is neither alphabetical nor numeric? We'll use our Employees example to illustrate how this can be done. Suppose I want a list of all the employees ordered alphabetically, but I always want George to appear first. How do I accomplish that? I can do this by creating an ORDER BY attribute using CASE, which always places George first, as in the following query: SELECT name FROM Employees which returns NAME Calculating Department Subtotals in a Single Query It's fairly easy to retrieve a list of department subtotals. The query SELECT dept, SUM(salary) FROM Employees GROUP BY dept retrieves the subtotals Finance 220000 However, we need to combine these results with the Employees table. This can be done with the UNION keyword, which simply combines the results of two queries. However, the use of UNION requires that the two queries match up exactly in column type and column order. Since we are retrieving name, salary, and department from Employees, we need to add an extra column to our subtotals query. SELECT name, dept, salary FROM Employees The result of this query
SELECT name, dept, salary, '1' AS order_helper returning in the desired order
This final tip isn't about SQL difference, but rather a tip about SQL development in general. I strongly recommend as a best practice that, as much as possible, all SQL be removed from ColdFusion code and placed in stored procedures. There are two very good reasons for this, neither of which involve performance:
While Oracle and SQL Server have greatly improved their compliance with ANSI standards, true universal SQL portability remains elusive. This has put off many developers from using more advanced SQL due to compatibility concerns. Nonetheless, SQL is designed to manipulate query results and does so in a far more elegant and efficient manner than ColdFusion. Thus, it's best to retrieve the exact desired results from the database via correct SQL than to manipulate the results of a simple query via ColdFusion. Hopefully, this article has demonstrated some of the ways in which more portable SQL can increase the utilization of the database in CF applications. Employees Table creation script: Resources CFDJ LATEST STORIES . . .
SUBSCRIBE TO THE WORLD'S MOST POWERFUL NEWSLETTERS SUBSCRIBE TO OUR RSS FEEDS & GET YOUR SYS-CON NEWS LIVE!
|
SYS-CON FEATURED WHITEPAPERS MOST READ THIS WEEK |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||