1. Trang chủ
  2. » Công Nghệ Thông Tin

Oracle SQL Plus The Definitive Guide- P15 pps

10 332 0
Tài liệu đã được kiểm tra trùng lặp

Đang tải... (xem toàn văn)

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Tiêu đề Taking Advantage of Unions
Trường học Standard University
Chuyên ngành Computer Science
Thể loại Bài luận
Năm xuất bản 1998
Thành phố New York
Định dạng
Số trang 10
Dung lượng 100,38 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

Here's the query to return a list of people employed as of January 1, 1998: SELECT employee_id, employee_name, employee_hire_date, employee_termination_date FROM employee WHERE empl

Trang 1

Page 117

Taking Advantage of Unions

A union is a SQL construct that allows you to knit together the results of several SQL queries and treat those results as

if they had been returned by just one query I find them invaluable when writing queries, and one of the more creative uses I've discovered involves using unions to produce reports that need to show data grouped by categories, and that may need to show the same records in more than one of those categories

A typical example

A good example of this type or report would be one that fulfills the following request:

Produce an employee turnover report that lists everyone employed at the beginning of the year, everyone hired during the year, everyone terminated during the year, and everyone still employed at the end of the year The report should be divided into four sections, one for each of those categories

This is not an unusual type of request, not for me at least The interesting thing about this request, though, is that every employee will need to be listed in exactly two categories That means you would need to write a query that returned each employee record twice, in the correct categories

When you are faced with this type of query, it can be helpful to simplify the problem by thinking in terms of separate queries, one for each category It's fairly easy to conceive of a query to bring back a list of employees that were on board at the beginning of the year You just need to make sure the first of the year is between the hire and termination dates, and account for the fact that the termination date might be null Here's the query to return a list of people

employed as of January 1, 1998:

SELECT employee_id,

employee_name,

employee_hire_date,

employee_termination_date

FROM employee

WHERE employee_hire_date < TO_DATE(1-Jan-1988, dd-mon-yyyy)

AND (employee_termination_date IS NULL

OR employee_termination_date >= TO_DATE(1-Jan-1988, dd-mon-yyyy))

This gives you the first section of the reportthose employed at the beginning of the year Retrieving the data for the remaining sections is a matter of using a different WHERE clause for each section Table 3-3 shows the selection

criteria for each section of the report

Trang 2

Table 3-3 Union Query Selection Criteria

Employed at beginning of year WHERE employee_hire date < TO_DATE(1-Jan-1998, dd-mon-yyyy)

AND (employee_termination_date IS NULL

OR employee_termination_date >=TO_DATE(1-Jan-1998, dd-mon-yyyy)) Hired during the year WHERE employee_hire_date >= TO_DATE (1-Jan-1998 dd-mon-yyyy)

AND (employee_hire_date < TO_DATE(1-Jan-1999, dd-mon-yyyy))

Terminated during the year WHERE employee_termination_date >=

TO_DATE (1-Jan-1998, dd-mm-yyyy) AND (employee_termination_date <

TO_DATE(1-Jan-1999, dd-mm-yyyy)) Employed at end of year WHERE employee_hire_date < TO_DATE (1-Jan-1998, dd-mm-yyyy)

AND (employee_termination_date IS NULL

OR employee_termination_date >= TO_DATE(1-Jan-1999, dd-mm-yyyy))

The UNION query

After separately developing the four queries, one for each section of the report, you can use SQL's UNION operator to link those four

queries together into one large query There are four things to consider when doing this:

1 You need to return all the records retrieved by all four queries.

2 You need to be able to group the retrieved records by category.

3 You need to be able to control which category prints first.

4 You need to identify each category on the printed report so the end user knows what's what.

To be certain of getting all the records back from the query, use the UNION ALL operator to tie the queries together Using UNION by itself

causes SQL to filter out any duplicate rows in the result set That's not really an issue with this example there won't be any duplicate rowsbut it's

an important point to consider.

In order to properly group the records, you can add a numeric constant to each of the four queries For example, the query to return the list of

those employed at the beginning of the year could return an arbitrary value of 1:

SELECT 1 sort_column,

employee_id,

employee_name,

The other queries would return values of 2, 3, and 4 in the sort column Sorting the query results on these arbitrary numeric values serves

two purposes First, the records for each section of the report will be grouped together because they will all have the same constant Second,

the value of the sort column controls the order in which the sections print Use a value of 1 for the section to be printed first, a value of 2 for

the second section, etc.

Trang 3

Page 119 The final thing to worry about is identifying the results to the reader of the report The values used in the sort column won't mean anything to the reader, so you also need to add a column with some descriptive text Here's how the final query for people employed at the beginning of the year looks with that text added:

SELECT 1 sort_column,

Employed at Beginning of Year employee_status_text,

employee_id,

employee_name,

employee_hire_date,

employee_termination_date

FROM employee

WHERE employee_hire_date < TO_DATE(1-Jan-1998,dd-mon-yyyy)

AND (employee_termination_date IS NULL

OR employee_termination_date >= TO_DATE(1-Jan-1998,dd-mon-yyyy))

The first column returned by this query is used to sort these records to the top of the report, while the second column serves to identify those records for the reader The full-blown UNION query to produce all four sections of the report looks like this:

SELECT 1 sort_column,

Employed at Beginning of Year employee_status_text,

employee_id,

employee_name,

employee_hire_date,

employee_termination_date

FROM employee

WHERE employee_hire_date < TO_DATE(1-Jan-1998, dd-mon-yyyy)

AND (employee_termination_date IS NULL

OR employee_termination_date >= TO_DATE(1-Jan-1998, dd-mon-yyyy)) UNION ALL

SELECT 2 as sort_column,

Hired During Year as employee_status_text,

employee_id,

employee_name,

employee_hire_date,

employee_termination_date

FROM employee

WHERE employee_hire_date >= TO_DATE(1-Jan-1998, dd-mon-yyyy)

AND (employee_hire_date < TO_DATE(1-Jan-1999,dd-mon-yyyy))

UNION ALL

SELECT 3 as sort_column,

Terminated During Year as employee_status_text,

employee_id,

employee_name,

employee_hire_date,

employee_termination_date

FROM employee

WHERE employee_termination_date >= TO_DATE(1-Jan-1998, dd-mon-yyyy)

AND (employee_termination_date < TO_DATE (1-Jan-1999, dd-mon-yyyy))

UNION ALL

SELECT 4 as sort_column,

Trang 4

Employed at End of Year as employee_status_text,

employee_id,

employee_name,

employee_hire_date,

employee_termination_date

FROM employee

WHERE employee_hire_date < TO_DATE(1-Jan-1999, dd-mon-yyyy)

AND (employee_termination_date IS NULL

OR employee_termination_date >= TO_DATE (1-Jan-1999, dd-mon-yyyy)) ORDER BY sort_column, employee_id, employee_hire_date;

As you can see, the four queries have been unioned together in the same order in which the report is to be printed That's done for readability, though It's the ORDER BY clause at the bottom that ensures that the records are returned in the proper order

The final report

All that's left now that the query has been worked out is to follow the remaining steps in the report development

methodology to format and print the report To produce a fairly basic, columnar report, precede the query with the

following commands:

Setup pagesize parameters

SET NEWPAGE 0

SET PAGESIZE 55

Set the linesize, which must match the number of equal signs used

for the ruling lines in the headers and footers

SET LINESIZE 75

TTITLE CENTER The Fictional Company SKIP 2 -

CENTER Employee Turnover Report SKIP 1 -

LEFT ================================ -

===================================== -

SKIP 3

Format the columns

CLEAR COLUMNS

COLUMN sort_column NOPRINT

COLUMN employee_status_text HEADING Status FORMAT A29

COLUMN employee_name HEADING Employee Name FORMAT A20

COLUMN employee_hire_date HEADING Hire Date FORMAT A11

COLUMN employee_termination_date HEADING Term Date FORMAT A11

Breaks and computations

BREAK ON EMPLOYEE_status_text SKIP 2 NODUPLICATES

CLEAR COMPUTES

COMPUTE NUMBER LABEL Total Count OF employee_name ON employee_status_text

Set the date format to use

ALTER SESSION SET NLS_DATE_FORMAT = dd-Mon-yyyy;

Trang 5

Page 121

When you execute this report, the output will look like this:

The Fictional Company

Employee Turnover Report

=============================================================================

Status Employee Name Hire Date Term date

- - - -

Employed at beginning of year Jonathan Gennick 15-Nov-1961

Jenny Gennick 16-Sep-1964 05-May-1998

Jeff Gennick 29-Dec-1987 01-Apr-1998

Pavlo Chubynsky 01-Mar-1994 15-Nov-1998

Taras Shevchenko 23-Agu-1976

Hermon Goche 15-Nov-1961 04-Apr-1998

**************************** -

Total Count 6

Hired During Year Hourace Walker 15-Jun-1998

Bohdan Khmelnytsky 02-Jan-1998

Ivan Mazepa

04-Apr-1998 30-Sep-1998

Jacob Marley 03-Mar-1998 31-oct-1998

**************************** -

Total Count 4

Terminated During Year Jenny Gennick 16-Sep-1964 05-May-1998 Jeff Gennick 29-Dec-1987 01-Apr-1998 Pavlo Chubynsky 01-Mar-1994 15-Nov-1998 Ivan Mazepa 04-Apr-1998 30-Sep-1998 Hermon Goche 15-Nov-1961 04-Apr-1998 Jacob Marley 03-Mar-1998 31-Oct-1998

**************************** -

Total Count 6

Employed at End of Year Jonathan Gennick 15-Nov-1961

Horace Walker 15-Jun-1998

Bohdan Khmelnytsky 02-Jan-1998

Taras Shevchenko 23-Aug-1976

**************************** -

Total Count 4

That's all there is to it It wouldn't be a big leap to turn this report into a master/ detail report, with each section starting on a new page Using this technique, you can develop similar reports with any number of sections you need.

Trang 6

4

Writing SQL*Plus Scripts

In this chapter:

Why Write Scripts?

Using Substitution Variables

Prompting for Values

Cleaning Up the Display

Packaging Your Script

The DEFINE and UNDEFINE Commands

Controlling Variable Substitution

Commenting Your Scripts

In the previous chapter, you saw how to write a script to produce a report This chapter delves more deeply into the subject of scripting, and shows you how to write interactive scripts You will learn how to use substitution variables, which allow the user to dynamically supply values to a script at runtime You will learn how to prompt the user for those values, and how to display other messages for the user to see Finally, you will learn how to package your script for easy access when you need it

Why Write Scripts?

The most compelling reason to write scripts, in my mind, is to encapsulate knowledge Say, for example, that you have developed a query that returns index definitions for a table You certainly don't want to have to think through the entire process of developing that query each time you need to see an index If you have a good script available, you just run it Likewise, if someone asks you how to see index definitions for a table, just give them a copy of the script

A second reason for developing scripts is that they save time Look at the script to produce the first report in Chapter 3,

Generating Reports with SQL*Plus It contains 17 separate commands, some quite long By placing those commands in

a script, you save yourself the time and effort involved in retyping all of them each time you run the report

Lastly, scripts can simplify tasks both for you and for others When you know you have a good, reliable script, you can just run it, answer the questions, then sit back

Trang 7

Page 123 while it does all the work You don't need to worry, thinking did I enter the correct command?, did I log on as the

correct user?, or did I get that query just right?

Anytime you find yourself performing a task over and over, think about writing a script to do it for you You'll save yourself time You'll save yourself stress You'll be able to share your knowledge more easily

A good source of ready-to-run scripts for Unix users is Oracle Scripts by Brian Lomasky and

David C Kreines, O'Reilly & Associates, 1998

Using Substitution Variables

Substitution variables allow you to write generic SQL*Plus scripts They allow you to mark places in a script where you want to substitute values at runtime

What Is a Substitution Variable?

A substitution variable is the same thing as a user variable In the previous chapter, you saw how to get the contents of a

database column into a user variable and how to place the contents of that user variable into the page header of a report SQL*Plus also allows you to place user variables in your script to mark places where you want to supply a value at runtime When you use them this way, they are called substitution variables

A substitution variable is not like a true variable used in a programming language Instead, a substitution variable marks places in the text where SQL*Plus does the equivalent of a search and replace at runtime, replacing the reference to a substitution variable with its value

Substitution variables are set off in the text of a script by preceding them with either one or two ampersand characters Say, for example, that you had this query to list all projects to which employee #107 had charged time:

SELECT DISTINCT p.project_id, p.project_name

FROM project p,

project_hours ph

WHERE ph.employee_id = 107

AND p.project_id = ph.project_id;

As you can see, this query is specific to employee number 107 To run the query for a different employee, you would need to edit your script file, change the ID number, save the file, then execute it That's a pain You don't want to do that

Trang 8

Instead, you can generalize the script by rewriting the SELECT statement with a substitution variable in place of the employee ID number It would look like this:

SELECT DISTINCE p.project_id, p.project_name

FROM project p,

project_hours ph

WHERE ph.employee_id = &employee_id

AND p.project_id = ph.project_id;

The ampersand in front of the word employee_id marks it as a variable At runtime, when it reads the statement,

SQL*Plus will see the substitution variable and replace it with the current value of the specified user variable If the employee_id user variable contained a value of 104, then &employee_id would be replaced by 104, and the resulting line would look like this:

WHERE ph.employee_id = 104

As stated earlier, and as you can see now, SQL*Plus truly does a search and replace operation The Oracle database does not know that a variable has been used Nor does SQL*Plus actually compare the contents of the employee_id column against the value of the variable SQL*Plus simply does the equivalent of a search and replace operation on each statement before that statement is executed As far as the Oracle database is concerned, you might just as well have included constants in your script

Substitution variables are the workhorse of SQL*Plus scripts They give you a place to store user input, and they give you a way to use that input in SQL queries, PL/ SQL code blocks, and other SQL*Plus commands

Using Single-Ampersand Variables.

The easiest way to generalize a script is to take one you have working for a specific case and modify it by replacing specific values with substitution variables In this section, we will revisit the Labor Hours and Dollars Detail report shown in Chapter 3 You will see how you can modify the script to print the report for only one employee, and you will see how you can use a substitution variable to generalize that script by making it prompt for the employee ID number at runtime

When SQL*Plus encounters a variable with a single leading ampersand, it always prompts you for a value This is true even when you use the same variable multiple times in your script If you use it twice, you will be prompted twice Doubleampersand variables allow you to prompt a user only once for a given value, and are explained later in this chapter

The report for one specific employee

The report in the previous chapter produced detailed hours and dollars information for all employees To reduce the scope to one employee, you can add this line to the WHERE clause:

Trang 9

Page 125 AND e.employee_id = 107

Since this report is now only for one employee, the grand totals don't make sense, so the COMPUTES to create them can be removed Finally, a SPOOL command has been added to capture the output in a file to be printed later The complete script for the report looks like this:

Setup pagesize parameters

SET NEWPAGE 0

SET PAGESIZE 55

Set the linesize, which must match the number of equal signs used

for the ruling lines in the headers and footers

SET LINESIZE 71

Get the date for inclusion in the page footer

SET TERMOUT OFF

ALTER SESSION SET NLS_DATE_FORMAT = DD-Mon_YYYY;

COLUMN SYSDATE NEW_VALUE report_date

SELECT SYSDATE FROM DUAL;

SET TERMOUT ON

Setup page headings and footings

TTITLE CENTER The Ficional Company SKIP 3 -

LEFT I.S.Department -

RIGHT Project Hours and Dollars Detail SKIP 1 -

LEFT ============================================================= -

SKIP 2 Employee: FORMAT 9999 emp_id_var emp_name_var SKIP 3

BTITLE LEFT ============================================================= -

SKIP 1 -

LEFT report_date -

RIGHT Page FORMATE 999 SQL.PNO

Format the columns

COLUMN employee_id NEW_VLUE emp_id_var NOPRINT

COLUMN employee_name NEW_VALUE emp_name_var NOPRINT

COLUMN project_id HEADING Proj ID FORMAT 9999

COLUMN project_name HEADING Project Name FORMAT A26 WORD_WRAPPED

COLUMN time_log_date HEADING Date FORMAT All

COLUMN hours_logged HEADING Hours FORMAT 9,999

COLUMN dollars_charged HEADING Dollars¦Charged FORMAT $999,999.99

Breaks and Computations

BREAK ON employee_id SKIP PAGE NODUPLICATES -

ON employee_name NODUPLICATES -

ON project_id SKIP 2 NODUPLICATES -

ON project_name NODUPLICATES

CLEAR COMPUTES

COMPUTE SUM LABEL Project Totals OF hours_logged ON project_name

COMPUTE SUM LABEL Project Totals OF dollars_charged ON project_name

COMPUTE SUM LABEL Totals OF hours_logged ON employee_id

COMPUTE SUM LABEL Totals OF dollars_charged ON employee_id

Trang 10

Execute the query to generate the report

SPOOL C:\A\HOURS_DOLLARS

SELECT P.PROJECT_ID

P.PROJECT_NAME,

TO_CHAR(PH.TIME_LOG_DATE, dd-Mon_yyyy) time_log_date,

PH.HOURS_LOGGED,

PH.DOLLARS_CHARGED,

E.EMPLOYEE_ID,

E.EMPLOYEE_NAME

FROM EMPLOYEE E,

PROJECT P,

PROJECT_HOURS PH

WHERE E.EMPLOYEE_ID = PH.EMPLOYEE_ID

AND P.PROJECT_ID=PH.PROJECT_ID

AND E.EMPLOYEE_ID = 107

ORDER BY.E.EMPLOYEE_ID, P.PROJECT_ID, PH.TIME_LOG_DATE;

SPOOL OFF

Reset everything back to the defaults

CLEAR BREAKS

CLEAR COMPUTES

TTITLE OFF

BTITLE OFF

SET NEWPAGE 1

SET PAGESIZE 24

SET LINESIZE 80

Running this script as shown will produce a report specifically for employee 107

Generalizing the report with substitution variables

You don't want to edit the script file and modify your script every time you need to produce a report for a different employee, and you don't have to Instead, you can replace the reference to a specific employee number with a

substitution variable and let SQL*Plus prompt you for a value at runtime Here's how the affected line of script looks with a substitution variable instead of a hardcoded value:

AND E.EMPLOYEE_ID = &employee_id

The variable name should be descriptive, and it needs to serve two purposes It needs to inform the user and it needs to inform you First and foremost, the variable name is used in the prompt, and must convey to the user the specific

information needed In this case, for example, using &id for the variable would leave the user wondering whether to enter an employee ID or a project ID The second thing to keep in mind is that you will need to look at the script again someday, so make sure the name is something that will jog your memory as well

Running the report

When you run the report, SQL*Plus will prompt you for the value of the &employee_id substitution variable Assume

that the script is in a file named HOURS_DOLLARS.SQL Here's how the output will look:

Ngày đăng: 05/07/2014, 04:20

TỪ KHÓA LIÊN QUAN