HomeStreek ResourcesClient AppsDeveloper ResourcesTalksHowTo Docs
 

How to Order a General Catalog Query

Courses in the Berkeley General are identified by department, course number, an optional prefix, and two optional suffixes. We explain how to formulate an Oracle SQL query to produce a summary report in the order preferred by the Academic Senate.

Intended Audience

This document is intended for SQL writers that need to understand the sql technique for ordering a query when one or more of the columns involved is nullable.

Sorting Nullable Columns in Oracle SQL

The preferred order of a summary report of classes in the catalog is:

  1. Course number only
  2. Course number with suffix
  3. Course number with prefix
  4. Course number with second suffix

1. First version

At first glance, the following query would produce the order above:

      SELECT
        dept_cd, crse_num, crse_suffix, crse_prefix, crse_suffix2
      FROM
        sisgco.catlg_crse_info_tb
      WHERE
       dept_cd = 'MATH'
      ORDER BY
        crse_num, crse_suffix, crse_prefix, crse_suffix2
     

Because the prefix and suffix columns are nullable, and null values sort last, the result is not quite what we want:

DEPT_CD CRSE_NUM CRSE_SUFFIX CRSE_PREFIX CRSE_SUFFIX2
MATH 001 A null null
MATH 001 B H null
MATH 001 B null null
MATH 016 A null null
MATH 016 B null null
MATH 024 null null null
MATH 032 null null null
MATH 039 null null null
MATH 049 null null null
MATH 053 M null null
MATH 053 null H null
MATH 053 null null null
MATH 054 M null null
MATH 054 null H null
MATH 054 null null null

2. Correct version

The solution lies in the SQL built-in function DECODE, which allows case-like expressions to be included in a query. Here we are simply replacing null values with ascii space so that lexical sort is correct. Because the course number column is a zero-padded VARCHAR, we use the TO_NUMBER function to strip leading zeros and obtain a numeric rather than lexical sort, and use the concatenation operator to present the course name:

 
      SELECT 
        dept_cd || ' ' || crse_prefix || TO_NUMBER(crse_num) ||
          crse_suffix || crse_suffix2 as course,
        TO_NUMBER(crse_num) as cat_num,
        DECODE(crse_suffix, null, ' ', crse_suffix) as s0,
        DECODE(crse_prefix, null, ' ', crse_prefix) as p,
        DECODE(crse_suffix2, null, ' ', crse_suffix2) as s1
      FROM
        sisgco.catlg_crse_info_tb@SISGCO_SS1P
      WHERE
        dept_cd = 'MATH'
      ORDER BY cat_num, s0, p, s1
      

Our new result is presented in a user-friendly format, and almost correctly ordered:

COURSE CAT_NUM S0 P S1
MATH 1A 1 A
MATH 1B 1 B
MATH H1B 1 B H
MATH 16A 16 A
MATH 16B 16 B
MATH 24 24
MATH 32 32
MATH 39 39
MATH 49 49
MATH 53 53
MATH H53 53 H
MATH 53M 53 M
MATH 54 54
MATH H54 54 H
MATH 54M 54 M

Why "almost?" The order of the Math 53 sequence is incorrect, because the prefixed course appears before the suffixed course. Since courses with a suffix but no prefix should appear before those with a prefix, we need to sort by prefix first. So we change the ORDER BY clause from:

      ORDER BY cat_num, s0, p, s1
     

to

      ORDER BY cat_num, p, s0, s1
     

Our new result is in the desired order:

COURSE CAT_NUM S0 P S1
MATH 1A 1 A
MATH 1B 1 B
MATH H1B 1 B H
MATH 16A 16 A
MATH 16B 16 B
MATH 24 24
MATH 32 32
MATH 39 39
MATH 49 49
MATH 53 53
MATH 53M 53 M
MATH H53 53 H
MATH 54 54
MATH 54M 54 M
MATH H54 54 H

Note

Note that this technique is not available in PL/SQL. Steve Feurstein offers a solution at oreview.com. That article links to a general discussion of the use of DECODE. Transact-SQL (Sybase, SQL Server) users can find similar techniques in Optimizing Transact-SQL : Advanced Programming Techniques by David Rozenshtein, Anatoly Abramovich, Eugene Birger, which is unfortunately out of print but definitely worth searching for.