Class Union - Program Example
Class Union corresponds to a SQL union statement. It is constructed from two or more objects of Select class. The same rules apply as for creating SQL union statement, namely number of columns and columns types in both select statements must be the identical. Let's take a look at the following SQL statement:
 
                select prod_id, prod_desc
                from products
                where prod_id <= 10004
                union all
                select prod_id, prod_desc
                from products
                where prod_id > 10004
                order by 1, 2;
 
where union all means that identical column set from both tables will be repeated. This is a little bit artificial example (shown only for demonstration purposes), as rows retrieved will be all rows stored in the table products. In reality, a several different tables can be used to make the union. Below is a listing of a corresponding C++ program using ORAC++:
 
// select_union.cc
#include <orac_lib.h>
int main()
{
        Connect c;
        try  {
                Database d = c.OpenDatabase("demo", "demo");
                Table tb = d.GetTable("products");
                Table tc = d.GetTable("products");

                Select sel(tb);                                
                sel << tb["prod_id"] << tb["prod_desc"];
                sel >> tb["prod_id"] <= 100004;
                Select sel1(tc);                                
                sel1 << tc["prod_id"] << tc["prod_desc"];
                sel1 >> tc["prod_id"] > 100004;

                Union u(sel, sel1);
                u >> "order by 1,2";
                u.AsLine(HEADER_OFF);                        

                while(u.Row())
                        cout << u;        
        }
        catch (OException& e)  {
                cerr << e.Show() << endl;
        }
        catch (...)  {
                cerr << "Error ..." << endl;
        }
        return 0;
}
 
Please note, that class Union creates by default 'union all' SQL statement. If we dont want repeated lines
a function SetAll(0) must be called. If called with argument not equal to zero, 'union all' is set again.
Note also, that constructor of class Record and Line can take as argument Union class object and column names are always columns from the first Select in the Union.
 

 

 

 

 

 

 

 

 

 

 
Class Intersect -  Program Example
Class Intersect corresponds to a SQL intersect statement. It is constructed from two objects of Select class. The same rules apply as for creating SQL intersect statement, namely number of columns and columns types in both select statements must be the identical. Let's take a look at the following SQL statement:
 
                select prod_id
                from products
                intersect
                select prod_id
                from deleted_products

  Below is a full listing of corresponding C++ program using ORAC++:
 

#include <orac_lib.h>
int main()
{

        Connect c;
        try  {
                Database d = c.OpenDatabase("demo", "demo");
                Table tb = d.GetTable("products");
                Table tc = d.GetTable("deleted_products");

                Select sel(tb);                                
                sel << tb["prod_id"];
                Select sel1(tc);                                
                sel1 << tc["prod_id"];

                Intersect i(sel, sel1);
                i.AsLine(HEADER_OFF);

                while(i.Row())
                        cout << i;
        }
        catch (OException& e)  {
                cerr << e.Show() << endl;
        }
        catch (...)  {
                cerr << "Error ..." << endl;
        }
        return 0;
}
 
Please note, that constructor of class Record and Line can take as an argument Intersect class object and column  names are always columns from the first Select in the Intersect.
 

 

 

 

 

 

 

 

 

 

 

 
Class Minus - Program Example
Class Minus corresponds to a SQL minus statement. It operates on two objects of Select class. The same rules apply as for creating SQL minus statement, namely number of columns and column types in both select statements must be the identical. Let's take a look at the following SQL statement:
 
                select prod_id
                from products
                minus
                select prod_id
                from deleted_products
 
Below is a complete listing of a corresponding C++ program using ORAC++ (program performs sub-query based on rows fetched by Minus class object).
 
#include <orac_lib.h>
int main()
{
        int prod_id;
        Connect c;
        try  {
                Database d = c.OpenDatabase("demo", "demo");
                Table tb = d.GetTable("products");
                Table tc = d.GetTable("deleted_products");

                Select sel(tb);                                
                sel << tb["prod_id"];
                Select sel1(tc);                                
                sel1 << tc["prod_id"];

                Select sel2(tb);
                sel2 << tb["prod_id"] << tb["prod_desc"];
                sel2 >> tb["prod_id"] == ":";             // insert placeholder to query criteria

                Minus m(sel, sel1);
                m >> "order by 1 desc";
                while(m.Row()) {
                        sel << prod_id;                        // load variable
                        sel2 >> prod_id;                      // substitute for placeholder
                        while (sel2.Row())
                                cout << m;                       // defaults to line
                }
        }
        catch (OException& e)  {
                cerr << e.Show() << endl;
        }
        catch (...)  {
                cerr << "Error ..." << endl;
        }
        return 0;
}
 
Please note that column names are always from the first Select in the Minus. In Union, Intersect and Minus where clause can be applied to each involved Select objects.
 

 

 

 

 

 

 

 

 

 

 

 
Outer Join
Let's take a look at the following SQL statement:
 
        select a.prod_desc, a.prod_price,
                b.ord_id, b.ord_date,
                b.ord_qty, c.type_desc
        from products a,
             orders b,
             product_types c
        where a.prod_id = b.prod_id
        and c.type_id = a.prod_type;
 
The statement creates a join of three tables. Let's assume that we want to list all products, which are not on 'orders' table. To do that, we have to modify above SQL statement as follows:
 
        select a.prod_desc, a.prod_price,
                b.ord_id, b.ord_date,
                b.ord_qty, c.type_desc
        from products a,
             orders b,
             product_types c
        where a.prod_id = b.prod_id(+)                
        and c.type_id = a.prod_type
        and b.prod_id is null;
 
And a corresponding C++ program using ORAC++:
 
#include <orac_lib.h>
int main()
{
        Connect c;
        try  {
                Database d = c.OpenDatabase("demo", "demo");
                Table tb = d.GetTable("products");
                Table tc = d.GetTable("orders");
                Table td = d.GetTable("product_types");

                Select sel(tb);                                
                sel += tc;
                sel += td;                                         // join of three tables

                sel << tb["prod_desc"] << tb["prod_price"]
                      << tc["ord_id"] << tc["ord_date"]
                      << tc["ord_qty"] << td["type_desc"];
 
                sel >> tb["prod_id"] += tc["prod_id"];   //outer join
                sel >> and_ >> td["type_id"] == tb["prod_type"];
                sel >> and_ >> tc["prod_id"] == "null";
                while(sel.Row())
                        cout << sel;                         // defaults to Line
        }
        catch (OException& e)  {
                cerr << e.Show() << endl;
        }
        catch (...)  {
                cerr << "Error ..." << endl;
        }
        return 0;
}
 
Program will create output as follows:
 
PROD_DESC PROD_PRICE     ORD_ID   ORD_DATE    ORD_QTY         TYPE_DESC
 
    PANTS                 19.99         NULL           NULL           NULL             CLOTHING
 
Please note, that in order to create outer-join using ORAC++ a += joining operator must be used. It can be only one += operator per line. Details of use outer join are out of scope of this document.  

 

 

 

 

 

 

 

 

 

 

 

 

 
Use of Hints
An Oracle optimizer goal can be controlled by use of hints. There are many Oracle hints, which can be used in SQL statements. A discussion of the hints is beyond scope of this document. Any valid Oracle hint can be easily used in a C++ program using ORAC++ library. A hint must always be entered as a first argument when building a SQL string. Let's consider a following example:
 
#include <orac_lib.h>
int main()
{
        Connect c;
        try  {
                Database d = c.OpenDatabase("demo", "demo");
                Table tb = d.GetTable("products");
                Table tc = d.GetTable("orders");
                Table td = d.GetTable("product_types");
                Select sel(tb);                                
                sel += tc;
                sel += td;                                 // join of three tables

                sel << "/*+ USE_NL(products orders product_types) */"   // hint
                            << tb["prod_desc"] << tb["prod_price"]
                        << tc["ord_id"] << tc["ord_date"]
                            << tc["ord_qty"] << td["type_desc"];
 
                        sel >> tb["prod_id"] == tc["prod_id"];
                        sel >> and_ >> td["type_id"] == tb["prod_type"];
                        sel >> and_ >> tc["prod_id"] == "null";
                while(sel.Row())
                        cout << sel;                 // defaults to Line
        }
        catch (OException& e) {
                cerr << e.Show() << endl;
        }
        catch (...) {
                cerr << "Error ..." << endl;
        }
        return 0;
}
 
In example above, hint /*+ USE_NL(products orders product_types) */ is a valid Oracle hint.
Hint is used in identical form as in SQL statement for simplicity. Please note that any table name if used within the hint must be a full table name as defined in Table class constructor.
 
As use of hints can significantly improve SQL statement execution and it will give a programmer a full control overriding optimizer defaults.
 

 

 

 

 

 

 

 

 

 

 

 

 

 

 
Accessing Table Embedded Objects

Following programs illustrate how objects embedded in Oracle table can be accessed by ORAC++. Table CUSTOMERS contains two embedded objects NAMES and . Assuming that a Table object is created by following statements:
 
        Database d = c.OpenDatabase("demo", "demo");
        Table t = d.GetTable("customers");
 
there are several types of Select objects, which can be used to query the table:
 
a)      query based solely on table columns (ignoring embedded objects)
 
          Select s(t);
 
b)      query based solely on first or second embedded object (NAMES or ADDRESS)

          Object o = t.GetObject("names"); or               Object o = t.GetObject("address");
          Select s(o);                                                 Select s(o);
 
c)      query based solely on first and second embedded object (NAMES and ADDRESS)

          Object o = t.GetObject("names");
          Object o1 = t.GetObject("address");
          Select s(o);
          s += o1;

        query based on table columns and first or second embedded object

          Object o = t.GetObject("names"); or               Object o = t.GetObject("address");
          Select s(t);                                                 Select s(t);
          s += o;                                                       s += o;
 
e)      query based on table columns and two embedded objects (all table data)

          Object o = t.GetObject("names");
          Object o1 = t.GetObject("address");
          Select s(t);
          s += o;
          s += o1;
 
Although in the examples above all objects have been created by specifying their name,
default method can also be used (without object name). A sequential call to GetObject() will create objects in order as they are stored in the table. To get object name a GetObjName() function must be called. To check if object is of type collection (table within table), an IsColl() function must be called. Following, is a complete C++ program example to illustrate this point:
 
#include <orac_lib.h>
int main()
{
        int i;
        Object *o;
        Connect c;
        try  {
                Database d = c.OpenDatabase("demo", "demo");
                Table t = d.GetTable("customers1");
                Select s(t);                                                         // query based on table
                for(i=0; i<t.GetNumObjects(); i++)  {                       // get number of objects
                        o = t.GetAObject();                                     // get object in sequence
                        cout << o->GetObjName() << endl;              // show object name
                        if (o->GetType() == EMBEDDED)            // check if embedded
                                s += o;                                               // add to query
                }
                while (s.Row())            
                        cout << s;                                                  // line display by default
                d.Close();
        }
        catch (OException& e)  {
                cerr << e.Show() << endl;
        }
        catch (...)  {
                cerr << "Error..." << endl;
        }
        return 0;
}
 
Let's now take a look at the following program:
 
#include <orac_lib.h>
int main()
{
        Connect c;
        try  {
                Database d = c.OpenDatabase("demo", "demo");
                Table t = d.GetTable("customers");
                Object o = t.GetObject("address");         // get object by name        
                Select sel(o);                                          // Select based on Object
                while (sel.Row())
                        cout << sel;                                    // defaults to Line
        }
        catch (OException& e) {
                cerr << e.Show() << endl;
        }
        catch (...) {
                cerr << "Error ..." << endl;
        }
        return 0;
}
 
In the example above, Select class object is constructed from Object class. In this case, default Line columns are only columns defined within the Object.  Columns which belong to a Table are not visible.
 
Finally, let's consider a program:
 
#include <orac_lib.h>
int main()
{
        Connect c;
        try {
                Database d = c.OpenDatabase("demo", "demo");
                Table t = d.GetTable("customers");
                Object o = t.GetObject();             // get first object (names)
                Select sel(t);                                // Select based on Table
                sel += o;                                     // add Object to Select (1)
                while (sel.Row())
                cout << sel;                         // defaults to line
        }
        catch (OException& e)  {
                cerr << e.Show() << endl;
        }
        catch (...)  {
                cerr << "Error ..." << endl;
        }
        return 0;
}
 
In latest example, Select class object is constructed from Table object. In this case, default Line columns are only columns defined within the Table. Columns which belong to an Object are not visible. To make them visible, Object must be added to Select (1).
         
Please note, that objects can be retrieved from table either in sequence by calling function
GetObject() with no arguments, or retrieved by name by calling GetObject(table_column_object_name).
If table contains several objects, object can be retrieved be either of two methods.
 
In addition to query, insert and update operations can be performed on table with embedded objects. Method of inserting and updating objects is similar to those used in case of select.
First, object must be extracted from the table:
 
        Database d = c.OpenDatabase("demo", "demo");
        Table t = d.GetTable("customers");
        Object o = t.GetObject("cust_address");                // get embedded object by name
 
Next, Insert or Update object must be constructed based on Table:
 
        Insert i(t);                or                 Update u(t);
 
After that, object must be added to Insert or Update (so object columns are visible):
 
        i += o;                        or                u += o;
 
Finally insert or update data must be entered:
 
        i >> 100 >> "Lawsons" >> "George" >> "Taraga Street" >> "Culvers";
        or
        u << o["street"] << o["city"];                                // update cust_address(street,city)
        u >> "Ruther Street" >> "Beckarton";                   // enter update data
        u >> t["cust_id"] == 100;                                    // where cust_id = 100
 
Note, that object type must be checked by calling function IsColl() after each embedded object is created. Only objects which are not a collection can be used in insert in update using above method.
 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 
Accessing Table of Oracle Objects
Table CUST  is a table of objects (CUSTOMER_OBJECT) created as a type (there are no table columns), all object columns became table columns, and can be accessed in the regular way:
 
#include <orac_lib.h>
int main()
{
        Connect c;
        try  {
                Database d = c.OpenDatabase("demo", "demo");
                Table t = d.GetTable("cust");
                Select sel(t);
                sel.AsRecord();
                while (sel.Row())
                        cout << sel;
        }
        catch (OException& e)  {
                cerr << e.Show() << endl;
        }
        catch (...)  {
                cerr << "Error ..." << endl;
        }
        return 0;
}
 
When Oracle table has one or more columns of named type, in order to access named type columns, Object (named type) must be first retrieved from the table. Table (CUST1) used in program below has only one column (of named type), therefore to access named type columns Object must be retrieved from the Table, and Select must be based on this Object.
 
#include <orac_lib.h>
int main()
{
        Connect c;
        try  {
                Database* d = c.OpenADatabase("demo", "demo");
                Table* t = d->GetATable("cust1");         
                Object* o = t->GetAObject();             // get next embedded object in sequence
                Select sel(o);                                    // select based on object
                sel.AsRecord();                               // all objects columns are visible
                while (sel.Row())
                        cout << sel;
        }
        catch (OException& e) {
                cerr << e.Show() << endl;
        }
        catch (...) {
                cerr << "Error ..." << endl;
        }
        return 0;
}
 
Another method of retrieving data from the object is to use object column names. Next program will illustrate this point. Select class is based on object.
 
#include <orac_lib.h>
int main()
{
        Connect c;
        try  {
                Database* d = c.OpenADatabase("demo", "demo");
                Table* t = d->GetATable("cust1");
                Object* o = t->GetAObject();                        // get first available object
                if (o->GetType() == EMBEDDED) {             // check if not nested table
                        Select sel(o);
                        sel << o["id"] << o["last_name"];            // get columns from the Object
                        sel.AsRecord();                                     // use Record format
                        while (sel.Row())
                                cout << sel;
                }
        }
        catch (OException& e)  {
                cerr << e.Show() << endl;
        }
        catch (...)  {
                cerr << "Error ..." << endl;
        }
        return 0;
}
 

 

 

 

 

 

 

 

 

 

 

 

 

 
Accessing Nested Table
Following programs demonstrate accessing of Oracle nested tables. Table SCHEDULES  contains a nested table PERFOMERS. The way program access nested table is similar to handling sub-queries. There are two Select class objects created: first for the main table, second for the object embedded in nested table. Nested table is queried within the loop, with unique primary key used in where clause ( Select s(o)).
 
#include <orac_lib.h>
int main()
{
        int evt, act;
        char name[50], desc[50];
        Connect c;
        try {
                Database d = c.OpenDatabase("demo", "demo");
                Table t = d.GetTable("schedules");
                Object o = t.GetObject("evt_lineup");        // use column name of embedded table

                Select sel(t);                                              // first Select based on Table
                sel << t["evt_id"];
                Select s(o);                                               // second Select based on Object                
                s << o["act_no"] << o["act_name"] << o["act_desc"];
                s >> o["evt_id"] == ":";                              // placeholder in query criteria
                while (sel.Row()) {
                        sel << evt;                                        // load first fetched column into variable
                        cout << "evt = " << evt << endl;
                        s >> evt;                                           // substitute data for placeholder
                        while (s.Row())
                                s << act << name << desc;
                                cout << " ------ " << act << ":"
                                             << name << ":" << desc << endl;
                        }
                }
        }
        catch (OException& e)  {
                cerr << e.Show() << endl;
        }
        catch (...)  {
                cerr << "Error ..." << endl;
        }
        return 0;
}
 
Following program is a variation of preceding one. Difference is that selected columns are not specified, so all columns defined in the main and nested tables are fetched accordingly. Only column which needs to be loaded separately, is key (evt) used to query nested table (EVT_ID) in where clause (op. >>).
 
#include <orac_lib.h>
int main()
{
        int evt;
        Connect c;
        try  { // use pointer wrapper class Ptr
                Ptr<Database> d (c.OpenADatabase("demo", "demo"));
                Ptr<Table> t (d.GetATable("schedules"));
                Ptr<Object> o (t.GetAObject());

                Select sel(t);                              // two Select objects
                Select s(o);
                s >> o["evt_id"] == ":";               // placeholder in query clause
                s.AsLine(HEADER_OFF);

                while (sel.Row())  {
                        cout << sel;                       // fetched data is displayed in Line format
                        sel << evt;                         // get value for placeholder
                        s >> evt;                           // and substitute
                        while (s.Row())
                                cout << s;                 // display data in Line format (no column names)
                        cout << endl;
                }
        }
        catch (OException& e) {
                cerr << e.Show() << endl;
        }
        catch (...)  {
                cerr << "Error ..." << endl;
        }
        return 0;
}

Now, let's take look at the program to fetch specific record from the nested table:

#include <orac_lib.h>
int main()
{
        int act;
        char name[30], desc[30];
        Connect c;
        try  {
                Database d = c.OpenDatabase("demo", "demo");
                Table t = d.GetTable("schedules");
                Object o = t.GetObject();

                Select s(o);
                s >> o["evt_id"] == 10;               // query table
                s >> nested_ >> o["act_no"] == 1; // query nested table
                while (s.Row())  {
                        sel << act << name << desc;                     
                         cout << act << name << desc << endl;
                }
        }
        catch (OException& e) {
                cerr << e.Show() << endl;
        }
        catch (...)  {
                cerr << "Error ..." << endl;
        }
        return 0;
}

Following, are programs to insert, update and delete records from nested table (only code within 'try' block is shown):

First, get object from the table (common code for insert, update and delete):

                Database d = c.OpenDatabase("demo", "demo");
                Table t = d.GetTable("schedules");
                Object o = t.GetObject("evt_lineup");

Program code to insert records into nested table:

                Insert ins(t);
                ins << t["evt_id"] << t["evt_name"] << t["evt_location"] << t["evt_lineup"];  
                ins >> 30 >> "screening" >> "Los Angeles"; // three data items
                cout << s.Row() << " rows inserted" << endl;

                Insert i(o);
                i << o["act_no"] << o["act_name"] << o["act_desc"];                     
                i >> 1 >> "John Resda" >> "actor";
                cout << i.Row() << " rows inserted - nested table" << endl;

                i >> 2 >> "Mary Hantan" >> "actor";
                cout << i.Row() << " rows inserted - nested table" << endl;

Program code to update records in nested table:

                Update u(o);
                u << o["act_name"] << o["act_desc"];                     
                u >> "Jerry Tantext" >> "support";
                u >> t["evt_id"] == 30; // specify query criteria
                u >> nested_ >> o["act_no"] == 1;

                cout << u.Row() << " rows updated - nested table" << endl;


 Program code to delete records from nested table:


                Delete del(o);
                del >> t["evt_id"] == 30; // specify query criteria
                del >> nested_ >> o["act_no"] == 1;
                cout << del.Row() << " rows deleted - nested table" << endl;
 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 
 Using In/NotIn Functions in Query

Use of operator 'in/not in' in Oracle select statement can be presented by following examples:

        select prod_id, prod_desc
        from products
        where prod_id in (100001,100003,100004);

        select prod_id, prod_desc
        from products,
        where prod_id in (
        select prod_id
        from orders
        where ord_id > 40);

In the first example a list of values is used, in the second a sub-query is created. Corresponding C++ programs are shown below:

#include <orac_lib.h>
int main()
{
        Connect c;
        try {
                Database d = c.OpenDatabase("demo", "demo");
                Table t = d.GetTable("products");

                Select sel(t);
                sel << t["prod_id"] << t["prod_desc"];
                sel >> t["prod_id"] >> In("100001,100003,100004");  
                while (sel.Row())
                        cout << sel;                            
        }
        catch (OException& e)  {
                cerr << e.Show() << endl;
        }
        catch (...)  {
                cerr << "Error ..." << endl;
        }
        return 0;
}

#include <orac_lib.h>
int main()
{
        Connect c;
        try {
                Database d = c.OpenDatabase("demo", "demo");
                Table t1 = d.GetTable("products");
                Table t2 = d.GetTable("orders");

                Select s1(t1); // create two Select objects
                Select s2(t2);
                               
                s2 << t2["prod_id"];
                s2 >> t2["ord_qty"] > 40;
                s1 >> t1["prod_id"] >> In(s2);  
                while (s1.Row()) // execute first Select
                        cout << s1;                            
        }
        catch (OException& e)  {
                cerr << e.Show() << endl;
        }
        catch (...)  {
                cerr << "Error ..." << endl;
        }
        return 0;
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 
Using Exists/NotExists Functions in Query
                    
Use of operator 'exists/not exists' in Oracle select statement can be presented by following examples:

        select *
        from products a
        where exists (
         select *
        from orders b
        where  a.prod_id = b.prod_id );

Corresponding C++ program using ORAC++ is shown below:

#include <orac_lib.h>
int main()
{
        Connect c;
        try {
                Database d = c.OpenDatabase("demo", "demo");
                Table t1 = d.GetTable("products");
                Table t2= d.GetTable("orders");

                Select sel(t1);                                              
                sel >> sel.Exists(t2)
                >> t1["prod_id"] == t2["prod_id"];
                while (sel.Row())
                        cout << sel;                            
        }
        catch (OException& e)  {
                cerr << e.Show() << endl;
        }
        catch (...)  {
                cerr << "Error ..." << endl;
        }
        return 0;
}

Placeholders can be used with 'In/NotInd/Exists/NotExists' functions.
Limitation is, that all placeholders must be inserted either before or after function.