""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
   COMPARING TEXT VALUES THAT MIGHT BE NULL
   """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
   PRODUCT   :  R:BASE                  VERSION      :  3.1
   CATEGORY  :  NULL VALUES             SUBCATEGORY  :  PROGRAMMING
   """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
   From Bill Downall, Downall Consulting Services, 5411 White Willow
   Court, Indianapolis, IN 46254-9633. Bill is a database consultant and
   educator. You can reach him at 317-297-3810 or through David M.
   Blocker and Associates at 617-784-1919.
 
   Full SQL support in R:BASE 3.x means application developers must be
   careful when comparing values that might be null. The SQL standard
   dictates that nulls can't be compared to anything, not even other
   nulls. This is a change from the way we could compare values in R:BASE
   for DOS and other earlier R:BASE versions.
 
   This article uses eight code segments to show what happens when you
   compare a null value to another value. It gives you guidelines to
   follow when writing programs that deal with nulls. Also, you'll see
   the differences between R:BASE 3.x and R:BASE for DOS when it comes to
   null comparisons.
 
 
   Eight Program Examples
   """"""""""""""""""""""
   The following eight program methods each try to meet the same simple
   need. if a name has a middle initial, format it like this: John Q.
   Doe. Otherwise, format it like this: John Doe.
 
     IF vmi = ' ' THEN                             *( 1 )
       SET VAR vname = (.vfn & .vln)
     ELSE
       SET VAR vname = (.vfn & .vmi + '.' & .vln)
     ENDIF
     IF vmi <> ' ' THEN                            *( 2 )
       SET VAR vname = (.vfn & .vmi + '.' & .vln)
     ELSE
       SET VAR vname = (.vfn & .vln)
     ENDIF
     IF vmi = '-0-' THEN                           *( 3 )
       SET VAR vname = (.vfn & .vln)
     ELSE
       SET VAR vname = (.vfn & .vmi + '.' & .vln)
     ENDIF
     IF vmi <> '-0-' THEN                        *( 4 )
       SET VAR vname = (.vfn & .vmi + '.' & .vln)
     ELSE
       SET VAR vname = (.vfn & .vln)
     ENDIF
     IF vmi IS NULL THEN                  *( 5 )
       SET VAR vname = (.vfn & .vln)
     ELSE
       SET VAR vname = (.vfn & .vmi + '.' & .vln)
     ENDIF
     IF vmi IS NOT NULL THEN            *( 6 )
       SET VAR vname = (.vfn & .vmi + '.' & .vln)
     ELSE
       SET VAR vname = (.vfn & .vln)
     ENDIF
     IF vmi = ' ' OR vmi IS NULL THEN      *( 7 )
       SET VAR vname = (.vfn & .vln)
     ELSE
       SET VAR vname = (.vfn & .vmi + '.' & .vln)
     ENDIF
     IF vmi <> ' ' AND +
       vmi IS NOT NULL THEN            *( 8 )
       SET VAR vname = (.vfn & .vmi + '.' & .vln)
     ELSE
       SET VAR vname = (.vfn & .vln)
     ENDIF
 
 
   Analysis
   """"""""
   Each pair contains logical opposites. For example, the condition
   tested by method two is the opposite of the condition tested by method
   one. But the commands in the IF or ELSE portions of the IF blocks are
   also reversed, so the two programs should get exactly the same result.
 
   Assuming NULL is set to -0-, the conditions tested in one through four
   all work in R:BASE for DOS. But none work correctly all of the time in
   R:BASE 3.x. Methods five through eight use IF conditions that are
   available only in R:BASE 3.x.
 
 
   What Can Go Wrong?
   """"""""""""""""""
   To demonstrate what can go wrong if you aren't careful with the NULL
   setting and your programming, I wrote a routine called MIDINIT.CMD
   (listed on the next page). It imitates all eight methods and allows
   you to control two other important details:
 
     o  The NULL symbol setting.
     o  The data for the middle initial - a character, a space, or a null
        value.
 
   Use MIDINIT.CMD to test various combinations. You'll see that in
   R:BASE 3.x, there's only one sure-fire way to test for null values.
   Set the NULL symbol to -0- and use the IS NULL or IS NOT NULL
   operator.
 
   Before running MIDINIT.CMD, create the following three menu files, and
   place them in your current directory:
 
 
   Listing of SPACNULL.MNU
   """""""""""""""""""""""
     spacnull
     POPUP |Pick Space or Null.|
     |Space||Make middle initial a space.|
     |Null||Make middle initial null.|
     ENDC
 
 
   Listing of NULLCHAR.MNU
   """""""""""""""""""""""
     nullchar
     POPUP | Pick the NULL symbol setting.|
     |Normal: -0-||Set the NULL symbol to -0-.|
     |Space: ' '||Set the NULL symbol to a space.|
     ENDC
 
 
   Listing of WHAT.MNU
   """""""""""""""""""
     what
     POPUP
     |Try another one|
     |Quit|
     ENDC
 
   All the quotation marks in MIDINIT.CMD are single quotation marks.
 
 
   How to Run MIDINIT.CMD
   """"""""""""""""""""""
   To see the demonstration, enter the following at the R> prompt:
 
     RUN midinit.cmd
 
   Below are five example tests you can try with MIDINIT.CMD.
 
 
   NULL -0- & Any Middle Initial
   """""""""""""""""""""""""""""
   Enter "Elwood" for a first name and "Dowd" for a last name. When the
   program asks for a middle initial, enter any letter of the alphabet. I
   entered "P." When prompted to enter a NULL symbol, choose normal, -0-.
 
   Now, you'll see the results of each of the eight programs. Notice the
   strange results for three and four.
 
     3. vmi = '-0-'                                  F    Elwood P. Dowd
     4. vmi <> '-0-'                                 F    Elwood Dowd
 
   The middle initial variable (VMI) contains the letter "P," so it isn't
   null. But you apparently can't use the NULL symbol setting to test for
   this because R:BASE 3.x determined that although P isn't "equal" to '-
   0-', it also isn't "not equal" to '-0-'. Both conditions, which look
   like opposites, are false.
 
 
   NULL -0- & Space Middle Initial
   """""""""""""""""""""""""""""""
   Choose "Try another one." This time enter "Margaret" for the first
   name, "Mead" for the last name, and press [Enter] when the program
   asks for a middle initial. A menu will appear asking you for a middle
   initial; choose SPACE. Then choose the normal null symbol.
 
     3. vmi = '-0-'                                  F    Margaret. Mead
     4. vmi <> '-0-'                                 F    Margaret Mead
     5. vmi IS NULL                                  F    Margaret. Mead
     6. vmi IS NOT NULL                              F    Margaret. Mead
 
   This time, one and two are correct but not three. Look at the code for
   methods three and four. Again both evaluate as false even though they
   are opposites - VMI isn't "equal" to '-0-' and it isn't "not equal" to
   '-0-'. As in the first example, when R:BASE compares a space to a
   quoted text string that looks like the current null character, the
   comparison always evaluates as false unless you use the IS NULL
   condition.
 
   The results from five and six demonstrate another problem. Here the
   names format incorrectly because the programmer didn't consider that a
   data entry operator might press the space bar rather than tab through
   a field. Methods five and six test for a null value only, adding a
   period in all other cases.
 
 
   NULL -0- & Null Middle Initial
   """"""""""""""""""""""""""""""
   This time make the middle initial null. Enter "Glenn" for the first
   name and "Ford" for the last. Press [Enter] when asked for a middle
   initial, choose NULL from the pop-up menu, and then choose the normal
   null symbol (-0-).
 
     1. vmi = ' '                                     F    Glenn. Ford
     2. vmi <> ' '                                    F    Glenn Ford
     3. vmi = '-0-'                                  F    Glenn. Ford
     4. vmi <> '-0-'                                 F    Glenn Ford
 
   The top four are unpredictable. The null value in VMI fails every
   comparison to anything, whether you use the equal sign or the not
   equal (<>) operator. VMI isn't "equal" to a space or -0-, nor is it
   "not equal" to a space or -0-.
 
 
   NULL ' '  & Any Middle Initial
   """"""""""""""""""""""""""""""
   If you set the NULL symbol to a space, matters get worse. Enter any
   name and include a middle initial. Choose SPACE for the NULL symbol.
   With NULL set to a space, you can't compare a letter to a single
   space, so methods two and eight give wrong answers.
 
 
   NULL ' '  & Space Middle Initial
   """"""""""""""""""""""""""""""""
   Next, enter any name and a space for a middle initial. Choose SPACE
   for the NULL symbol. You'll discover that just about every method
   formats the name incorrectly. Method one produces a faulty answer
   because R:BASE can no longer evaluate the expression IF VMI = ' ',
   even though VMI is equal to a single space. Even method seven, which
   carefully tests both for a space and a null, gets the wrong answer.
 
 
   Conclusion & Summary
   """"""""""""""""""""
   It's clear that you should set the NULL symbol to -0- before all
   comparisons that may involve a null. The chart at the bottom of the
   page summarizes the results of all the possible combinations of
   conditions, NULL settings, and variable values.
 
   There are no surprises in the lower left quadrant of the chart. The
   opposite conditions always evaluate with opposite results. But with
   the NULL symbol set to a space, many things can go wrong.
 
   Even with NULL set to -0-, you can no longer compare nulls to quoted
   text that looks like the current null symbol setting because the SQL
   standard won't allow it.
 
 
   Guidelines for Null Comparisons
   """""""""""""""""""""""""""""""
   Here are the guidelines to follow in R:BASE 3.x when comparing TEXT
   columns or variables that may sometimes be null. Be sure to follow the
   first three guidelines together. If you don't follow the first two,
   the third won't give predictable results.
 
     o  Set the NULL symbol to '-0-'.
     o  Test for empty values with the IS NULL or IS NOT NULL operator.
     o  Test for both null and blank values if it's possible that a value
        could sometimes be null and other times be blank. For example,
        this IF command tests for both conditions:
 
        IF vname = ' ' OR vname IS NULL THEN
 
     o  Avoid unexpected null value comparisons by using INDICATOR
        variables to test for null values in DECLARE CURSOR,
        SELECT...INTO, and FETCH commands.
 
 
 
   ZERO Setting & Logical Functions
   """"""""""""""""""""""""""""""""
   The NULL symbol and ZERO settings have no effect on IF condition null
   comparisons with numeric data types. All IF conditions will be false
   if the numeric variable is null unless you use IS NULL or IS NOT NULL.
 
   However, if ZERO is ON, you can compare a null with zero and come up
   with a match inside a logical function (IFEQ, IFLT, or IFGT). This is
   because the logical functions aren't subject to the SQL rules. For
   example, this code works:
 
     SET ZERO ON
     SET VAR vnull INTEGER = NULL
     SET VAR vtorf TEXT=(IFEQ(.vnull, 0, 'T', 'F'))
 
   The variable VTORF will be set to T. But in every other place in
   R:BASE, you must use the IS NULL or IS NOT NULL operator to test for a
   null value, as required by the SQL standard.