OCA/OCP Oracle Database 11g All-in-One Exam Guide 386 2. The first query needs to identify the REGION_ID of the Europe region. This is accomplished by the SQL statement, which shows that the Europe region has a REGION_ID value of 1. select * from regions; 3. To identify which countries have 1 as their REGION_ID, you can execute the SQL query select region_id, country_name from countries; 4. Manually counting the country rows with a REGION_ID of 1 returned shows that there are eight countries in the Europe region as far as the HR data model is concerned. SQL Expressions and Operators The general form of the SELECT statement introduced the notion that columns and expressions are selectable. An expression usually consists of an operation being performed on one or more column values or expressions. The operators that can act upon values to form an expression depend on the underlying data type. They are the four cardinal arithmetic operators (addition, subtraction, multiplication, and division) for numeric columns; the concatenation operator for character or string columns; and the addition and subtraction operators for date and timestamp columns. As in regular arithmetic, there is a predefined order of evaluation (operator precedence) when more than one operator occurs in an expression. Round brackets have the highest precedence. Division and multiplication operations are next in the hierarchy and are evaluated before addition and subtraction, which have lowest precedence. Operators with the same level of precedence are evaluated from left to right. Round brackets may therefore be used to enforce nondefault operator precedence. Using brackets generously when constructing complex expressions is good practice and is encouraged. It leads to readable code that is less prone to error. Expressions expose a large number of useful data manipulation possibilities. Arithmetic Operators Consider the JOB_HISTORY table, which stores the start date and end date of an employee’s term in a previous job role. It may be useful for tax or pension purposes to calculate how long an employee worked in that role. This information can be obtained using an arithmetic expression. Several elements of both the SQL statement and the results returned from Figure 9-5 warrant further discussion. The SELECT clause specifies five elements. The first four are regular columns of the JOB_HISTORY table, while the latter provides the source information required to calculate the number of days that an employee filled a particular position. Consider employee number 176 on the ninth row of output. This employee started as a Sales Manager on January 1, 1999, and ended employment on December 31, 1999. Therefore, this employee worked for exactly one year, which, in 1999, consisted of 365 days. Chapter 9: Retrieving, Restricting, and Sorting Data Using SQL 387 PART II The number of days for which an employee was employed can be calculated by using the fifth element in the SELECT clause, which is an expression. This expression demonstrates that arithmetic performed on columns containing date information returns numeric values that represent a certain number of days. To enforce operator precedence of the subtraction operation, the subexpression end_date-start_date is enclosed in round brackets. Adding 1 makes the result inclusive of the final day. TIP As you practice SQL on your test database environment, you may encounter two infamous Oracle errors: “ORA-00923: FROM keyword not found where expected” and “ORA-00942: table or view does not exist.” These usually indicate spelling or punctuation errors, such as missing enclosing quotes around character literals. Expression and Column Aliasing Figure 9-5 introduced a new concept called column aliasing. Notice that the expression has a meaningful heading named Days Employed. This heading is an alias. An alias is Figure 9-5 Arithmetic expression to calculate number of days worked OCA/OCP Oracle Database 11g All-in-One Exam Guide 388 an alternate name for a column or an expression. If this expression did not make use of an alias, the column heading would be (END_DATE-START_DATE)+1, which is not very user friendly. Aliases are especially useful with expressions or calculations and may be implemented in several ways. There are a few rules governing the use of column aliases in SELECT statements. The alias “Days Employed” in Figure 9-5 was specified by leaving a space and entering the alias in double quotation marks. These quotation marks are necessary for two reasons. First, this alias is made up of more than one word. Second, case preservation of an alias is only possible if the alias is double quoted. If a multiworded space-separated alias is specified, an “ORA-00923: FROM keyword not found where expected” error is returned if it is not double quoted. SQL offers a more formalized way of inserting aliases by inserting the AS keyword between the column or expression and the alias as shown in the first line of this query: SELECT EMPLOYEE_ID AS "Employee ID", JOB_ID AS "Occupation", START_DATE, END_DATE, (END_DATE-START_DATE)+1 "Days Employed" FROM JOB_HISTORY; Character and String Concatenation Operator The double pipe symbols || represent the character concatenation operator. This operator is used to join character expressions or columns together to create a larger character expression. Columns of a table may be linked to each other or to strings of literal characters to create one resultant character expression. The concatenation operator is flexible enough to be used multiple times and almost anywhere in a character expression. Consider the following query: SELECT 'THE '||REGION_NAME||' region is on Planet Earth' "Planetary Location", FROM REGIONS; Here, the character literal “The” is concatenated to the contents of the REGION_NAME column. This new string of characters is further concatenated to the character literal “region is on Planet Earth”, and the entire expression is aliased with the friendly heading “Planetary Location”. Literals and the DUAL Table Literals are commonly used in expressions and refer to numeric, character, or date and time values found in SELECT clauses that do not originate from any database object. Concatenating character literals to existing column data can be useful, but what about processing literals that have nothing to do with existing column data? To ensure relational consistency, Oracle offers a clever solution to the problem of using the database to evaluate expressions that have nothing to do with any tables or columns. To get the database to evaluate an expression, a syntactically legal SELECT statement must be submitted. What if you wanted to know the sum of two numeric literals? Oracle solves the problem of relational interaction with the database operating on literal expressions by providing a special single-rowed, single-columned table called DUAL. Chapter 9: Retrieving, Restricting, and Sorting Data Using SQL 389 PART II Recall the DUAL table described in Figure 9-1. It contains one column called DUMMY of the character data type. You can execute the query SELECT * FROM DUAL, and the data value “X” is returned as the contents of the DUMMY column. Testing complex expressions during development, by querying the dual table, is an effective method to evaluate whether these expressions are correct. Literal expressions can be queried from any table, but remember that the expression will be processed for every row in the table, while querying the DUAL table returns only one row. select 'literal '||'processing using the REGIONS table' from regions; select 'literal '||'processing using the DUAL table' from dual; The first statement will return four lines in the results set, since there are four rows of data in the REGIONS table, while the second returns only one row. Two Single Quotes or the Alternative Quote Operator The literal character strings concatenated so far have been singular words prepended and appended to column expressions. These character literals are specified using single quotation marks. For example: select 'I am a character literal string' from dual; What about character literals that contain single quotation marks? Plurals pose a particular problem for character literal processing. Consider the following statement: select 'Plural's have one quote too many' from dual; Executing this statement causes an ORA-00923 Oracle error to be generated. So, how are words that contain single quotation marks dealt with? There are essentially two mechanisms available. The most popular of these is to add an additional single quotation mark next to each naturally occurring single quotation mark in the character string. The following statement demonstrates how the previous error is avoided by replacing the character literal 'Plural's with the literal 'Plural''s. select 'Plural''s have one quote too many' from dual; Using two single quotes to handle each naturally occurring single quote in a character literal can become messy and error prone as the number of affected literals increases. Oracle offers a neat way to deal with this type of character literal in the form of the alternative quote (q) operator. The problem is that Oracle chose the single quote character as the special symbol with which to enclose or wrap other character literals. These character-enclosing symbols could have been anything other than single quotation marks. Bearing this in mind, consider the alternative quote (q) operator. The q operator enables you to choose from a set of possible pairs of wrapping symbols for character literals as alternatives to the single quote symbols. The options are any single-byte or OCA/OCP Oracle Database 11g All-in-One Exam Guide 390 multibyte character or the four brackets: (round brackets), {curly braces}, [square brackets], or <angle brackets>. Using the q operator, the character delimiter can effectively be changed from a single quotation mark to any other character, as shown here: SELECT q'<Plural's can also be specified with alternate quote operators>' "q<>" FROM DUAL; SELECT q'[Even square brackets' [] can be used for Plural's]' "q[]" FROM DUAL; SELECT q'XWhat about UPPER CASE X for Plural'sX' "qX" FROM DUAL; The syntax of the alternative quote operator is as follows: q'delimiter character literal which may include single quotes delimiter' where delimiter can be any character or bracket. The first and second examples show the use of angle and square brackets as character delimiters, while the third example demonstrates how an uppercase “X” has been used as the special character delimiter symbol through the alternative quote operator. Note that the “X” character can itself be included in the string—so long as it is not followed by a quotation mark. NULL Is Nothing Null refers to an absence of data. A row that contains a null value lacks data for that column. Null is formally defined as a value that is unavailable, unassigned, unknown, or inapplicable. Failure to heed the special treatment that null values require will almost certainly lead to an error, or worse, an inaccurate answer. This section focuses on interacting with null column data with the SELECT statement and its impact on expressions. Not Null and Nullable Columns Tables store rows of data that are divided into one or more columns. These columns have names and data types associated with them. Some of them are constrained by database rules to be mandatory columns. It is compulsory for some data to be stored in the NOT NULL columns in each row. When columns of a table, however, are not compelled by the database constraints to hold data for a row, these columns run the risk of being empty. TIP Any arithmetic calculation with a NULL value always returns NULL. Oracle offers a mechanism for interacting arithmetically with NULL values using the general functions discussed in Chapter 10. Division by a null value results in null, unlike division by zero, which results in an error. When a null is encountered by the character concatenation operator, however, it is simply ignored. The character Chapter 9: Retrieving, Restricting, and Sorting Data Using SQL 391 PART II concatenation operators ignore null, while the arithmetic operations involving null values always result in null. Foreign Keys and Nullable Columns Data model design sometimes leads to problematic situations when tables are related to each other via a primary and foreign key relationship, but the column that the foreign key is based on is nullable. The DEPARTMENTS table has, as its primary key, the DEPARTMENT_ID column. The EMPLOYEES table has a DEPARTMENT_ID column that is constrained by its foreign key relationship to the DEPARTMENT_ID column in the DEPARTMENTS table. This means that no record in the EMPLOYEES table is allowed to have in its DEPARTMENT_ID column a value that is not in the DEPARTMENTS table. This referential integrity forms the basis for third normal form and is critical to overall database integrity. But what about NULL values? Can the DEPARTMENT_ID column in the DEPARTMENTS table contain nulls? The answer is no. Oracle insists that any column that is a primary key is implicitly constrained to be mandatory. But what about implicit constraints on foreign key columns? This is a quandary for Oracle, since in order to remain flexible and cater to the widest audience, it cannot insist that columns related through referential integrity constraints must be mandatory. Further, not all situations demand this functionality. The DEPARTMENT_ID column in the EMPLOYEES table is actually nullable. Therefore, the risk exists that there are records with null DEPARTMENT_ID values present in this table. In fact, there are such records in the EMPLOYEES table. The HR data model allows employees, correctly or not, to belong to no department. When performing relational joins between tables, it is entirely possible to miss or exclude certain records that contain nulls in the join column. Chapter 12 discusses ways to deal with this challenge. Exercise 9-2: Construct Expressions In this exercise you will construct two queries to display results with an appropriate layout, one from the WEBSTORE schema and the other from the HR schema. 1. Query the WEBSTORE.CUSTOMERS table to retrieve a list of the format: X has been a member for Y days, where X is the CUSTOMER_NAME and Y is the number of days between today and the day the customer joined. Alias the expression: Customer Loyalty. 2. Add a character string expression that concatenates string literals around the CUSTOMER_NAME value and the date expression. A possible solution is select customer_name||' has been a member for: '||(sysdate-join_date)||' days.' "Customer Loyalty" from customers; 3. Query the HR.JOBS table and return a single expression of the form The Job Id for the <job_title's> job is: <job_id>. Take note that the job_title should have an apostrophe and an “s” appended to it to read more naturally. A sample of this output for the organization president is: “The Job Id for the OCA/OCP Oracle Database 11g All-in-One Exam Guide 392 President’s job is: AD_PRES”. Alias this column expression: Job Description using the AS keyword. There are multiple solutions to this problem. The approach chosen here is to handle the naturally occurring single quotation mark with an additional single quote. You could make use of the alternate quote operator to delimit the naturally occurring quote with another character. 4. A single expression aliased as Job Description is required; you may construct it by concatenating the literal “The Job Id for the” to the JOB_TITLE column. This string is then concatenated to the literal “‘s job is: ”, which is further concatenated to the JOB_ID column. An additional single quotation mark is added to yield the SELECT statement that follows: select 'The Job Id for the '||job_title||'''s job is: '||job_id AS "Job Description" from jobs; Limit the Rows Retrieved by a Query One of the cornerstone principles in relational theory is selection. Selection is actualized using the WHERE clause of the SELECT statement, sometimes referred to as the predicate. Conditions that restrict the dataset returned take many forms and operate on columns as well as expressions. Only rows that conform to these conditions are returned. Conditions restrict rows using comparison operators in conjunction with columns and literal values. Boolean operators provide a mechanism to specify multiple conditions to restrict the rows returned. Boolean, conditional, concatenation, and arithmetic operators are discussed to establish their order of precedence when they are encountered in a SELECT statement. The WHERE Clause The WHERE clause extends the SELECT statement by providing the ability to restrict rows returned based on one or more conditions. Querying a table with just the SELECT and FROM clauses results in every row of data stored in the table being returned. Using the DISTINCT keyword, duplicate values are excluded, and the resultant rows are restricted to some degree. What if very specific information is required from a table, for example, only the data where a column contains a specific value? How would you retrieve the countries that belong to the Europe region from the COUNTRIES table? What about retrieving just those employees who work as sales representatives? These questions are answered using the WHERE clause to specify exactly which rows must be returned. The format of the SQL SELECT statement that includes the WHERE clause is SELECT *|{[DISTINCT] column|expression [alias], } FROM table [WHERE condition(s)]; The WHERE clause always follows the FROM clause. The square brackets indicate that the WHERE clause is optional. One or more conditions may be simultaneously applied to restrict the result set. A condition is specified by comparing two terms using a conditional operator. These terms may be column values, literals, or expressions. The Chapter 9: Retrieving, Restricting, and Sorting Data Using SQL 393 PART II equality operator is most commonly used to restrict result sets. An example of using a WHERE clause is shown next: select country_name from countries where region_id=3; This example projects the COUNTRY_NAME column from the COUNTRIES table. Instead of selecting every row, the WHERE clause restricts the rows returned to only those containing a 3 in the REGION_ID column. Numeric-Based Conditions Conditions must be formulated appropriately for different column data types. The conditions restricting rows based on numeric columns can be specified in several different ways. Consider the SALARY column in the EMPLOYEES table. This column has a data type of NUMBER(8,2). The SALARY column can be restricted as follows: select last_name, salary from employees where salary = 10000; The LAST_NAME and SALARY values of the employees who earn $10,000 are retrieved, since the data types on either side of the operator match and are compatible. A numeric column can be compared to another numeric column in the same row to construct a WHERE clause condition, as the following query demonstrates: select last_name, salary from employees where salary = department_id; This WHERE clause is too restrictive and results in no rows being selected because the range of SALARY values is 2100 to 999999.99, and the range of DEPARTMENT_ID values is 10 to 110. Since there is no overlap in the range of DEPARTMENT_ID and SALARY values, there are no rows that satisfy this condition and therefore nothing is returned. WHERE clause conditions may also be used to compare numeric columns and expressions or to compare expressions to other expressions: select last_name, salary from employees where salary = department_id*100; select last_name, salary from employees where salary/10 = department_id*10; The first example compares the SALARY column with DEPARTMENT_ID*100 for each row. The second example compares two expressions. Notice that the conditions in both examples are algebraically identical, and the same dataset is retrieved when both are executed. Character-Based Conditions Conditions determining which rows are selected based on character data are specified by enclosing character literals in the conditional clause, within single quotes. The JOB_ID column in the EMPLOYEES table has a data type of VARCHAR2(10). Suppose you OCA/OCP Oracle Database 11g All-in-One Exam Guide 394 wanted a list of the LAST_NAME values of those employees currently employed as sales representatives. The JOB_ID value for a sales representative is SA_REP. The following statement produces such a list: select last_name from employees where job_id='SA_REP'; If you tried specifying the character literal without the quotes, an Oracle error would be raised. Remember that character literal data is case sensitive, so the following WHERE clauses are not equivalent. Clause 1: where job_id=SA_REP Clause 2: where job_id='Sa_Rep' Clause 3: where job_id='sa_rep' Clause 1 generates an “ORA-00904: ‘SA_REP’: invalid identifier” error, since the literal SA_REP is not wrapped in single quotes. Clause 2 and Clause 3 are syntactically correct but not equivalent. Further, neither of these clauses yields any data, since there are no rows in the EMPLOYEES table having JOB_ID column values that are either Sa_Rep or sa_rep. Character-based conditions are not limited to comparing column values with literals. They may also be specified using other character columns and expressions. Character-based expressions may form either one or both parts of a condition separated by a conditional operator. These expressions can be formed by concatenating literal values with one or more character columns. The following four clauses demonstrate some of the options for character-based conditions: Clause 1: where 'A '||last_name||first_name = 'A King' Clause 2: where first_name||' '||last_name = last_name||' '||first_name Clause 3: where 'SA_REP'||'King' = job_id||last_name Clause 4: where job_id||last_name ='SA_REP'||'King' Clause 1 concatenates the string literal “A” to the LAST_NAME and FIRST_NAME columns. This expression is compared to the literal “A King”. Clause 2 demonstrates that character expressions may be placed on both sides of the conditional operator. Clause 3 illustrates that literal expressions may also be placed on the left of the conditional operator. It is logically equivalent to clause 4, which has swapped the operands in clause 3 around. Both clauses 3 and 4 identically restrict the results. Date-Based Conditions DATE columns are useful for storing date and time information. Date literals must be enclosed in single quotation marks just like character data. When used in conditional WHERE clauses, date columns may be compared to other date columns, literals, or expressions. The literals are automatically converted into DATE values based on the default date format, which is DD-MON-RR. If a literal occurs in an expression involving a DATE column, it is automatically converted into a date value using the default format mask. DD represents days, MON represents the first three letters of a month, and RR represents a Year 2000–compliant year (that is, if RR is between 50 Chapter 9: Retrieving, Restricting, and Sorting Data Using SQL 395 PART II and 99, then the Oracle server returns the previous century, or else it returns the current century). The full four-digit year, YYYY, can also be specified. Consider the following four WHERE clauses: Clause 1: where start_date = end_date; Clause 2: where start_date = '01-JAN-2001'; Clause 3: where start_date = '01-JAN-01'; Clause 4: where start_date = '01-JAN-99'; The first clause tests equality between two DATE columns. Rows that contain the same values in their START_DATE and END_DATE columns will be returned. Note, however, that DATE values are only equal to each other if there is an exact match between all their components, including day, month, year, hours, minutes, and seconds. Chapter 10 discusses the details of storing DATE values. Until then, don’t worry about the hours, minutes, and seconds components. In the second WHERE clause, the START_DATE column is compared to the character literal: ‘01-JAN-2001’. The entire four-digit year component (YYYY) has been specified. This is acceptable to the Oracle server. The third condition is equivalent to the second, since the literal ‘01-JAN-01’ is converted to the date value 01-JAN-2001. This is due to the RR component being less than 50, so the current (twenty-first) century, 20, is prefixed to the year RR component to provide a century value. The century component for the literal ‘01-JAN-99’ becomes the previous century (19) and is converted to a date value of 01-JAN-1999 for the fourth condition, since the RR component, 99, is greater than 50. Date arithmetic using the addition and subtraction operators is supported. An expression like END_DATE – START_DATE returns the number of days between START_DATE and END_DATE. START_DATE + 30 returns a date 30 days later than START_DATE. EXAM TIP Conditional clauses compare two terms using comparison operators. Knowing the data types of the terms is important so that they can be enclosed in single quotes, if necessary. Comparison Operators The equality operator is generally used to illustrate the concept of restricting rows using a WHERE clause. There are several alternative operators that may also be used. The inequality operators like “less than” or “greater than or equal to” may be used to return rows conforming to inequality conditions. The BETWEEN operator facilitates range-based comparison to test whether a column value lies between two values. The IN operator tests set membership, so a row is returned if the column value tested in the condition is a member of a set of literals. The pattern matching comparison operator LIKE is extremely powerful, allowing components of character column data to be matched to literals conforming to a specific pattern. The last comparison operator discussed in this section is the IS NULL operator, which returns rows where the column value contains a null value. These operators may be used in any combination in the WHERE clause. . heading is an alias. An alias is Figure 9-5 Arithmetic expression to calculate number of days worked OCA/ OCP Oracle Database 11g All-in-One Exam Guide 388 an alternate name for a column or an. EMPLOYEES table has a data type of VARCHAR2(10). Suppose you OCA/ OCP Oracle Database 11g All-in-One Exam Guide 394 wanted a list of the LAST_NAME values of those employees currently employed as sales. a quotation mark. NULL Is Nothing Null refers to an absence of data. A row that contains a null value lacks data for that column. Null is formally defined as a value that is unavailable, unassigned,