![]() |
Table of Contents |
![]() |
3.3 SQL Command Reference
The ALL operator performs a Boolean test of a subquery for the existence of a value in all rows. The ANY operator, and its synonym SOME, performs a Boolean test of a subquery for the existence of a value in any of the rows tested.
SQL2003 SyntaxSELECT ... WHERE expression comparison {ALL | ANY | SOME} ( subquery ) Keywords
Rules at a GlanceThe ALL operator returns a Boolean TRUE value when one of two things happen: either the subquery returns an empty set (i.e., no records) or every record in the set meets the comparison. ALL returns FALSE when any records in the set do not match the value comparison. ANY and SOME operators return a Boolean TRUE when at least one record in the subquery matches the comparison operation, and FALSE when no record matches the comparison operation (or when a subquery returns an empty result set). If even one return value of the subquery is NULL, then the operation evaluates as NULL, not as TRUE.
For example, we want to see which authors currently have no titles: SELECT au_id FROM authors WHERE au_id <> ALL(SELECT au_id FROM titleauthor) You can use ANY / SOME to perform filtering checks of different kinds. For example, the following query will retrieve any records from the employees table that exist in the employees_archive table that were located in Anchorage: SELECT * FROM employees WHERE job_lvl = ANY(SELECT job_lvl FROM employees_archive WHERE city = 'Anchorage') The above query returns information about employees who have the same job_lvl as any employee working in Anchorage, Alaska. Programming Tips and GotchasThe ALL and ANY/SOME operators are somewhat difficult to get used to. Most developers find it a lot easier to use similar functions like IN and EXISTS.
Platform DifferencesAll of the platforms support ALL and ANY/SOME in the manner described above except MySQL, which doesn't support subqueries until Version 4.0. On pre-Version 4.0 MySQL platforms, consider using the IN operator instead of EXISTS for ANY/SOME. Oracle supports one minor variation in that you can supply a list of values instead of a subquery. For example, we can find all employees who have a job_lvl value equal to 9 or 14: SELECT * FROM employee WHERE job_lvl = ALL(9, 14); DB2 and SQL Server support some additional comparison operators: not greater than (!>) and not less than (!<). See Also
The BETWEEN operator performs a Boolean test of a value against a range of values. It returns TRUE when the value is included in the range and FALSE when the value falls outside of the range. The results are NULL (unknown) if any of the range values are NULL.
SQL2003 SyntaxSELECT ... WHERE expression [NOT] BETWEEN lower_range AND upper_range Keywords
Rules at a GlanceThe BETWEEN operator is used to test an expression against a range of values. The BETWEEN operator may be used with any datatype except BLOB, CLOB, NCLOB, REF, and ARRAY. For example, we want to see title_ids that have year-to-date sales between 10,000 and 20,000: SELECT title_id FROM titles WHERE ytd_sales BETWEEN 10000 AND 20000 BETWEEN is inclusive of the range of values listed. In this case, it includes the values of 10,000 and 20,000 in the search. If you want an exclusive search, then you must use the greater than (>) and less than (<) symbols: SELECT title_id FROM titles WHERE ytd_sales > 10000 AND ytd_sales < 20000 The NOT operator allows you to search for values outside of the BETWEEN range. For example, you can also find all the title_ids that were not published during 2002: SELECT title_id FROM titles WHERE pub_date NOT BETWEEN '01-JAN-2003' AND '31-DEC-2003' Programming Tips and GotchasSome coders are very particular about how the keyword AND is used in WHERE clauses. To prevent a casual reviewer from thinking that the AND used in a BETWEEN operation is a logical AND operator, you might want to use parentheses to encapsulate the entire BETWEEN clause: SELECT title_id FROM titles WHERE (ytd_sales BETWEEN 10000 AND 20000) AND pubdate >= '1991-06-12 00:00:00.000' Platform DifferencesAll of the platforms support BETWEEN in the manner described above. See Also
The CALL statement invokes a stored procedure.
SQL2003 SyntaxCALL procedure_name ([parameter [,...] ]) Keywords
Rules at a GlanceThe CALL statement makes it easy to invoke a stored procedure. Simply provide the name of the stored procedure and include any parameters used by the stored procedure, enclosing them within parentheses. This Oracle example creates a simple stored procedure, and then calls it: CREATE PROCEDURE update_employee_salary (emp_id NUMBER, updated_salary NUMBER) IS BEGIN UPDATE employee SET salary = updated_salary WHERE employee_id =emp_id ; END; CALL update_employee_salary(1517, 95000); Programming Tips and GotchasThe return status of a called stored procedure can be found, typically, by using GET DIAGNOSTIC. GET DIAGNOSTIC is not widely supported among the various database platforms, so check the platform documentation for more details. Many platforms also support an alternative command called EXECUTE to perform the same functionality. In some cases, you may prefer EXECUTE to CALL, since the former can be used to execute any kind of prepared SQL, including methods, functions, or batches of SQL code. DB2DB2 supports CALL along with DESCRIPTOR capabilities: CALL procedure_name { [(parameter [,...] )] | USING DESCRIPTOR descriptor_name } where:
DB2 allows you to substitute host variables for the procedure name or the parameters, where appropriate. When using the USING DESCRIPTOR clause, the variables must be string variables of 254 or fewer characters. On DB2, any return status issued by the called stored procedure is returned to the SQLERRD field of the SQLCA (SQL Communications Area). You can also use the GET DIAGNOSTIC statement to retrieve the RETURN_STATUS value (-1 indicates error) or row count. MySQLNot supported. OracleOracle allows the CALL statement to invoke standalone stored procedures, functions, and methods, as well as stored procedures and functions contained within a type or package. Following is the Oracle syntax: CALL [schema.][{type_name | package_name}.]procedure_name@dblink [(parameter [,...] )] [INTO :variable_name [[INDICATOR] :indicator_name] ] where:
The parameters used in an Oracle CALL statement may not include pseudocolumns or the VALUE or REF functions. You must use a host variable for any parameter that corresponds to an OUT or IN OUT argument of the called stored procedure. PostgreSQLNot Supported. SQL ServerNot supported. Instead, use the non-ANSI EXECUTE statement. See Also
The CLOSE CURSOR statement closes a server-side cursor created with a DECLARE CURSOR statement.
SQL2003 SyntaxCLOSE cursor_name Keywords
Rules at a GlanceThe CLOSE statement closes a cursor and destroys the cursor result set. All the database platforms release any locks that were held by the cursor, though this is not specified in the ANSI standard. For example: CLOSE author_names_cursor; Programming Tips and GotchasYou can also close a cursor implicitly using a COMMIT statement or, for cursors defined WITH HOLD, using a ROLLBACK statement. DB2DB2 supports an added optional WITH clause on the CLOSE statement: CLOSE cursor_name [WITH RELEASE] where:
The WITH RELEASE clause is ignored on cursors run under isolation level 3 CS or UR, when the cursor is defined in a method, when the cursor is defined in a function, or when the cursor is defined in a stored procedure that is called by a function or method. WITH RELEASE may allow phantom read and nonrepeatable read anomalies when run under isolation levels RS or RR. For a detailed explanation of isolation levels, including phantom reads and nonrepeatable reads, refer to SET TRANSACTION ISOLATION LEVEL. PostgreSQLPostgreSQL supports the ANSI standard syntax. PostgreSQL issues an implicit CLOSE statement for every open cursor when a transaction is ended with a COMMIT or ROLLBACK statement. SQL ServerMicrosoft SQL Server supports the ANSI standard syntax, and an additional GLOBAL keyword: CLOSE [GLOBAL] cursor_name where:
In the ANSI standard behavior, closing a cursor destroys the cursor result set. Locking is a physical feature of each database platform and thus not a part of the ANSI SQL definition. However, all the database platforms covered here drop the locks taken up by the cursor. Another physical implementation detail is that SQL Server does not automatically reallocate memory structures consumed by a cursor to the memory pool. To accomplish such reallocation, you must issue a DEALLOCATE cursor_name command. This example from Microsoft SQL Server opens a cursor and fetches a result set of all employees who have a last name starting with "K": DECLARE employee_cursor CURSOR FOR SELECT lname, fname FROM pubs.dbo.employee WHERE lname LIKE 'K%' OPEN employee_cursor FETCH NEXT FROM employee_cursor WHILE @@FETCH_STATUS = 0 BEGIN FETCH NEXT FROM employee_cursor END CLOSE employee_cursor DEALLOCATE employee_cursor GO See Also
The COMMIT statement explicitly ends an open transaction and makes the changes permanent in the database. Transactions can be opened implicitly as part of an INSERT, UPDATE, or DELETE statement, or opened explicitly with a START statement. In either case, an explicitly issued COMMIT statement will end the open transaction.
SQL2003 SyntaxCOMMIT [WORK] [AND [NO] CHAIN] Keywords
Rules at a GlanceFor simple operations, you will execute transactions (that is, SQL code that manipulates or changes data and objects in a database) without explicitly declaring a transaction. However, all transactions are best managed by explicitly closing them with a COMMIT statement. Because records and even entire tables can be locked for the duration of a transaction, it is extremely important that transactions are completed as quickly as possible. Thus, manually issuing a COMMIT statement with a transaction can help control user concurrency issues and locking problems on the database. Programming Tips and GotchasThe most important gotcha to consider is that some database platforms perform automatic and implicit transactions, while others require explicit transactions. If you assume a platform uses one method of transactions over the other, you may get bitten. Thus, when moving between database platforms you should follow a standard, preset way of addressing transactions. We recommend always using explicit transactions with START TRAN, on database platforms that support it, to begin a transaction, and COMMIT or ROLLBACK to end a transaction. In addition to finalizing a single or group of data-manipulation operation(s), COMMIT has some interesting effects on other aspects of a transaction. First, it closes any associated open cursors. Second, any temporary table(s) specified with ON COMMIT DELETE ROWS (an optional clause of the CREATE TABLE statement) are cleared of data. Third, all deferred constraints are checked. If any deferred constraints are violated, the transaction is rolled back. Finally, all locks opened by the transaction are released. Please note that SQL2003 dictates that transactions are implicitly opened when one of the following statements is executed:
If you did not explicitly open a transaction when you started one of the commands listed above, the standard dictates that the DBMS platform open a transaction for you. DB2DB2 supports the standard, but not the AND [NO] CHAIN clause. MySQLMySQL supports COMMIT and transactions only with InnoDB tables. It does not support the AND [NO] CHAIN clause. OracleOracle supports the standard, but not the AND [NO] CHAIN clause. In addition, Oracle has a couple of extensions to the standard clause: COMMIT [WORK] [ {COMMENT 'text' | FORCE 'text' [, int]} ]; where:
Issuing a COMMIT statement with the FORCE clause will commit only the transaction explicitly identified in the FORCE clause. It will not affect the current transaction unless it is explicitly identified. The FORCE clause is not usable in PL/SQL statements. The following example commits a transaction while associating a comment with the transaction: COMMIT WORK COMMENT 'In-doubt transaction, Call (949) 555-1234'; PostgreSQLPostgreSQL implements the following syntax: COMMIT [WORK | TRANSACTION]; In PostgreSQL, both the WORK and TRANSACTION keywords are optional. When you issue a COMMIT, all open transactions are written to disk and the results of those transactions then become visible to other users. For example: INSERT INTO sales VALUES('7896','JR3435','Oct 28 1997',25, 'Net 60','BU7832'); COMMIT WORK; SQL ServerSQL Server does support the AND [NO] CHAIN clause. SQL Server supports the keyword TRANSACTION as an equivalent to WORK, as in the following syntax: COMMIT { [TRAN[SACTION] [transaction_name] ] | [WORK] } Microsoft SQL Server allows the creation of a specific, named transaction using the START TRAN statement. The COMMIT TRANSACTION syntax allows you to specify an explicit, named transaction to close or to store a transaction name in a variable. Curiously, SQL Server still commits only the most recently opened transaction, despite the name of the transaction that is specified. When you issue COMMIT WORK, SQL Server terminates all open transactions and writes their changes to the database. You may not specify a transaction name when using COMMIT WORK. See Also
The CONNECT statement establishes a connection to the DBMS and to a specific database within the DBMS.
SQL2003 SyntaxCONNECT TO {DEFAULT | {[server_name] [AS connection_name] [USER user_name ] } Keywords
Rules at a GlanceIssue the CONNECT statement to open an interactive SQL session with a DBMS. The period between issuing the CONNECT and DISCONNECT statements is commonly called a session. Typically, you will complete all work on a DBMS during an explicitly invoked session. If you do not specify the server name, the connection name, or the username, then the platform DBMS will provide default values for you. The defaults vary from platform to platform. To connect to the "houston" server under a specific user ID, you might issue the command: CONNECT TO houston USER pubs_admin If the DBMS requires named connections, you might use the following, alternative syntax: CONNECT TO houston USER pubs_admin AS pubs_administrative_session; If you want just a simple, short-term connection then you might use the default: CONNECT TO DEFAULT; Programming Tips and GotchasIf the CONNECT statement is invoked without explicitly disconnecting, the old session becomes dormant and the new session becomes active. You can then switch between connections using the SET CONNECTION statement.
DB2DB2 supports a couple of variations to the ANSI standard: CONNECT { [TO] {[server_name] [IN SHARE MODE | IN EXCLUSIVE MODE [ON SINGLE DBPARTITIONNUM] | [RESET] } [USER user_name ] { [USING password] | [NEW password CONFIRM password]} where:
For example, we might want to change the password for the user account sam to mephrun using a VARCHAR(8) host variable, app_server, to store the value of the application server: CONNECT TO :app_server USER sam NEW 'mephrun' CONFIRM 'mephrun'; MySQLNot supported. OracleThe CONNECT clause allows a database connection as a specific username. Alternately, a connection can be established for special privileges with AS SYSOPER or AS SYSDBA. The Oracle syntax is: CONN[ECT] [[username/password] {AS {SYSOPER | SYSDBA} ] ] where:
If another connection is already open, CONNECT commits any open transactions, closes the current session, and opens the new one. Oracle also allows the CONNECT statement in its SQL*Plus and iSQL*Plus tools. PostgreSQLPostgreSQL does not explicitly support the CONNECT statement. However, it does support the statement SPI_CONNECT under the Server Programming Interface and PG_CONNECT under the PG/TCL programming package. SQL ServerSQL Server supports the basic elements of the CONNECT statement within Embedded SQL (inside of C++ or Visual Basic programs), as in the following: CONNECT TO [server_name.]database_name [AS connection_name] USER {login_name[.password] | $integrated} where:
For example, we can connect to the server named new_york as the Windows login pubs_admin: CONNECT TO new_york.pubs USER pubs_admin To issue the same command under SQL Server standard security: EXEC SQL CONNECT TO new_york.pubs USER pubs_admin To issue the same command under Windows integrated security: EXEC SQL CONNECT TO new_york.pubs USER $integrated To switch to a different connection, you should use the SET CONNECTION statement. See Also
The ANSI standard does not actually contain a CREATE DATABASE statement. However, since it is nearly impossible to operate a SQL database without this command, we've added CREATE DATABASE here. Almost all database platforms support some version of this command.
Rules at a GlanceThis command creates a new, blank database with a specific name. Most DBMS platforms require the user to possess administrator privileges in order to create a new database. Once the new database is created, you can populate it with database objects (such as tables, views, triggers, and so on) and populate the tables with data. Depending on the platform, CREATE DATABASE may also create corresponding files on the filesystem that contain the data and metadata of the database. Programming Tips and GotchasSince CREATE DATABASE is not an ANSI command, it is prone to rather extreme variation in syntax between platforms. DB2The DB2 statement CREATE DATABASE initializes a new database with a variety of user-defined characteristics, such as collation and autoconfiguration options. In addition, the statement also creates the three initial tablespaces, system tables, and recovery logs required by a DB2 database: CREATE {DATABASE | DB} database_name [AT DBPARITIONNUM] [ON path_and_drive] [ALIAS database_alias] [USING CODESET codeset TERRITORY territory] [COLLATE USING {COMPATIBILTIY | IDENTITY | NLSCHAR | SYSTEM} ] [NUMSEGS int] [DFT_EXTENT_SZ int] [CATALOG TABLESPACE tablespace_definition] [USER TABLESPACE tablespace_definition] [TEMPORARY TABLESPACE tablespace_definition] [WITH "comments"] [AUTOCONFIGURE [USING param value [...] ] [APPLY {DB ONLY | DB AND DBM | NONE} ] where:
The tablespace_definition used by DB2 has its own syntax as well: MANAGED BY {SYSTEM USING ('container' [,...] ) | DATABASE USING ({FILE | DEVICE} 'container' int [,...] ) } [EXTENTSIZE int] [PREFETCHSIZE int] [OVERHEAD int] [TRANSFERRATE int] where:
Note that DB2 tablespaces may also be created using the DB2-specific command CREATE TABLESPACE. On DB2 servers that are enabled to support Lightweight Directory Access Protocol (LDAP), the new database is automatically registered in the LDAP directory.
The following example creates a database called sales with specifically declared NUMSEGS, DFT_EXTENT_SZ, and AUTOCONFIGURATION enabled and applied: CREATE DATABASE sales NUMSEGS 200 DFT_EXTENT_SZ 1000 WITH "Temporary sales database" AUTOCONFIGURE APPLY DB ONLY; The WITH clause adds a description of the sales database to the database directory. MySQLIn MySQL, CREATE DATABASE essentially creates a new directory that holds the database objects: CREATE DATABASE [IF NOT EXISTS] database_name where:
For example, we could create a database called sales_archive on a MySQL server. If the database already exists, we want to abort the command without causing an error: CREATE DATABASE IF NOT EXISTS sales_archive Any tables that we create in the sales_archive database will now appear as files within the sales_archive directory.
OracleOracle provides an extraordinary level of control over database file structures beyond merely naming the database and specifying a path for the database files. CREATE and ALTER DATABASE are very powerful commands in Oracle. Some of the more sophisticated clauses are best used only by experienced DBAs. These commands can be very large and complex. (ALTER DATABASE alone consumes over 50 pages in the Oracle vendor documentation!) Novices should be aware that CREATE DATABASE, when run, erases all data in the specified datafiles that are already in existence. Similarly, all the data in an existing database will be lost. Following is the syntax to create a new database in Oracle: CREATE DATABASE [database_name] {[USER SYS IDENTIFIED BY password | USER SYSTEM IDENTIFIED BY password]} [CONTROLFILE REUSE] {[LOGFILE definition [,...] ] [MAXLOGFILES int] [[MAXLOGMEMBERS] int] [[MAXLOGHISTORY] int] [{ARCHIVELOG | NOARCHIVELOG}] [FORCE LOGGING]} [MAXDATAFILES int] [MAXINSTANCES int] [CHARACTER SET charset] [NATIONAL CHARACTER SET charset] [EXTENT MANAGEMENT {DICTIONARY | LOCAL}] [DATAFILE definition [,...] ] [SYSAUX DATAFILE definition [,...] ] [DEFAULT TABLESPACE tablespace_name [DATAFILE file_definition] EXTENT MANAGEMENT {DICTIONARY | LOCAL {AUTOALLOCATE | UNIFORM [SIZE int [K | M] } } ] [ [{ BIGFILE | SMALLFILE}] DEFAULT [TEMPORARY] TABLESPACE tablespace_name [TEMPFILE file_definition] EXTENT MANAGEMENT {DICTIONARY | LOCAL {AUTOALLOCATE | UNIFORM [SIZE int [K | M] } } ] [ [{ BIGFILE | SMALLFILE}] UNDO TABLESPACE tablespace_name [DATAFILE temp_datafile_definition] ] [SET TIME_ZONE = ' { {+ | -} hh:mi | time_zone_region}'] [SET DEFAULT {BIGFILE | SMALLFILE} TABLESPACE] Following is the syntax to alter an existing database: ALTER DATABASE [database_name]
[ARCHIVELOG | NOARCHIVELOG] |
{MOUNT [{STANDBY | CLONE} DATABASE] |
OPEN [READ WRITE | READ ONLY
[RESETLOGS | NORESETLOGS] | [UPGRADE | DOWNGRADE] } |
{ACTIVATE [PHYSICAL | LOGICAL] STANDBY DATABASE
[SKIP [STANDBY LOGFILE] ] |
SET STANDBY [DATABASE] TO MAXIMIZE {PROTECTION | AVAILABILITY |
PERFORMANCE} |
REGISTER [OR REPLACE] [PHYSICAL | LOGICAL] LOGFILE ['file']
[FOR logminer_session_name] |
{COMMIT | PREPARE} TO SWITCHOVER TO
{{[PHYSICAL | LOGICAL] PRIMARY | STANDBY} [WITH[OUT]
SESSION SHUTDOWN] [WAIT | NOWAIT]} |
CANCEL} |
START LOGICAL STANDBY APPLY [IMMEDIATE] [NODELAY]
[{INITIAL int | NEW PRIMARY dblink_name | {FINISH |
SKIP FAILED TRANSACTION] } ] |
{STOP | ABORT} LOGICAL STANDBY APPLY |
[[NO]PARALLEL int] } |
{RENAME GLOBAL_NAME TO database[.domain[.domain ...]] |
CHARACTER SET character_set |
NATIONAL CHARACTER SET character_set |
DEFAULT TABLESPACE tablespace_name |
DEFAULT TEMPORARY TABLESPACE {GROUP int | tablespace_name} |
{DISABLE BLOCK CHANGE TRACKING | ENABLE BLOCK CHANGE TRACKING [USING
FILE 'file' [REUSE] } |
FLASHBACK {ON | OFF} |
SET TIME_ZONE = '{ {+ | -} hh:mi | time_zone_region}' |
SET DEFAULT {BIGFILE | SMALLFILE} TABLESPACE } |
{ENABLE | DISABLE} { [PUBLIC] THREAD int | INSTANCE 'instance_name' } |
{GUARD {ALL | STANDBY | NONE} } |
{CREATE DATAFILE 'file' [,...] [AS {NEW | file_definition [,...] } ] |
{DATAFILE 'file' | TEMPFILE 'file'} [,...]
{ONLINE | OFFLINE [FOR DROP | RESIZE int [K | M] |
END BACKUP | AUTOEXTEND {OFF | ON [NEXT int [K | M] [MAXSIZE
[UNLIMITED | int [K | M] } |
{TEMPFILE 'file' | TEMPFILE 'file'} [,...]
{ONLINE | OFFLINE | DROP [INCLUDING DATAFILES] |
RESIZE int [K | M] | AUTOEXTEND {OFF | ON [NEXT int [K | M]
[MAXSIZE [UNLIMITED | int [K | M] } |
RENAME FILE 'file' [,...] TO 'new_file_name' [,...] } |
{[[NO] FORCE LOGGING] | [ [NO]ARCHIVELOG [MANUAL] ] |
[ADD | DROP] SUPPLEMENTAL LOG DATA [(ALL | PRIMARY KEY | UNIQUE |
FOREIGN KEY) [,...] ] COLUMNS |
[ADD | DROP] [STANDBY] LOGFILE
{{[THREAD int | INSTANCE 'instance_name']} {[GROUP int |
logfile_name [,...]} [SIZE [K | M]] | [REUSE] |
[MEMBER] 'file' [REUSE] [,...] TO logfile_name [,...]} |
ADD LOGFILE MEMBER 'file' [RESUSE] [,...] TO {[GROUP int |
logfile_name [,...]} |
DROP [STANDBY] LOGFILE {MEMBER 'file' | {[GROUP int | logfile_name
[,...]} }
CLEAR [UNARCHIVED] LOGFILE {[GROUP int | logfile_name [,...]} [,...]
[UNRECOVERABLE DATAFILE] } |
{CREATE [LOGICAL | PHYSICAL] STANDBY CONTROLFILE AS 'file' [REUSE] |
BACKUP CONTROLFILE TO
{'file' [REUSE] | TRACE [AS 'file' [REUSE] ] {RESETLOGS |
NORESETLOGS]} } |
{RECOVER
{[AUTOMATIC [FROM 'location'] ] |
{[STANDBY] DATABASE
{[UNTIL {CANCEL | TIME date | CHANGE int}] |
USING BACKUP CONTROLFILE } |
{{[STANDBY] {TABLESPACE tablespace_name [,...] | DATAFILE
'file' [,...] } [UNTIL [CONSISTENT WITH] CONTROLFILE]} |
TABLESPACE tablespace_name [,...] | DATAFILE 'file' [,...]} |
LOGFILE filename [,...] } [{TEST | ALLOW int CORRUPTION | [NO]PARALLEL
int}]} |
CONTINUE [DEFAULT] |
CANCEL [IMMEDIATE][NOWAIT] }
{MANAGED STANDBY DATABASE
{[[NO]TIMEOUT int] [{[NO]DELAY int | DEFAULT DELAY}] [NEXT int]
[[NO]EXPIRE int] [{[NO]PARALLEL int}] [USING CURRENT LOGFILE]
[UNTIL CHANGE int]
[THROUGH
{ALL ARCHIVELOG | {ALL | LAST | NEXT} SWITCHOVER |
[[THREAD int] SEQUENCE int ] } |
[CANCEL [IMMEDIATE] [{WAIT | NOWAIT}] ] |
[DISCONNECT [FROM SESSION] [{[NO]PARALLEL int}]
FINISH [SKIP [STANDBY LOGFILE]] [{WAIT | NOWAIT}] ] | {BEGIN | END} BACKUP} } The syntax elements in Oracle are as follows:
After that long discussion of specific syntax, it's important to establish some Oracle basics. Oracle allows the use of primary and standby databases. A primary database is a mounted and open database accessible to users. The primary database then ships its redo logs to a standby database where they are recovered, thus making the standby database a very up-to-date copy of the primary. Unique to the Oracle environment is the INIT.ORA file, which specifies the database name and a variety of other options when creating and starting up the database. You should always define startup parameters, such as the name of any control files, in the INIT.ORA file to identify the control files or else the database will not start. Starting in Oracle 9.1, you can use binary parameter files rather than INIT.ORA files. When a group of logfiles is listed, they are usually shown in parentheses. The parentheses aren't needed when creating a group with only one member, but this is seldom done. Here's an example using a parenthetical list of logfiles: CREATE DATABASE publications LOGFILE ('/s01/oradata/loga01','/s01/oradata/loga02') SIZE 5M DATAFILE; The example above creates a database called publications with an explicitly defined logfile clause and an automatically created datafile. The following example of an Oracle CREATE DATABASE command is much more sophisticated: CREATE DATABASE sales_reporting CONTROLFILE REUSE LOGFILE GROUP 1 ('diskE:log01.log', 'diskF:log01.log') SIZE 15M, GROUP 2 ('diskE:log02.log', 'diskF:log02.log') SIZE 15M MAXLOGFILES 5 MAXLOGHISTORY 100 MAXDATAFILES 10 MAXINSTANCES 2 ARCHIVELOG CHARACTER SET AL32UTF8 NATIONAL CHARACTER SET AL16UTF16 DATAFILE 'diskE:sales_rpt1.dbf' AUTOEXTEND ON, 'diskF:sales_rpt2.dbf' AUTOEXTEND ON NEXT 25M MAXSIZE UNLIMITED DEFAULT TEMPORARY TABLESPACE temp_tblspc UNDO TABLESPACE undo_tblspc SET TIME_ZONE = '-08:00'; This example defines log and data files, as well as all appropriate character sets. We also define a few characteristics for the database, such as the use of ARCHIVELOG mode and CONTROLFILE REUSE mode, the time zone, maximum number of instances, datafiles, etc. The example also assumes that the INIT.ORA parameter for DB_CREATE_FILE_DEST has already been set. Thus, we don't have to define file definitions for the DEFAULT TEMPORARY TABLESPACE and UNDO TABLESPACE clauses. When issued by a user with SYSDBA privileges, this statement creates a database and makes it available to users in either exclusive or parallel mode, as defined by the value of the CLUSTER_DATABASE initialization parameter. Any data that exists in predefined datafiles is erased. You will usually want to create tablespaces and rollback segments for the database. (Refer to the vendor documentation for the platform specific commands CREATE TABLESPACE and CREATE ROLLBACK SEGMENT.) Oracle has tightened up security around default database user accounts. Many default database user accounts are now locked and expired during initial installation. Only SYS, SYSTEM, SCOTT, DBSNMP, OUTLN, AURORA$JIS$UTILITY$, AURORA$ORB$UNAUTHENTICATED, and OSE$HTTP$ADMIN are the same as they were in earlier versions. You must manually unlock and assign a new password to all locked accounts, as well as assign a password to SYS and SYSTEM during the initial installation. In the next example, we add more logfiles to the current database, then add a datafile: ALTER DATABASE ADD LOGFILE GROUP 3 ('diskf: log3.sales_arch_log','diskg:log3.sales_arch_log') SIZE 50M; ALTER DATABASE sales_archive CREATE DATAFILE 'diskF:sales_rpt4.dbf' AUTOEXTEND ON NEXT 25M MAXSIZE UNLIMITED; We can set a new default temporary tablespace, as shown in the next example: ALTER DATABASE DEFAULT TEMPORARY TABLESPACE sales_tbl_spc_2; Next, we'll perform a simple full database recovery: ALTER DATABASE sales_archive RECOVER AUTOMATIC DATABASE; In the next example, we perform a more elaborate partial database recovery: ALTER DATABASE RECOVER STANDBY DATAFILE 'diskF:sales_rpt4.dbf' UNTIL CONTROLFILE; Now, we'll perform a simple recovery of a standby database in managed standby recovery mode: ALTER DATABASE RECOVER sales_archive MANAGED STANDBY DATABASE; In the following example, we gracefully switch over from a primary database to a logical standby, and promote the logical standby to primary: -- Demotes the current primary to logical standby database. ALTER DATABASE COMMIT TO SWITCHOVER TO LOGICAL STANDBY; -- Applies changes to the new standby. ALTER DATABASE START LOGICAL STANDBY APPLY; -- Promotes the current standby to primary database. ALTER DATABASE COMMIT TO SWITCHOVER TO PRIMARY; PostgreSQLPostgreSQL's implementation of the CREATE DATABASE command creates a database and a file location for the datafiles: CREATE DATABASE database_name [ WITH {[LOCATION = 'dbpath' ] [TEMPLATE = tmp_name] [ENCODING = enc_value]} ] Where:
For example, to create the database sales_revenue in the /home/teddy/private_db directory: CREATE DATABASE sales_revenue WITH LOCATION = '/home/teddy/private_db'; Be careful using absolute file- and pathnames, since they impact security and data integrity issues. PostgreSQL advises that databases used as templates be treated as read-only. Template databases should not be used for general purpose "copy database" functionality.
SQL ServerSQL Server offers a high degree of control over the OS filesystem structures that hold the database and its objects. The syntax for Microsoft SQL Server's CREATE DATABASE statement looks like this: CREATE DATABASE database_name [ ON file_definition [,...] ] [, FILEGROUP filegroup_name file_definition [,...] ] [ LOG ON file_definition [,...] ] [ COLLATE collation_name] [ FOR LOAD | FOR ATTACH ] Following is the syntax for ALTER DATABASE: ALTER DATABASE database_name {ADD FILE file_definition [,...] [TO FILEGROUP filegroup_name] | ADD LOG FILE file_definition [,...] | REMOVE FILE file_name | ADD FILEGROUP filegroup_name | REMOVE FILEGROUP filegroup_name | MODIFY FILE file_definition | MODIFY NAME = new_database_name | MODIFY FILEGROUP filegroup_name {filegroup_property | NAME = new_filegroup_name } | SET {state_option | cursor_option | auto_option | sql_option | recovery_option} [,...] [ WITH termination_option] | COLLATE collation_name } Parameter descriptions are as follows:
The CREATE DATABASE command should be issued from the master system database. You can, in fact, issue the command CREATE DATABASE database_name, with no other clauses, to get a very small, default database. SQL Server uses files, formerly called devices, to act as a repository for databases. Files are grouped into one or more filegroups, with at least a PRIMARY filegroup assigned to each database. A file is a predefined block of space created on the disk structure. A database may be stored on one or more files or filegroups. SQL Server also allows the transaction log to be placed in a separate location from the database using the LOG ON clause. These functions allow sophisticated file planning for optimal control of disk I/O. For example, we can create a database called sales_report with a data and transaction logfile: USE master GO CREATE DATABASE sales_report ON ( NAME = sales_rpt_data, FILENAME = 'c:\mssql\data\salerptdata.mdf', SIZE = 100, MAXSIZE = 500, FILEGROWTH = 25 ) LOG ON ( NAME = 'sales_rpt_log', FILENAME = 'c:\mssql\log\salesrptlog.ldf', SIZE = 25MB, MAXSIZE = 50MB, FILEGROWTH = 5MB ) GO When a database is created, all objects in the model database are copied into the new database. All of the empty space within the file or files defined for the database is then initialized (i.e., emptied out), which means that creating a new and very large database can take a while, especially on a slow disk. A database always has at least a primary datafile and a transaction logfile, but may also have secondary files for both the data and log components of the database. SQL Server uses default filename extensions: .mdf for primary datafiles, .ndf for secondary files, and .ldf for transaction logfiles. The following example creates a database called sales_archive with several very large files that are grouped into a couple of filegroups: USE master GO CREATE DATABASE sales_archive ON PRIMARY (NAME = sales_arch1, FILENAME = 'c:\mssql\data\archdata1.mdf', SIZE = 100GB, MAXSIZE = 200GB, FILEGROWTH = 20GB), (NAME = sales_arch2, FILENAME = 'c:\mssql\data\archdata2.ndf', SIZE = 100GB, MAXSIZE = 200GB, FILEGROWTH = 20GB), (NAME = sales_arch3, FILENAME = 'c:\mssql\data\archdat3.ndf', SIZE = 100GB, MAXSIZE = 200GB, FILEGROWTH = 20GB) FILEGROUP sale_rpt_grp1 (NAME = sale_rpt_grp1_1_data, FILENAME = ' c:\mssql\data\SG1Fi1dt.ndf', SIZE = 100GB, MAXSIZE = 200GB, FILEGROWTH = 20GB), (NAME = sale_rpt_grp1_1_data, FILENAME = ' c:\mssql\data\SG1Fi2dt.ndf', SIZE = 100GB, MAXSIZE = 200GB, FILEGROWTH = 20GB), FILEGROUP sale_rpt_grp2 (NAME = sale_rpt_grp2_1_data, FILENAME = ' c:\mssql\data\SRG21dt.ndf', SIZE = 100GB, MAXSIZE = 200GB, FILEGROWTH = 20GB), (NAME = sale_rpt_grp2_2_data, FILENAME = ' c:\mssql\data\SRG22dt.ndf', SIZE = 100GB, MAXSIZE = 200GB, FILEGROWTH = 20GB), LOG ON (NAME = sales_archlog1, FILENAME = 'd:\mssql\log\archlog1.ldf', SIZE = 100GB, MAXSIZE = UNLIMITED, FILEGROWTH = 25%), (NAME = sales_archlog2, FILENAME = 'd:\ mssql\log\archlog2.ldf', SIZE = 100GB, MAXSIZE = UNLIMITED, FILEGROWTH = 25%) GO The FOR ATTACH clause is commonly used for situations like a salesperson traveling with a database on a CD. This clause tells SQL Server that the database is attached from an existing operating-system file structure, such as a DVD-ROM, CD-ROM, or portable hard drive. When using FOR ATTACH, the new database inherits all the objects and data of the parent database, not the model database. The following examples show how to change the name of a database, file, or filegroup: -- Rename a database ALTER DATABASE sales_archive MODIFY NAME = sales_history GO -- Rename a file ALTER DATABASE sales_archive MODIFY FILE NAME = sales_arch1, NEWNAME = sales_hist1 GO -- Rename a filegroup ALTER DATABASE sales_archive MODIFY FILEGROUP sale_rpt_grp1 NAME = sales_hist_grp1 GO There may be times when you want to add new free space to a database, especially if you have not enabled it to auto-grow: USE master GO ALTER DATABASE sales_report ADD FILE ( NAME = sales_rpt_added01, FILENAME = 'c:\mssql\data\salerptadded01.mdf', SIZE = 50MB, MAXSIZE = 250MB, FILEGROWTH = 25MB ) GO When you alter a database, you can set many behavior options on the database. State options (shown as state_option in the earlier syntax diagram) control how users access the database. Following is a list of valid state options:
Cursor options control default behavior for cursors in the database. In the ALTER DATABASE syntax shown earlier, you can replace cursor_option with any of the following:
In the SET clause, auto_option controls the automatic file handling behaviors of the database. The following are valid replacements for auto_option:
The sql_options clause controls ANSI compatibility of the database. You can use the standalone SQL Server command SET ANSI_DEFAULTS ON to enable all the ANSI SQL92 behaviors at one time, rather than using the individual statements below. In the SET clause, you can replace sql_option with any of the following:
Recovery options control the recovery model used by the database. Use any of the following in place of recovery_option in the ALTER DATABASE syntax:
For example, we may want to change some behavior settings for the sales_report database without actually changing the underlying file structure: ALTER DATABASE sales_report SET ONLINE, READ_ONLY, AUTO_CREATE_STATISTICS ON GO This statement puts the database online and in read-only mode. It also sets the AUTO_CREATE_STATISTICS behavior to ON. See Also
The CREATE FUNCTION and CREATE PROCEDURE statements are very similar in syntax and coding (as are the respective ALTER statements). The CREATE PROCEDURE statement creates a stored procedure, which takes input arguments and performs conditional processing against various objects in the database. According to the ANSI standard, a stored procedure returns no result set (though it may return a value in an OUTPUT parameter). For example, you might use a stored procedure to perform all the processes that close an accounting cycle. The CREATE FUNCTION statement creates a user-defined function (UDF), which takes input arguments and returns a single value output in the same way as system-supplied functions like CAST( ) or UPPER( ). These functions, once created, can be called in queries and data-manipulation operations, such as INSERT, UPDATE, and the WHERE clause of DELETE statements. Refer to Chapter 4 for descriptions of built-in SQL functions and their individual vendor implementations.
SQL2003 SyntaxUse the following syntax to create a stored procedure or function: CREATE {PROCEDURE | FUNCTION} object_name ( [{[IN | OUT | INOUT] [parameter_name] datatype [AS LOCATOR] [RESULT]} [,...] ] ) [ RETURNS datatype [AS LOCATOR] [CAST FROM datatype [AS LOCATOR] ] [LANGUAGE {ADA | C | FORTRAN | MUMPS | PASCAL | PLI | SQL}] [PARAMETER STYLE {SQL | GENERAL}] [SPECIFIC specific_name] [DETERMINISTIC | NOT DETERMINISTIC] [NO SQL | CONTAINS SQL | READS SQL DATA | MODIFIES SQL DATA] [RETURN NULL ON NULL INPUT | CALL ON NULL INPUT] [DYNAMIC RESULT SETS int] [STATIC DISPATCH] code_block Use the following syntax to alter a pre-existing UDF or stored procedure: ALTER {PROCEDURE | FUNCTION} object_name [( {parameter_name datatype } [,...])] [NAME new_object_name] [LANGUAGE {ADA | C | FORTRAN | MUMPS | PASCAL | PLI | SQL}] [PARAMETER STYLE {SQL | GENERAL}] [NO SQL | CONTAINS SQL | READS SQL DATA | MODIFIES SQL DATA] [RETURN NULL ON NULL INPUT | CALL ON NULL INPUT] [DYNAMIC RESULT SETS int] [CASCADE | RESTRICT] Keywords
The syntax for the parameter declaration is: [{IN | OUT | INOUT}] parameter_name1 datatype, [{IN | OUT | INOUT}] parameter_name2 datatype,[...] To provide an optional parameter_name, make sure the name is unique within the stored procedure. The optional AS LOCATOR subclause is used to validate an external routine with a RETURNS parameter that is a BLOB, CLOB, NCLOB, ARRAY, or user-defined type. If you need to change the datatype of a RETURNS parameter on the fly, use the CAST clause (refer to the function, CAST, in Chapter 4). For example, RETURNS VARCHAR(12) CAST FROM DATE.When used with ALTER, this clause adds parameters to a pre-existing stored procedure. Refer to Chapter 2 for details on datatypes.
Rules at a GlanceWith a user-defined function (UDF), you declare the input arguments and the single output argument that the function passes back out. You can then call the user-defined function just as you would any other function, for example, in SELECT statements, INSERT statements, or the WHERE clause of DELETE statements.
With a stored procedure, you declare the input arguments that go into the stored procedure and the output arguments that come out from it. You invoke a stored procedure using the CALL statement. The content of the code_block must conform to the rules of whatever procedural language is supported by the database platform. Some platforms do not have their own internal procedural language, requiring you to use EXTERNAL code_block constructs. For example, you might want build a user-defined function on Microsoft SQL Server that returns the first and last name of a person as a single string: CREATE FUNCTION formatted_name (@fname VARCHAR(30), @lname VARCHAR(30) ) RETURNS VARCHAR(60) AS BEGIN DECLARE @full_name VARCHAR(60) SET @full_name = @fname + ' ' + @lname RETURN @full_name END You could then use this user-defined function as any other function: SELECT formatted_name(au_fname, au_lname) AS name, au_id AS id FROM authors The SELECT statement in this example will return a result set with the two columns, name and id.
Stored procedures behave similarly. In the following small example, this Microsoft SQL Server stored procedure generates a unique 22-digit value (based on elements of the system date and time) and returns it to the calling process: -- A Microsoft SQL Server stored procedure CREATE PROCEDURE get_next_nbr @next_nbr CHAR(22) OUTPUT AS BEGIN DECLARE @random_nbr INT SELECT @random_nbr = RAND( ) * 1000000 SELECT @next_nbr = RIGHT('000000' + CAST(ROUND(RAND(@random_nbr)*1000000,0))AS CHAR(6), 6) + RIGHT('0000' + CAST(DATEPART (yy, GETDATE( ) ) AS CHAR(4)), 2) + RIGHT('000' + CAST(DATEPART (dy, GETDATE( ) ) AS CHAR(3)), 3) + RIGHT('00' + CAST(DATEPART (hh, GETDATE( ) ) AS CHAR(2)), 2) + RIGHT('00' + CAST(DATEPART (mi, GETDATE( ) ) AS CHAR(2)), 2) + RIGHT('00' + CAST(DATEPART (ss, GETDATE( ) ) AS CHAR(2)), 2) + RIGHT('000' + CAST(DATEPART (ms, GETDATE( ) ) AS CHAR(3)), 3) END GO In this next, and final, ANSI SQL2003 example, we change the name of an existing stored procedure: ALTER PROCEDURE get_next_nbr NAME 'get_next_ID' RESTRICT; Programming Tips and GotchasA key advantage of a stored procedure or function is the fact that it is precompiled, meaning that once created its query plans are already stored in the database. Precompiled routines can often (though not always) be cached in database memory to provide an additional boost in performance. A stored procedure or user-defined function can perform many statements with a single communication to the server, thus reducing network traffic. Implementations of user-defined functions and stored procedures vary widely by platform. Some database platforms do not support internal code_block content. On these platforms, you can only write an external code_block. Be sure to read about the variations and capabilities on each platform. If you execute an ALTER PROCEDURE or FUNCTION statement, dependent objects may become invalid after a change to an object they depend on. Be careful to check all dependencies when altering UDFs or stored procedures on which other UDFs or stored procedures may depend. DB2DB2's implementations of these commands are very close to the ANSI standard. Although the syntax diagrams are long, much of the usage is identical to the ANSI standard. Consequently, the keyword explanation section that follows the syntax diagram is relatively short, since many of the clauses are explained in the SQL standard. DB2 supports several classifications of functions. Each kind of UDF has its own set of allowable syntax, though much of it is shared across the various kinds of UDF. DB2 allows external scalar functions, external table functions, OLE-DB external table functions, sourced (or template) functions, and SQL functions. DB2 supports two kinds of stored procedures: external and SQL. The following syntax is the full syntax to create any sort of stored procedure or a UDF in DB2. It is grouped together here to give you a concise list of clause definitions. However, refer to the sections below for greater detail on the specific allowable syntax for each type of UDF and stored procedure. If you prefer, for example, to see only the allowable syntax for a SQL user-defined function, then skip to the section below. The total available syntax for UDFs and stored procedures follows: CREATE {PROCEDURE | FUNCTION} object_name [( {[IN | OUT| INOUT] [parameter_name] datatype } [,...])] RETURNS {datatype | {ROW | TABLE} column_list} [AS LOCATOR] [CAST FROM datatype [AS LOCATOR] ] [LANGUAGE {C | JAVA | COBOL | OLE | SQL] [PARAMETER STYLE {SQL | DB2GENERAL | GENERAL | GENERAL WITH NULLS | JAVA}] [SPECIFIC specific_name] [EXTERNAL NAME external_routine_name] [DETERMINISTIC | NOT DETERMINISTIC] [ [NO] EXTERNAL ACTION] [NO SQL | CONTAINS SQL | READS SQL DATA | MODIFIES SQL DATA] [CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT] [DYNAMIC RESULT SETS int] [STATIC DISPATCH] [SOURCE object_name | AS TEMPLATE ] [INHERIT SPECIAL REGISTERS] [[NOT] FENCED] [[NOT] THREADSAFE] [PROGRAM TYPE {SUB | MAIN}] [ [NO] SCRATCHPAD int] [ [NO] FINAL CALL] [{ALLOW | DISALLOW} PARALLEL] [ [NO] DBINFO] [TRANSFORM GROUP group_name] [CARDINALITY int] [PREDICATES (predicate_definition) ] code_block In addition, use the following syntax to alter a pre-existing UDF or stored procedure, and assign it the characteristics that you declare: ALTER {PROCEDURE | FUNCTION} object_name [EXTERNAL NAME external_routine_name] [ [NOT] FENCED] [ [NOT] THREADSAFE] Following are the parameter descriptions for DB2:
DB2 accommodates several kinds of user-defined functions: external scalar, external table, OLE-DB, source/template, and SQL, and two kinds of stored procedures: external and SQL. Each of these types allows a mix of only some of the clauses shown above. DB2 external scalar user-defined functionsThis variation of the CREATE FUNCTION statement is used to register a user-defined external scalar function with an application server. (Remember that scalar functions return a single value each time they are invoked and are valid wherever SQL expressions are valid.) An external scalar UDF allows the following clauses: CREATE FUNCTION object_name (parameter_name datatype [,...]) [AS LOCATOR] RETURNS datatype [AS LOCATOR] [CAST FROM datatype [AS LOCATOR] ] [LANGUAGE {C | JAVA | OLE} ] [PARAMETER STYLE {SQL | DB2GENERAL | JAVA}] [SPECIFIC specific_name] [EXTERNAL NAME external_routine_name] [DETERMINISTIC | NOT DETERMINISTIC] [STATIC DISPATCH] [ [NO] EXTERNAL ACTION] [NO SQL | CONTAINS SQL | READS SQL DATA] [CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT] [INHERIT SPECIAL REGISTERS] [ [NOT] FENCED [{THREADSAFE | NOT THREADSAFE}] ] [ [NO] SCRATCHPAD int] [ [NO] FINAL CALL] [{ALLOW | DISALLOW} PARALLEL] [ [NO] DBINFO] [TRANSFORM GROUP group_name] [PREDICATES (predicate_definition) ] code_block The following code is an example of an external scalar UDF, called report_reader.center, that defines the center of a circle. The statement explicitly declares all option clauses, even those that have identical defaults to the declared values: CREATE FUNCTION report_reader.center (FLOAT, FLOAT, FLOAT) RETURNS DECIMAL(8,4) CAST FROM FLOAT SPECIFIC center04 EXTERNAL NAME 'center!focalpt' LANGUAGE C PARAMETER STYLE SQL DETERMINISTIC FENCED NOT NULL CALL NO SQL NO EXTERNAL ACTION SCRATCHPAD NO FINAL CALL DISALLOW PARALLEL Note that the report_reader.center UDF uses a scratch pad and can accumulate and act on results stored there DB2 external table user-defined functionsThis variation of the CREATE FUNCTION statement is used to register a user-defined external table function with an application server. (A table function can be used in the FROM clause of a SELECT statement, returning a table to the SELECT one row at a time.) An external table UDF allows the following clauses: CREATE FUNCTION object_name (parameter_name datatype } [AS LOCATOR] [,...]) RETURNS TABLE datatype [AS LOCATOR] [,...] ] [LANGUAGE {C | JAVA | OLE} ] [PARAMETER STYLE {SQL | DB2GENERAL}] [SPECIFIC specific_name] [EXTERNAL NAME external_routine_name] [DETERMINISTIC | NOT DETERMINISTIC] [ [NO] EXTERNAL ACTION] [NO SQL | CONTAINS SQL | READS SQL DATA] [CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT] [INHERIT SPECIAL REGISTERS] [ [NOT] FEDERATED] [ [NOT] FENCED [{THREADSAFE | NOT THREADSAFE}] ] [ [NO] SCRATCHPAD int] [ [NO] FINAL CALL] [DISALLOW PARALLEL] [ [NO] DBINFO] [TRANSFORM GROUP group_name] [CARDINALITY int] code_block Following is an example external table UDF that retrieves message header information and some message text from Microsoft Exchange: CREATE FUNCTION MAIL( ) RETURNS TABLE (time_rcvd DATE, subjct VARCHAR(15), lngth INTEGER, txt VARCHAR(30)) EXTERNAL NAME 'mail.header!list' LANGUAGE OLE PARAMETER STYLE SQL NOT DETERMINISTIC FENCED CALLED ON NULL INPUT SCRATCHPAD FINAL CALL NO SQL EXTERNAL ACTION DISALLOW PARALLEL Note that the external function, mail.header!list, should already exist. DB2 OLE-DB external table user-defined functionsThis variation of the CREATE FUNCTION statement registers a user-defined OLE-DB external table function to access data from an OLE-DB provider. The table function may then be used in the FROM clause of a SELECT statement. This is useful to link to non-DB2 sources of relational data. An OLE-DB external table UDF allows the following clauses: CREATE FUNCTION object_name (parameter_name datatype ) [,...]) RETURNS TABLE datatype [,...] ] [SPECIFIC specific_name] [LANGUAGE OLEDB] [EXTERNAL NAME external_routine_name] [DETERMINISTIC | NOT DETERMINISTIC] [ [NO] EXTERNAL ACTION] [STATIC DISPATCH] [CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT] [CARDINALITY int] code_block The following example registers an OLE-DB table function that retrieves order information from the Microsoft Access database called Northwind: CREATE FUNCTION orders ( ) RETURNS TABLE (orderid INTEGER, customerid CHAR(5), employeeid INTEGER, orderdate TIMESTAMP, requireddate TIMESTAMP, shippeddate TIMESTAMP, shipvia INTEGER, freight DEC(19,4)) LANGUAGE OLEDB EXTERNAL NAME '!orders!Provider=Microsoft.Jet.OLEDB.3.51; Data Source=c:\sqllib\samples\oledb\nwind.mdb'; Note that the connection string is defined in the external name of the function. DB2 sourced user-defined functionsThis variation of the CREATE FUNCTION statement registers a user-defined function that is based on another existing scalar or column function. This statement can also be used to register a function template with an application server that acts as a federated server. (A function template is a partial function that has no executable code, but instead uses it to map to a data source function. Once the mapping is created, the user specifies the function template in queries to the federated server. The federated server will invoke the data source function and return values that correspond to those in the RETURNS portion of the template definition.) A source/template UDF allows the following clauses: CREATE FUNCTION object_name (parameter_name datatype [,...]) RETURNS datatype [,...] ] [SOURCE {object_name [input_argument [,...] ] | [SPECIFIC specific_name] ] [AS TEMPLATE [DETERMINISTIC | NOT DETERMINISTIC] [ [NO] EXTERNAL ACTION] ] code_block For example, the following sourced UDF is based on the report_reader.center scalar function created earlier in this section. The following function, called fn_center, accepts only integer arguments: CREATE FUNCTION fn_center (INTEGER, INTEGER) RETURNS FLOAT SOURCE report_reader.center (INTEGER, FLOAT); In the next example, using a federated server, we can invoke an Oracle UDF, called statistics in the schema scott, that returns table statistics as a double-precision floating point value: CREATE FUNCTION scott.statistics (DOUBLE, DOUBLE) RETURNS DOUBLE AS TEMPLATE DETERMINISTIC NO EXTERNAL ACTION; DB2 SQL user-defined functionsThis variation of the CREATE FUNCTION statement defines a SQL scalar, table, or row function. This application shows the most common form of SQL UDF's in DB2. (Remember a scalar function returns a single value when invoked. A table function may be used in the FROM clause of a SELECT statement and returns a table. A row function returns a row and may be used as a transform function.) A SQL UDF allows the following clauses: CREATE FUNCTION object_name [(parameter_name datatype [,...])] RETURNS {datatype | {ROW | TABLE} column_list} [SPECIFIC specific_name] [LANGUAGE SQL] [DETERMINISTIC | NOT DETERMINISTIC] [ [NO] EXTERNAL ACTION] [CONTAINS SQL | READS SQL DATA] [STATIC DISPATCH] [CALLED ON NULL INPUT] [PREDICATES (predicate_definition) ] code_block Here's an example of a SQL user-defined function that returns a concatenated name from separate fields: CREATE FUNCTION prod.cust_name (in_custno INTEGER ) RETURNS VARCHAR(31) LANGUAGE SQL READS SQL DATA NO EXTERNAL ACTION DETERMINISTIC RETURN SELECT (c.firstnme CONCAT ' ' CONCAT c.midinit CONCAT '. ' CONCAT c.lastname) AS name FROM prod.customer c WHERE c.custno = 10; DB2 external stored procedureThis variation of the CREATE PROCEDURE statement defines an external procedure with a DB2 application server. Usually, external procedures are written in a non-SQL language like C or Java. An external procedure allows the following clauses: CREATE PROCEDURE object_name [( [IN | OUT | INOUT] parameter_name datatype [,...] ) ] [SPECIFIC specific_name] [DYNAMIC RESULT SETS int] [NO SQL | CONTAINS SQL | READS SQL DATA | MODIFIES SQL DATA] [DETERMINISTIC | NOT DETERMINISTIC] [CALLED ON NULL INPUT] LANGUAGE {C | JAVA | COBOL | OLE} EXTERNAL NAME external_routine_name [INHERIT SPECIAL REGISTERS] PARAMETER STYLE {SQL | DB2GENERAL | DB2SQL | GENERAL | GENERAL WITH NULLS | JAVA} [ [NOT] FENCED [{THREADSAFE | NOT THREADSAFE}] ] [PROGRAM TYPE {SUB | MAIN}] [ [NO] DBINFO] code_block Following is an example of an external procedure. In the example, we define a procedure definition in DB2 for a C program that, when passed an author ID, tells us the total books sold, the total sales in dollars, and the book titles the specified author wrote: CREATE PROCEDURE author_sales (IN au_id INTEGER, OUT books_sold INTEGER, OUT total_sales DOUBLE, OUT title VARCHAR(50) ) EXTERNAL NAME 'authors!sales' DYNAMIC RESULT SETS 1 NOT FENCED LANGUAGE C PARAMETER STYLE GENERAL DB2 SQL stored procedureThis variation of the CREATE PROCEDURE statement defines a SQL-based procedure with a DB2 application server. A SQL procedure allows the following clauses: CREATE PROCEDURE object_name [( [IN | OUT| INOUT] parameter_name datatype [,...] ) ] [SPECIFIC specific_name] [DYNAMIC RESULT SETS int] [CONTAINS SQL | READS SQL DATA | MODIFIES SQL DATA] [DETERMINISTIC | NOT DETERMINISTIC] [CALLED ON NULL INPUT] [INHERIT SPECIAL REGISTERS] [LANGUAGE SQL] code_block The following SQL stored procedure returns a customer's record based upon a SOUNDEX function on their name and state: CREATE PROCEDURE prod.custlookup (IN in_lastname VARCHAR(15), IN in_st CHARACTER(2) ) DYNAMIC RESULT SETS 1 LANGUAGE SQL NOT DETERMINISTIC CALLED ON NULL INPUT MODIFIES SQL DATA INHERIT SPECIAL REGISTERS P1: BEGIN DECLARE cursor1 CURSOR WITH RETURN FOR SELECT * FROM prod.customer c WHERE SOUNDEX (c.lastname) = SOUNDEX (in_lastname) AND c.st = UPPER (in_st); OPEN cursor1; END P1; DB2 has recently implemented new procedural extensions to SQL. You can now define the code_block for a DB2 UDF or stored procedure using conditional processing constructs like IF...THEN and WHILE loops (as shown in the examples above). RETURNS TABLE is available as a return value for any LANGUAGE. The only clauses allowed are STATIC DISPATCH, DETERMINISTIC, EXTERNAL ACTION, RETURNS NULL ... | CALLED NULL... ON INPUT, and CARDINALITY. READ SQL DATA and CONTAINS SQL are the only SQL-style options allowed when you chose LANGUAGE SQL. These clauses disable the CAST clause and the RETURNS NULL ON NULL INPUT clause. It also makes available (though optional) the RETURNS {ROW | TABLE} clause, FEDERATED clause, PREDICATES clause, and INHERIT SPECIAL REGISTERS clause. ALTER FUNCTION and PROCEDURE on DB2 merely change the characteristics of an existing UDF or stored procedure. The two examples shown next make a function and a procedure, respectively, NOT FENCED. ALTER FUNCTION mail( ) NOT FENCED; ALTER PROCEDURE employee_pay NOT FENCED; MySQLMySQL does not support the ALTER FUNCTION statement, nor does it support the CREATE PROCEDURE and ALTER PROCEDURE statements. All user-defined functions created on a MySQL database are, in effect, external function calls: CREATE [AGGREGATE] FUNCTION function_name RETURNS {STRING | REAL | INTEGER} SONAME shared_program_library_name ; where:
Once implemented, a MySQL UDF may be called just like any built-in function, such as ABS( ) or SOUNDEX( ). The implementation of CREATE FUNCTION in MySQL is dependent on procedural code in C/C++ under an operating system that supports dynamic loading. The C/C++ program is named in the shared_program_library_name option. The function may be compiled either directly into the MySQL server, making the function permanently available, or as a dynamically callable program. For example, the code behind the UDF created in the following statement might be found on a Unix server: CREATE FUNCTION find_radius RETURNS INT SONAME "radius.so"; OracleOracle supports ALTER and CREATE for both FUNCTION and PROCEDURE object types. (You may also wish to learn about Oracle packages, which can also be used to create UDFs and stored procedures. Check Oracle's documentation.) Oracle's CREATE PROCEDURE syntax is as follows: CREATE [OR REPLACE] {FUNCTION | PROCEDURE} [schema.]object_name [(parameter1 [IN | OUT | IN OUT] [NOCOPY] datatype] [,...] ) ] ] RETURN datatype [DETERMINISTIC] [AUTHID {CURRENT_USER | DEFINER}] [PARALLEL_ENABLE [( PARTITION prtn_name BY {ANY | {HASH | RANGE} (column [,...] ) [{ORDER | CLUSTER} BY (column [,...] ) ] ] { [PIPELINED | AGGREGATE] USING [schema.]implementation_type] | [PIPELINED] {IS | AS} } {code_block | LANGUAGE {JAVA NAME external_program_name | C [NAME external_program_name] LIBRARY lib_name [AGENT IN (argument [,...]) [WITH CONTEXT] [PARAMETERS ( param [,...]) ] }; The ALTER FUNCTION and ALTER PROCEDURE statement shown next is used to recompile invalid UDFs or stored procedures: ALTER {FUNCTION | PROCEDURE} [schema.]object_name COMPILE [DEBUG] [compiler_param = value [...] ] [REUSE SETTINGS] Following are the parameter descriptions:
In Oracle, UDFs and stored procedures are very similar in composition and structure. The primary difference is that a stored procedure cannot return a value to the invoking process, while a function may return a single value to the invoking process. For example, you can pass in the name of a construction project to the following function to obtain the project's profit: CREATE OR REPLACE FUNCTION project_revenue (project IN varchar2) RETURN NUMBER AS proj_rev NUMBER(10,2); BEGIN SELECT SUM(DECODE(action,'COMPLETED',amount,0)) - SUM(DECODE(action,'STARTED',amount,0)) + SUM(DECODE(action,'PAYMENT',amount,0)) INTO proj_rev FROM construction_actions WHERE project_name = project; RETURN (proj_rev); END; In this example, the user-defined function accepts the project name as an argument. Then it processes the project revenue, behind the scenes, by subtracting the starting costs from the completion payment and adding any other payments into the amount. The RETURN (proj_rev); line returns the amount to the invoking process. In Oracle, UDFs cannot be used in the following situations:
Note that when you recompile a routine with the ALTER statement, it is marked valid if no compiler errors are encountered. (It is marked invalid if any errors are encountered.) Perhaps more importantly, any objects that depend upon the recompiled routine are marked invalid, regardless of whether an error occurs. You can either recompile those dependent objects yourself, or you can allow Oracle to take some additional time to recompile them at runtime. By way of example, the following recompiles the project_revenue function and maintains any compiler information for the PL/SQL debugger: ALTER FUNCTION project_revenue COMPILE DEBUG; PostgreSQLPostgreSQL supports the CREATE FUNCTION statement, but not CREATE PROCEDURE, ALTER FUNCTION, or ALTER PROCEDURE. Since PostgreSQL does not have an internal procedural language, user-defined functions are usually external functions, though they may be simple SQL queries. The syntax to use to create a function is: CREATE [OR REPLACE] FUNCTION function_name ( [ parameter [,...] ] ) RETURNS datatype AS {code_block | object_file, link_symbol} LANGUAGE {C | SQL | PLPGSQL | PLTCL | PLTCLU | PLPERL | internal} [WITH {[ISCACHABLE] [,] [ISSTRICT]} ] where:
PostgreSQL also allows function overloading in which the same function name is allowed for different functions, as long as they accept distinct input parameters.
Here's an example of a simple SQL function in PostgreSQL: CREATE FUNCTION max_project_nbr RETURNS int4 AS "SELECT MAX(title_ID) FROM titles AS RESULT" LANGUAGE 'sql'; In this example, we created a UDF that returns the maximum title_ID from the titles table. PostgreSQL uses CREATE FUNCTION as a substitute for CREATE PROCEDURE, as well as to define actions for CREATE TRIGGER. SQL ServerSQL Server supports CREATE and ALTER for both types of routines. By default, SQL Server stored procedures can return result sets and SQL Server UDFs can return single or multirow result sets using the TABLE datatype on RETURNS arguments, contrary to the ANSI standard. However, this makes SQL Server routines more flexible and powerful. Use the following syntax to create a user-defined function or stored procedure: CREATE {FUNCTION | PROCEDURE} [owner_name.]object_name[;int] ( [ {@parameter datatype [VARYING] [=default] [OUTPUT]} [,...] ] ) RETURNS {datatype | TABLE] [WITH {ENCRYPTION | SCHEMABINDING | RECOMPILE | RECOMPILE, ENCRIPTION}] [FOR REPLICATION] [AS] code_block Use the following syntax to alter an existing user-defined function or stored procedure: ALTER {FUNCTION | PROCEDURE} [owner_name.]object_name[;int] ( [ {@parameter datatype [VARYING] [=default] [OUTPUT]} [,...] ] ) RETURNS {datatype | TABLE] [WITH {ENCRYPTION | SCHEMABINDING | RECOMPILE | RECOMPILE, ENCRIPTION}] [FOR REPLICATION] [AS] code_block Following are the parameter descriptions:
Like tables (see CREATE TABLE), local and global temporary stored procedures may be declared by prefixing a pound symbol (#) and double-pound symbol (##) to the name of the procedure, respectively. Temporary procedures exist only for the duration of the user or process session that created them. When that session ends, the temporary procedure automatically deletes itself. A SQL Server stored procedure or UDF may have as many as 1,024 input parameters, specified by the at sign (@). Parameters are defined using SQL Server datatypes. (Parameters of CURSOR datatype must be both VARYING and OUTPUT.) The user or calling process must supply values for any input parameters. However, a default value can be supplied for any input parameter to allow the procedure to execute without a user- or process-supplied value. The default must be a constant or NULL, but it may contain wildcard characters, as discussed later in this chapter in the section LIKE Operator. SQL Server requires that one or more user-supplied parameters be declared for a given user-defined function. All SQL Server datatypes are supported as parameters, except TIMESTAMP. Values returned by the function can be any datatype except TIMESTAMP, TEXT, NTEXT, or IMAGE. If an inline table value is required, the TABLE option without an accompanying column list may be used.
For UDFs, the code_block is either a single SELECT statement for an inline function, in the format RETURN (SELECT...), or a series of Transact-SQL statements following the AS clause for a multistatement operation. When using RETURN (SELECT), the AS clause is optional. Some other rules for SQL Server UDFs:
The following is an example of a scalar function that returns a single value. Once created, the scalar UDF can then be utilized in a query just like a system-supplied function. For example: CREATE FUNCTION metric_volume -- Input dimensions in centimeters. (@length decimal(4,1), @width decimal(4,1), @height decimal(4,1) ) RETURNS decimal(12,3) -- Cubic Centimeters. AS BEGIN RETURN ( @length * @width * @height ) END GO SELECT project_name, metric_volume(construction_height, construction_length, construction_width) FROM housing_construction WHERE metric_volume(construction_height, construction_length, construction_width) >= 300000 GO An inline table-valued UDF supplies values via a single SELECT statement using an AS RETURN clause. For example, we can supply a store ID and find all of their titles: CREATE FUNCTION stores_titles (@stor_id varchar(30)) RETURNS TABLE AS RETURN (SELECT title, qty FROM sales AS s JOIN titles AS t ON t.title_id = s.title_id WHERE s.stor_id = @storeid ) Now, let's alter the UDF just a bit by changing the input argument datatype length and adding another condition to the WHERE clause (indicated in boldface): ALTER FUNCTION stores_titles (@stor_id VARCHAR(4)) RETURNS TABLE AS RETURN (SELECT title, qty FROM sales AS s JOIN titles AS t ON t.title_id = s.title_id WHERE s.stor_id = @storeid AND s.city = 'New York') User-defined functions that return TABLE values are often selected as result set values or are used in the FROM clause of a SELECT statement, just as a regular table is used. These multi-statement, table-valued functions can have very elaborate code bodies since the code_block is composed of many Transact-SQL statements that populate a TABLE return variable. Here is an example invoking a multistatement, table-valued function in a FROM clause. Notice that a table alias is assigned, just as for a regular table: SELECT co.order_id, co.order_price FROM construction_orders AS co, fn_construction_projects('Cancelled') AS fcp WHERE co.construction_id = fcp.construction_id ORDER BY co.order_id GO For stored procedures, the code_block clause contains one or more Transact-SQL commands, up to a maximum size of 128 MB, delimited by BEGIN and END clauses. Some rules about Microsoft SQL Server stored procedures include:
In the following example, a SQL Server stored procedure generates a unique 22-digit value (based on elements of the system date and time) and returns it to the calling process: -- A Microsoft SQL Server stored procedure CREATE PROCEDURE get_next_nbr @next_nbr CHAR(22) OUTPUT AS BEGIN DECLARE @random_nbr INT SELECT @random_nbr = RAND( ) * 1000000 SELECT @next_nbr = RIGHT('000000' + CAST(ROUND(RAND(@random_nbr)*1000000,0)) AS CHAR(6), 6) + RIGHT('0000' + CAST(DATEPART (yy, GETDATE( ) ) AS CHAR(4)), 2) + RIGHT('000' + CAST(DATEPART (dy, GETDATE( ) ) AS CHAR(3)), 3) + RIGHT('00' + CAST(DATEPART (hh, GETDATE( ) ) AS CHAR(2)), 2) + RIGHT('00' + CAST(DATEPART (mi, GETDATE( ) ) AS CHAR(2)), 2) + RIGHT('00' + CAST(DATEPART (ss, GETDATE( ) ) AS CHAR(2)), 2) + RIGHT('000' + CAST(DATEPART (ms, GETDATE( ) ) AS CHAR(3)), 3) END GO See Also
Indexes are special objects built on top of tables that speed many data-manipulation operations, such as SELECT, UPDATE, and DELETE statements. The selectivity of a given WHERE clause and the available query plans the database query optimizer can choose from is usually based upon the quality of indexes that have been placed on the table in a given database. The CREATE INDEX command is not a part of the ANSI SQL standard, and thus varies greatly among vendors.
Common Vendor SyntaxCREATE [UNIQUE] INDEX index_name ON table_name (column_name [, ...]) Keywords
Rules at a GlanceIndexes are created on tables to speed data manipulation operations that go against the tables such as those in a WHERE or JOIN clause. Indexes may also speed other operations, like:
After creating a table, you can create indexes on columns within the table. It is a good idea to create indexes on columns that are frequently part of the WHERE clause or JOIN clause of the queries that go against a table. For example, the following statement creates an index on a column in the sales table that is frequently used in the WHERE clause of queries against that table: CREATE INDEX ndx_ord_date ON sales(ord_date); In another case, we want to set up the pub_name and country as a unique index on the publishers table: CREATE UNIQUE INDEX unq_pub_id ON publishers(pub_name, country); Since the index is unique, any new record entered into the publishers table must have a unique combination of publisher name and country.
Programming Tips and GotchasConcatenated indexes are most useful when queries address the columns of the index starting from the left. If you omit left-side columns in a query against a concatenated key, the query may not perform as well. For example, assume that we have a concatenated index on (last_name, first_name). If we query only by first_name, the concatenated index that starts with last_name and includes first_name may not be any good to us. Some of the vendor platforms have advanced their query engines to the point where this is much less of a problem.
You should be aware that there are situations in which too many indexes can actually slow down system performance. In general, indexes greatly speed lookup operations, especially in SELECT statements, against a table or view. However, every index you create adds overhead whenever you perform an UPDATE, DELETE, or INSERT operation because the database must then update all dependent indexes with the values that have changed in the table. As a rule of thumb, six to twelve indexes are about the most you might want to create on a single table. In addition, indexes take up extra space within the database. The more columns in an index, the more space it consumes. This is not usually a problem, but sometimes catches novices off guard when developing a new database. Most databases use indexes to create statistical samplings, usually just called statistics, so that the query engine can quickly determine which, if any, index or combination of indexes will be most useful for a query. These indexes are always fresh and useful when the index is first created, but may become stale and less useful over time as records are deleted, updated, and inserted within the table. Consequently, indexes, like day-old bread, are not guaranteed to be useful as they age. You need to be sure to refresh, rebuild, and maintain your databases regularly to keep index statistics fresh. DB2DB2 supports the CREATE INDEX statement, but not the ALTER INDEX statement. (DB2 does have the COMMENT ON INDEX statement to allow users to update the comment for an index without dropping and recreating the index. Also, DB2 has a RENAME INDEX statement to rename an index with having to drop and recreate it.) The CREATE INDEX statement has additional clauses that allow partitioning based upon indexed columns, as well as other clauses that impact how data is physically arranged on disk: CREATE [UNIQUE] INDEX index_name ON table_name ({column_name [ASC | DESC]} [, ...]) [SPECIFICATION ONLY] [INCLUDE (column_name [ASC | DESC] [,...] )] [{CLUSTER | EXTEND USING index_extension [(constant [,...]) ] } ] [PCTFREE int] [MINPCTUSED int] [[DIS]ALLOW REVERSE SCANS] [COLLECT [SAMPLED [DETAILED] ] STATISTICS] where:
DB2 will not create a new index where an identical index exists. DB2 considers an index identical to another when one index contains the same columns in the same order as the other. DB2 allows online use of a table while a new index is being created. Read access is always available during the index creation process, but write access is temporarily locked at the end of the index build process. Use the DB2 command LOCK TABLE to temporarily lock the table and avoid read access. You should also use the DB2 command RUNSTATS to update index statistics whenever a great deal of data has changed or after an index is created without the COLLECT STATISTICS clause included in the statement. The following two index creation examples are functionally the same: -- collect basic index statistics at index creation CREATE INDEX ndx_ord_date ON sales(ord_date) COLLECT STATISTICS; -- collect index statistics after index creation LOCK TABLE sales IN EXCLUSIVE MODE; CREATE INDEX ndx_ord_date ON sales(ord_date); RUNSTATS ON TABLE sales AND INDEX ndx_ord_date; Now, we'll collect detailed statistics using sampling on the publishers table: CREATE UNIQUE INDEX unq_pub_id ON publishers(pub_name, country) COLLECT SAMPLED DETAILED STATISTICS; You may wish to create an index on a nicknamed object. In the example below, the publishers nickname represents the current_publishers table: CREATE UNIQUE INDEX unq_pub_id ON publishers(pub_name, country) SPECIFICATION ONLY; The following example shows how to create an extended indextype on a structured type column. The coordinate_positions UDT requires a literal value to maintain the satellite_images column: CREATE INDEX satellite_images ON gis_sites(coordinates) EXTEND USING (coordinate_positions (x'00010010010000100020'));
MySQLMySQL supports a form of the CREATE INDEX statement, but not the ALTER INDEX statement. MySQL indexes are always stored in B-tree structures on the filesystem. Strings within an index are automatically prefix- and end-space-compressed. MySQL's CREATE INDEX syntax is: CREATE [UNIQUE | FULLTEXT] INDEX index_name ON table_name (column_name(length) [,...]) where:
MySQL supports the basic industry standard syntax for the CREATE INDEX statement. Interestingly, MySQL also lets you build an index on the first length characters of a CHAR or VARCHAR column. MySQL requires the (length) clause for BLOB and TEXT columns. Specifying a length can be useful when selectivity is sufficient in the first, say, 10 characters of a column, and in those situations where saving disk space is very important. For example: CREATE UNIQUE INDEX unq_pub_id ON publishers(pub_name(25), country(10)) This example indexes only the first 25 characters of the pub_name column and the first 10 characters of the country column. As a general rule, MySQL allows at least 16 keys per table with a total maximum length of at least 256 bytes. This can vary by storage engine, however. OracleOracle allows the creation of indexes on tables, partitioned tables, clusters, and index-organized tables, as well as on scalar type object attributes of a typed table or cluster, and on nested table columns using the CREATE INDEX statement. Oracle also allows several types of indexes, including normal B-tree indexes, BITMAP indexes (useful for columns that have each value repeated 100 or more times), partitioned indexes, function-based indexes (based on an expression rather than a column value), and domain indexes.
Oracle also supports the ALTER INDEX statement. It is used to change or rebuild an existing index without forcing the user to drop and recreate the index. Oracle's CREATE INDEX syntax is: CREATE [UNIQUE | BITMAP] INDEX index_name {ON {table_name ( {column | expression} [ASC | DESC] [,...]) [{INDEXTYPE IS index_type [PARALLEL [int] | NOPARALLEL] [PARAMETERS ('values') }] | CLUSTER cluster_name | FROM table_name WHERE condition [LOCAL partitioning] } [ {LOCAL partitioning | GLOBAL partitioning} ] [physical_attributes_clause] [{LOGGING | NOLOGGING}] [ONLINE] [COMPUTE STATISTICS] [{TABLESPACE tablespace_name | DEFAULT}] [{COMPRESS int | NOCOMPRESS}] [{NOSORT | SORT}] [REVERSE] [{PARALLEL [int] | NOPARALLEL}] The syntax for ALTER INDEX is: ALTER INDEX index_name { {ENABLE | DISABLE} | UNUSABLE | RENAME TO new_index_name | COALESCE | [NO]MONITORING USAGE | UPDATE BLOCK REFERENCES | PARAMETERS ( 'ODCI_params') | alter_index_partitioning_clause | rebuild_clause | [DEALLOCATE UNUSED [KEEP int [K | M | G | T] ] [ALLOCATE EXTENT ( [SIZE int [K | M | G | T] ] [DATAFILE 'filename'] [INSTANCE int] ) ] [SHRINK SPACE [COMPACT] [CASCADE] ] [{PARALLEL [int] | NOPARALLEL}] [{LOGGING | NOLOGGING}] [physical_attributes_clause] } where the non-ANSI clauses are:
GLOBAL PARTITION BY {RANGE (column_list) ( PARTITION [partition_name] VALUE LESS THAN (value_list) [physical_attributes_clause] [TABLESPACE tablespace_name] [LOGGING | NOLOGGING] } [,...] ), | HASH (column_list) ( PARTITION [partition_name] {[TABLESPACE tablespace_name] [[OVERFLOW] TABLESPACE tablespace_name] [VARRAY varray_name STORE AS {LOB lob_segment_name] [LOB (lob_name) STORE AS [lob_segment_name] [TABLESPACE tablespace_name] } } | [STORE IN (tablespace_name [,...])] [OVERFLOW STORE IN (tablespace_name [,...] ), } [,...] } The GLOBAL PARTITION clause declares that the global index is manually partitioned via either range or hash partitioning onto partition_name. (The default is to partition the index equally in the same way the underlying table is partitioned, if at all.) You can specify a maximum of 32 columns, though none may be ROWID. You may also apply the [NO]LOGGING clause, TABLESPACE clause, and the physical_attributes_clause (defined earlier) to a specific partition. You cannot partition on ROWID. You may include one or more partitions, along with any attributes, in a comma-delimited list, according to the following:
By default, Oracle indexes are non-unique. It is also important to know that Oracle's regular B-tree indexes do not include records that have a null key value. Oracle does not support indexes on columns with the following datatypes: LONG, LONG RAW, REF (with the SCOPE attribute), or user-defined datatypes. You may create indexes on functions and expressions, but they cannot allow null values or aggregate functions. When you create an index on a function, the function, if it has no parameters, should show an empty set. For example: function_name( ). If the function is a UDF, it must be DETERMINISTIC. Oracle supports a special index structure called an index-organized table (IOT). IOTs combine the table data and primary key index on a single physical structure, instead of having separate structures for the table and for the index. IOTs are created using the CREATE TABLE...ORGANIZATION INDEX statement. Refer to CREATE/ALTER TABLE Statement for more information on making an IOT. Oracle automatically creates any additional indexes on an index-organized table as secondary indexes. Secondary indexes do not support the REVERSE clause. Oracle allows the creation of partitioned indexes and tables with the PARTITION clause. Consequently, Oracle's indexes also support partitioned tables. The LOCAL clause tells Oracle to create separate indexes for each partition of a table. The GLOBAL clause tells Oracle to create a common index for all the partitions. Note that any time an object name is referenced in the syntax diagram, you may optionally supply the schema. This applies to indexes, tables, etc., but not to tablespaces. You must have the explicitly declared privilege to create an index in a schema besides the current one. As an example, you can use a statement such as the following to create an Oracle index that is compressed, created in parallel, and with compiled statistics, but without logging the creation: CREATE UNIQUE INDEX unq_pub_id ON publishers(pub_name, country) COMPRESS 1 PARALLEL NOLOGGING COMPUTE STATISTICS; As with other Oracle object creation statements, you can control how much space they consume and in what increments they grow. The following example constructs an index in Oracle on a specific tablespace with specific instructions for how the data is to be stored: CREATE UNIQUE INDEX unq_pub_id ON publishers(pub_name, country) STORAGE (INITIAL 10M NEXT 5M PCTINCREASE 0) TABLESPACE publishers; For example, when you create the housing_construction table as a partitioned table on an Oracle server, you should also create a partitioned index with its own index partitions: CREATE UNIQUE CLUSTERED INDEX project_id_ind ON housing_construction(project_id) GLOBAL PARTITION BY RANGE (project_id) (PARTITION part1 VALUES LESS THAN ('H') TABLESPACE construction_part1_ndx_ts, PARTITION part2 VALUES LESS THAN ('P') TABLESPACE construction_part2_ndx_ts, PARTITION part3 VALUES LESS THAN (MAXVALUE) TABLESPACE construction_part3_ndx_ts); If in fact the housing_construction table used a composite partition, we could accommodate that here: CREATE UNIQUE CLUSTERED INDEX project_id_ind ON housing_construction(project_id) STORAGE (INITIAL 10M MAXEXTENTS UNLIMITED) LOCAL (PARTITION part1 TABLESPACE construction_part1_ndx_ts, PARTITION part2 TABLESPACE construction_part2_ndx_ts (SUBPARTITION subpart10, SUBPARTITION subpart20, SUBPARTITION subpart30, SUBPARTITION subpart40, SUBPARTITION subpart50, SUBPARTITION subpart60), PARTITION part3 TABLESPACE construction_part3_ndx_ts); In the following example, we could rebuild the project_id_ind index that was created earlier by using parallel execution processes to scan the old index and build the new index in reverse order: ALTER INDEX project_id_ind REBUILD REVERSE PARALLEL; Similarly, we could split out an additional partition on project_id_ind: ALTER INDEX project_id_ind SPLIT PARTITION part3 AT ('S') INTO (PARTITION part3_a TABLESPACE constr_p3_a LOGGING, PARTITION part3_b TABLESPACE constr_p3_b); PostgreSQLPostgreSQL allows the creation of ascending-order indexes, as well as UNIQUE indexes. Its implementation also includes a performance enhancement under the USING clause. PostgreSQL's CREATE INDEX syntax is: CREATE [UNIQUE] INDEX index_name ON table_name [USING {BTREE | RTREE | HASH} ] {function_name | (column_name [,...] ) } [WHERE condition] where:
In PostgreSQL, a column may have an associated operator class based on the datatype of the column. An operator class specifies the operators for a particular index. Although users are free to define any valid operator class for a given column, the default operator class is the appropriate operator class for that column type. In the following example, we create an index using the GIST indextype. We also make sure that uniqueness is only enforced for publishers outside of the USA: CREATE UNIQUE INDEX unq_pub_id ON publishers(pub_name, country) USING GIST WHERE country <> 'USA'; SQL ServerSQL Server's CREATE INDEX syntax is: CREATE [UNIQUE] [[NON]CLUSTERED] INDEX index_name ON {table_name | view_name} (column [ASC | DESC] [,...]) [WITH [PAD_INDEX] [[,] FILLFACTOR = int] [[,] IGNORE_DUP_KEY] [[,] DROP_EXISTING] [[,] STATISTICS_NORECOMPUTE] ] [[,] SORT_IN_TEMPDB] ] [ON filegroup] where:
SQL Server allows the creation of a unique clustered index on a view, effectively materializing the view. This can greatly speed up data retrieval operations against the view. Once a view has a unique clustered index, nonclustered indexes also can be added to the view. Note that the view also must be created using the SCHEMABINDING option. Indexed views are only allowed in SQL Server 2000 Enterprise Edition, unless you add a NOEXPAND hint to the view. Indexed views support data retrieval, but not data modification. (See the SQL Server section of CREATE/ALTER VIEW Statement" for specific restrictions on indexed views.) SQL Server allows up to 249 nonclustered indexes (unique or non-unique) on a table, as well as one primary key index. Index columns may not be of datatype NTEXT, TEXT, or IMAGE. Concatenated keys may contain up to 16 columns and/or a total of 900 bytes across all fixed-length columns. SQL Server automatically parallelizes the creation of the index according to the configuration option max degree of parallelism. It is often necessary to build indexes that span several columns-i.e., a concatenated key. Here is an example: CREATE UNIQUE INDEX project2_ind ON housing_construction(project_name, project_date) WITH PAD_INDEX, FILLFACTOR = 80 ON FILEGROUP housing_fg GO Adding the PAD_INDEX clause and setting the FILLFACTOR to 80 tells SQL Server to leave the index and data pages 80% full, rather than 100% full. The example also tells SQL Server to create the index on the housing_fg filegroup, rather than the default filegroup. See Also
The CREATE/ALTER METHOD statements allow the creation of a new database method or the alteration of an already existing database method. An easy (but loose) way to think of a method is that it is a user-defined function associated with a user-defined type. For example, a method called Office, of type Address, can accept a VARCHAR input parameter and passes out a result of Address. An implicitly defined method is created every time a structured type is created (see CREATE/ALTER TYPE Statement" for more details). Using a combination of the CREATE TYPE statement and the CREATE METHOD statement creates user-defined methods.
SQL2003 Syntax{CREATE | ALTER} [INSTANT | STATIC] METHOD method_name ( [ [{IN | OUT | INOUT}] param datatype [AS LOCATOR] [RESULT] [,...] ) RETURNS datatype FOR udt_name [SPECIFIC specific_name] code_body Keywords
[{IN | OUT | INOUT}] parameter_name1 datatype, [{IN | OUT | INOUT}] parameter_name2 datatype,[...] Make sure the name is unique within the method. When used with ALTER, this clause adds parameters to a pre-existing method. Refer to Chapter 2 for details on datatypes.
Rules at a GlanceUser-defined methods are essentially a different approach to the same output provided by user-defined functions. For example, consider the following two pieces of code: CREATE FUNCTION my_fcn (order_udt) RETURNS INT; CREATE METHOD my_mthd ( ) RETURNS INT FOR order_udt; Although the code sections for the function and method are different, they do exactly the same thing. The rules for use and invocation of methods are otherwise the same as for functions. Programming Tips and GotchasThe main difficulty with methods is that they are an object-oriented approach to the same sort of functionality provided by user-defined functions. Since they accomplish the same work, using a different approach, it can be hard to decide which approach to use. DB2DB2 supports both the CREATE METHOD and ALTER METHOD statements: CREATE [SPECIFIC] METHOD method_name ( [ [{IN | OUT | INOUT}] [param] datatype [AS LOCATOR] [RESULT] [,...] ) [RETURNS datatype [{AS LOCATOR | CAST FROM datatype [AS LOCATOR] } ] FOR udt_name EXTERNAL [NAME 'name'] [TRANSFORM GROUP group_name] code_body The syntax for ALTER METHOD is quite a bit simpler: ALTER METHOD method_name FOR udt_name EXTERNAL NAME 'name' where:
The method specification is determined by the LANGUAGE setting of the CREATE TYPE statement associated with the method. For LANGUAGE SQL, the method specification is SQL. For any other LANGUAGE setting, the method specification is external. The following example method, called office_zip, checks to see if a ZIP Code is valid based on the user-defined type address_type: CREATE METHOD office (address address_type) RETURNS INTEGER FOR address_type RETURN (CASE WHEN (self..zip = address.zip) THEN 1 -- valid ELSE 0 -- invalid END); In the next example, the method distance uses and external routine and transform group to process the between two address_types: CREATE METHOD distance (address_type) FOR address_type EXTERNAL NAME 'addrlib!distance' TRANSFORM GROUP method_group; Later, we could change the method so that it uses a different external routine: ALTER METHOD distance ( ) FOR TYPE address_type EXTERNAL NAME 'newaddrlib!distance_2'; MySQLNot supported. OracleNot supported. PostgreSQLNot supported. SQL ServerNot supported. See Also
CREATE ROLE allows the creation of a named set of privileges that may be assigned to users of a database. When a role is granted to a user, that user gets all the privileges and permissions of that role. Roles are generally accepted as one of the best means for maintaining security and controlling privileges within a database.
SQL2003 SyntaxCREATE ROLE role_name [WITH ADMIN {CURRENT_USER | CURRENT_ROLE}] Keywords
Rules at a GlanceUsing roles for database security can greatly ease administration and user maintenance. The general steps for using roles in database security are:
Permissions can be disabled using the REVOKE command. Programming Tips and GotchasThe main problem with roles is that occasionally a database administrator will provide redundant permissions to a role and separately to a user. If you ever need to prevent a user's access to the resource in situations like this, you usually will need to REVOKE the permissions twice. The role must be revoked from the user and then the specific user-level privilege must also be revoked from the user. DB2Not supported. Instead, DB2 uses operating system groups, so you can achieve similar functionality but you need to:
DB2 allows OS groups to be assigned special admin roles at the instance level using the sysadm_group, sysctrl_group, and sysmaint_group configuration parameters. MySQLNot supported. OracleAlthough it is not currently supported by the ANSI standard, Oracle also offers an ALTER ROLE statement. Oracle supports the concept of roles, though Oracle's implementation of the commands is very different from the ANSI SQL standard: {CREATE | ALTER} ROLE role_name [NOT IDENTIFIED | IDENTIFIED {BY password | EXTERNALLY | GLOBALLY | USING package_name}] where:
In Oracle, the role is created first, then granted privileges and permissions as if it is a user via the GRANT command. When users want to get access to the permissions of a role protected by a password, they use the SET ROLE command. If a password is placed on the role, any user wishing to access it must provide the password with the SET ROLE command. Oracle ships with several preconfigured roles. CONNECT, DBA, and RESOURCE are available in all versions of Oracle. EXP_FULL_DATABASE and IMP_FULL_DATABASE are newer roles used for import and export operations. The GRANT statement reference has a more detailed discussion of all of the preconfigured roles available in Oracle. The following example uses CREATE to specify a new role in Oracle, GRANTs it privileges, assigns it a password with ALTER ROLE, and GRANTs that role to a couple of users: CREATE ROLE boss; GRANT ALL ON employee TO boss; GRANT CREATE SESSION, CREATE DATABASE LINK TO boss; ALTER ROLE boss IDENTIFIED BY le_grande_fromage; GRANT boss TO emily, jake; PostgreSQLAlthough PostgreSQL does not support the CREATE ROLE command, it offers a nearly identical facility of its own called CREATE GROUP. PostgreSQL also offers an ALTER GROUP statement to modify existing groups. In PostgreSQL, a group and a role are identical: {CREATE | ALTER} GROUP name [ [WITH] {SYSID int | USER username [,...] }] [ {DROP | ADD} username [,...] } where:
Use the DROP GROUP clause to drop a group you no longer want. SQL ServerMicrosoft SQL Server does not support the CREATE ROLE command, but has the equivalent capability via the system stored procedure sp_add_role. See Also
This statement creates a schema-i.e., a named group of related objects. A schema is a collection of tables, views, and permissions granted to specific users or roles. According to the ANSI standard, specific object permissions are not schema objects in themselves and do not belong to a specific schema. However, roles are sets of privileges that do belong to a schema.
SQL2003 SyntaxCREATE SCHEMA [schema_name] [AUTHORIZATION owner_name] [DEFAULT CHARACTER SET char_set_name] [PATH schema_name [,...] ] [ ANSI CREATE statements [...] ] [ ANSI GRANT statements [...] ] Keywords
Rules at a GlanceThe CREATE SCHEMA statement is a container that can hold many other CREATE and GRANT statements. The most common way to think of a schema is as all of the objects that a specific user owns. For example, the user jake may own several tables and views including the table, publishers, in his own schema. Meanwhile, the user dylan may own several other tables and views in his schema, but may also own his own separate copy of the publishers table. The ANSI standard requires that all CREATE statements are allowed in a CREATE SCHEMA statement. In practice, most implementations of CREATE SCHEMA only allow three subordinate statements: CREATE TABLE, CREATE VIEW, and GRANT. The order of the commands is not important, meaning you can grant privileges on tables or views whose CREATE statements appear later in the CREATE SCHEMA statement. Programming Tips and GotchasIt is not required, but is considered a best practice, to arrange the objects and grants within a CREATE SCHEMA statement in an order that will execute naturally. In other words, CREATE VIEW statements should follow the CREATE TABLE statements that they depend on, and then GRANT statements should come last. If your database system uses schemas, we recommend that you always reference your objects by schema and then object name (as in jake.publishers). If you do not include a schema qualifier, the database platform will typically assume the default schema for the current user connection. Some database platforms do not explicitly support the CREATE SCHEMA command. However, they implicitly create a schema when a user creates database objects. For example, Oracle creates a schema whenever a user is created. The CREATE SCHEMA command is simply a single-step method of creating all the tables, views, and other database objects along with their permissions. DB2DB2 supports the basic elements of the CREATE SCHEMA statement. It does not support the PATH or DEFAULT CHARACTER SET clauses: CREATE SCHEMA [schema_name] [AUTHORIZATION owner_name] [ ANSI CREATE statements [...] ] [ ANSI GRANT statements [...] ] The syntax and usage is otherwise identical to the ANSI standard. DB2 creates a schema implicitly whenever a new schema is referenced in a GRANT or CREATE statement. DB2 does not support an ALTER SCHEMA statement. MySQLNot supported. OracleIn Oracle, the CREATE SCHEMA does not create a schema-only the CREATE USER statement does that. CREATE SCHEMA allows a user to perform multiple CREATEs and GRANTs in a previously created schema in one SQL statement: CREATE SCHEMA AUTHORIZATION schema_name [ ANSI CREATE statements [...] ] [ ANSI GRANT statements [...] ] Note that Oracle only allows ANSI standard CREATE TABLE, CREATE VIEW, and GRANT statements within a CREATE SCHEMA statement. You should not use any of Oracle's extensions to these commands when using the CREATE SCHEMA statement. The following Oracle example places the permissions before the objects within the CREATE SCHEMA statement: CREATE SCHEMA AUTHORIZATION emily GRANT SELECT, INSERT ON view_1 TO sarah GRANT ALL ON table_1 TO sarah CREATE VIEW view_1 AS SELECT column_1, column_2 FROM table_1 ORDER BY column_2 CREATE TABLE table_1(column_1 INT, column_2 CHAR(20)); As the example above shows, the order of the statements within the CREATE SCHEMA statement is unimportant. Oracle commits the CREATE SCHEMA statement only if all CREATE and GRANT statements in the statement complete successfully. PostgreSQLNot supported. SQL ServerSQL Server supports the basic CREATE SCHEMA statement without support for the PATH clause or the DEFAULT CHARACTER SET clause: CREATE SCHEMA AUTHORIZATION owner_name [ ANSI CREATE statements [...] ] [ ANSI GRANT statements [...] ] If any statement fails within the CREATE SCHEMA statement, then the entire statement fails. SQL Server does not require that the CREATE or GRANT statements be in any particular order, except that nested views must be declared in their logical order. Thus, if view_100 references view_10, then view_10 must appear in the CREATE SCHEMA statement before view_100. For example: CREATE SCHEMA AUTHORIZATION katie GRANT SELECT ON view_10 TO public CREATE VIEW view_10(col1) AS SELECT col1 FROM foo CREATE TABLE foo(col1 INT) CREATE TABLE foo (col1 INT PRIMARY KEY, col2 INT REFERENCES foo2(col1)) CREATE TABLE foo2 (col1 INT PRIMARY KEY, col2 INT REFERENCES foo(col1)) See Also
Manipulating tables is one of the most common activities that database administrators and programmers perform when working with database objects. This section details how to create and modify tables. The ANSI standard represents a sort of least common denominator among the vendors, though not every option of the ANSI standard version of CREATE TABLE and ALTER TABLE is offered by each of the vendors. However, the ANSI standard does represent the basic form that can be used across all of the platforms. In turn, the vendor platforms offer a variety of extensions and additions to the ANSI standards for CREATE and ALTER TABLE.
SQL2003 SyntaxThe SQL2003 statement CREATE TABLE creates a permanent or temporary table within the database where the command is issued. The syntax is as follows: CREATE [{LOCAL TEMPORARY| GLOBAL TEMPORARY}] TABLE table_name (column_name datatype attributes [,...] ) | [column_name WITH OPTIONS options] | [LIKE table_name] | [REF IS column_name {SYSTEM GENERATED | USER GENERATED | DERIVED} ] [CONSTRAINT constraint_type [constraint_name] [,...] ] [OF type_name [UNDER super_table] [table_definition] ] [ON COMMIT {PRESERVE ROWS | DELETE ROWS} The SQL2003 statement ALTER TABLE allows many useful modifications to be made to an existing table without dropping any existing indexes, triggers, or permissions assigned to it. Following is the ALTER TABLE syntax: ALTER TABLE table_name [ADD [COLUMN] column_name datatype attributes] | [ALTER [COLUMN] column_name SET DEFAULT default_value] | [ALTER [COLUMN] column_name DROP DEFAULT] | [ALTER [COLUMN] column_name ADD SCOPE table_name | [ALTER [COLUMN] column_name DROP SCOPE {RESTRICT | CASCADE}] | [DROP [COLUMN] column_name {RESTRICT | CASCADE}] | [ADD table_constraint] | [DROP CONSTRAINT table_constraint_name {RESTRICT | CASCADE}] Keywords
Rules at a GlanceThe typical CREATE TABLE statement is very simple. Generally, it names the table and any columns contained in the table. Many table definitions also include a nullability constraint for most of the columns, as in this SQL Server example: CREATE TABLE housing_construction (project_number INT NOT NULL, project_date DATE NOT NULL, project_name VARCHAR(50) NOT NULL, construction_color NCHAR(20) , construction_height DECIMAL(4,1), construction_length DECIMAL(4,1), construction_width DECIMAL(4,1), construction_volume INT ) This example adds a foreign key to the example table: -- Creating a column-level constraint CREATE TABLE favorite_books (isbn CHAR(100) PRIMARY KEY, book_name VARCHAR(40) UNIQUE, category VARCHAR(40) , subcategory VARCHAR(40) , pub_date DATETIME NOT NULL, purchase_date DATETIME NOT NULL, CONSTRAINT fk_categories FOREIGN KEY (category) REFERENCES category(cat_name)); The foreign key on the categories column relates it to the cat_name table in the category table. All the vendors mentioned in this book support this syntax.
ALTER TABLE favorite_books ADD CONSTRAINT fk_categories FOREIGN KEY (category, subcategory) REFERENCES category(cat_name, subcat_name)); Now, we could use an ALTER TABLE statement to drop the constraint altogether: ALTER TABLE favorite_books DROP CONSTRAINT fk_categories RESTRICT; Listed here are more full examples from pubs, the sample database that ships with Microsoft SQL Server and Sybase Adaptive Server: -- For a Microsoft SQL Server database CREATE TABLE jobs (job_id SMALLINT IDENTITY(1,1) PRIMARY KEY CLUSTERED, job_desc VARCHAR(50) NOT NULL DEFAULT 'New Position', min_lvl TINYINT NOT NULL CHECK (min_lvl >= 10), max_lvl TINYINT NOT NULL CHECK (max_lvl <= 250)) -- For a MySQL database CREATE TABLE employee (emp_id INT AUTO_INCREMENT CONSTRAINT PK_emp_id PRIMARY KEY, fname VARCHAR(20) NOT NULL, minit CHAR(1) NULL, lname VARCHAR(30) NOT NULL, job_id SMALLINT NOT NULL DEFAULT 1 REFERENCES jobs(job_id), job_lvl TINYINT DEFAULT 10, pub_id CHAR(4) NOT NULL DEFAULT ('9952') REFERENCES publishers(pub_id), hire_date DATETIME NOT NULL DEFAULT (CURRENT_DATE( )); CREATE TABLE publishers (pub_id char(4) NOT NULL CONSTRAINT UPKCL_pubind PRIMARY KEY CLUSTERED CHECK (pub_id IN ('1389', '0736', '0877', '1622', '1756') OR pub_id LIKE '99[0-9][0-9]'), pub_name varchar(40) NULL, city varchar(20) NULL, state char(2) NULL, country varchar(30) NULL DEFAULT('USA')) Once you get into the vendor extensions, the CREATE TABLE command is no longer portable between database platforms. The following is an example of an Oracle CREATE TABLE statement with many storage properties: CREATE TABLE classical_music_cds (music_id INT, composition VARCHAR2(50), composer VARCHAR2(50), performer VARCHAR2(50), performance_date DATE DEFAULT SYSDATE, duration INT, cd_name VARCHAR2(100), CONSTRAINT pk_class_cds PRIMARY KEY (music_id) USING INDEX TABLESPACE index_ts STORAGE (INITIAL 100K NEXT 20K), CONSTRAINT uq_class_cds UNIQUE (composition, performer, performance_date) USING INDEX TABLESPACE index_ts STORAGE (INITIAL 100K NEXT 20K)) TABLESPACE tabledata_ts; When issuing a CREATE or ALTER statement, we recommend that it be the only statement in your transaction. For example, do not attempt to create a table and select from it in the same batch. Instead, (1) create the table, (2) verify the operation, (3) COMMIT, and then (4) perform any subsequent operations against the table. Table_name is the name for a new or existing table. New table names should start with an alphabetic character and in general contain no other special symbol besides underscore ( _ ). Length of the name and exact composition is governed differently according to each vendor. When creating or altering a table, the list of column definitions is always encapsulated within parentheses and separated by commas between each new column definition. Programming Tips and GotchasThe user issuing the CREATE TABLE command must have the appropriate permissions. Similarly, any user wishing to ALTER or DROP a table should own the table within his own schema or have adequate permissions to the alter or drop the table. Since the ANSI standard does not specify the privileges required, expect some variation between vendors. You can encapsulate a CREATE TABLE or ALTER TABLE statement within a transaction, using a COMMIT or ROLLBACK statement to explicitly conclude the transaction. We recommend that it be the only command in the transaction. You can have a great deal of control over the way that the records of a table are physically written to disk through extensions to the ANSI standard. DB2 and SQL Server use a technique called clustered indexes to control the way that records are written to disk. Oracle uses a technique that is functionally similar called an index-organized table. Some databases will lock a table that is being modified by an ALTER TABLE statement. It is wise to issue this command only on tables that are not in use on a production database. Some databases will lock the target and source tables when using the CREATE TABLE...LIKE statement. DB2DB2 supports a very rich and elaborate set of extensions to the CREATE TABLE statement: CREATE TABLE table_name {(column_name datatype attributes [,...] ) | OF type_name [{UNDER super_table INHERIT SELECT PRIVILEGES | HIERARCHY hierarchy_name}] [table_definition] | AS subquery {derived_table_options | DEFINITION ONLY [copy_options] } | [LIKE table_name] [copy_options] | [ [(staging_column [,...] ) ] FOR table_name PROPAGATE IMMEDIATE] } [ORGANIZE BY [DIMENSTIONS] (column_name [,...] ) ] [DATA CAPTURE {NONE | CHANGES} ] [IN tablespace_name [INDEX IN tablespace_name] [LONG IN tablespace_name]] [VALUE COMPRESSION] [WITH RESTRICT ON DROP] [NOT LOGGED INITIALLY] [{REPLICATED | PARTITIONING KEY (column_name [,...] ) [USING HASHING] } ] [OPTIONS (REMOTE_SERVER 'server_name' [, REMOTE_SCHEMA 'schema_name'] [, REMOTE_TABNAME 'table_name'] ] Similarly rich, the DB2 statement ALTER TABLE allows many useful modifications to be made to an existing table without dropping any existing indexes, triggers, or permissions assigned to it: ALTER TABLE table_name {[ADD { {[COLUMN] column_name datatype attributes} | table_constraint | partitioning_key | RESTRICT_ON_DROP } | {[ALTER { {CHECK | FOREIGN KEY} constraint_name constraint_definition] | [COLUMN] column_name column_definition } | {[DROP constraint_type constraint_name] | partitioning_key | RESTRICT_ON_DROP } | [DATA CAPTURE {NONE | CHANGES [INCLUDE LONGVAR COLUMNS]} | [ACTIVATE NOT LOGGED INITIALLY [WITH EMPTY TABLE] ] | [PCTFREE int] | [LOCKSIZE {ROW | TABLE} ] | [APPEND {ON | OFF} ] | [ [NOT] VOLATILE [CARDINATITY] ] | [ [DE]ACTIVATE VALUE COMPRESSION] } [...] | SET MATERIALIZED QUERY AS {DEFINITION ONLY | subquery] The parameters for both commands are as follows:
MySQLThe MySQL syntax for CREATE TABLE creates a permanent or local temporary table within the database in which the command is issued. CREATE [TEMPORARY] TABLE [IF NOT EXISTS] table_name {(column_name datatype attributes constraint_type constraint_name [,...] ) [constraint_type [constraint_name] [,...] ] [MATCH FULL | MATCH PARTIAL] [ON {DELETE | UPDATE} {RESTRICT | CASCADE | SET NULL | NO ACTION | SET DEFAULT}] LIKE other_table_name } {[TYPE = {ISAM | MyISAM | HEAP | BDB | InnoDB | MERGE | MRG_MyISAM} | AUTO_INCREMENT = int | AVG_ROW_LENGTH = int | CHECKSUM = {0 | 1} | COMMENT = "string" | DELAY_KEY_WRITE = {0 | 1} | MAX_ROWS = int | MIN_ROWS = int | PACK_KEYS = {0 | 1} | PASSWORD = "string" | ROW_FORMAT= { default | dynamic | static | compressed } | RAID_TYPE = {1 | STRIPED | RAID0} | RAID_CHUNKS = int | RAID_CHUNKSIZE = int | INSERT_METHOD = {NO | FIRST | LAST} | DATA DIRECTORY = "path to directory" INDEX DIRECTORY = " path to directory "] } [[IGNORE | REPLACE] select_statement] The MySQL syntax for ALTER TABLE allows modifications to a table or even renaming of a table: ALTER [IGNORE] TABLE table_name {[ADD [COLUMN] (column_name datatype attributes ] [FIRST | AFTER column_name]] | [ADD [UNIQUE | FULLTEXT] INDEX [index_name] | (index_col_name [,...]) ] | [ADD PRIMARY KEY (index_col_name [,...]) ] | [ALTER [COLUMN] column_name {SET DEFAULT literal | | DROP DEFAULT}] | [CHANGE | MODIFY] [COLUMN] old_col_name new_column_name] [FIRST | AFTER column_name] | [MODIFY] column_name datatype attributes | [DROP [COLUMN] column_name] | [DROP PRIMARY KEY] | [DROP INDEX index_name] | [{ENABLE | DISABLE} KEYS] | [RENAME [TO] new_tbl_name] | [table_options] } [,...] Keywords and parameters are as follows:
For example: CREATE TABLE test_example (column_a INT NOT NULL AUTO_INCREMENT, PRIMARY KEY(column_a), INDEX(column_b)) TYPE=HEAP IGNORE SELECT column_b,column_c FROM samples; This creates a heap table with three columns: column_a, column_b, and column_c. Later on, we could change this table to the MyISAM type: ALTER TABLE example TYPE=MyISAM; Three operating-system files are created when MySQL creates a MyISAM table: a table definition file with the extension .FRM, a datafile with the extension .MYD, and an index file with the extension .MYI. The .FRM datafile is used for all other tables. The following example creates two base MyISAM tables and then creates a MERGE table from them: CREATE TABLE message1 (message_id INT AUTO_INCREMENT PRIMARY KEY, message_text CHAR(20)); CREATE TABLE message2 (message_id INT AUTO_INCREMENT PRIMARY KEY, message_text CHAR(20)); CREATE TABLE all_messages (message_id INT AUTO_INCREMENT PRIMARY KEY, message_text CHAR(20)) TYPE=MERGE UNION=(message1, message2) INSERT_METHOD=LAST; The following example renames both a table and a column: ALTER TABLE employee RENAME AS emp; ALTER TABLE employee CHANGE employee_ssn emp_ssn INTEGER; Since MySQL allows the creation of indexes on a portion of a column (for example, on the first ten characters of a column), you can also build short indexes on very large columns. MySQL can redefine the datatype of an existing column. To avoid losing any data, the values contained in the column must be compatible with the new datatype. For example, a date column could be redefined to a character datatype, but not a character datatype to an integer. Here's an example: ALTER TABLE mytable MODIFY mycolumn LONGTEXT MySQL allows some additional flexibility in the ALTER TABLE statement by allowing users to issue multiple ADD, ALTER, DROP, and CHANGE clauses in a comma-delimited list in a single ALTER TABLE statement. However, be aware that the CHANGE column_name and DROP INDEX clauses are MySQL extensions not found in SQL2003. MySQL supports the clause MODIFY column_name to mimic the same feature found in Oracle. OracleThe Oracle syntax for CREATE TABLE creates a relational table either by declaring the structure or by referencing an existing table. You can modify a table after it is created using the ALTER TABLE statement. Oracle also allows the creation of a relational table that uses user-defined types for column definitions. You can also create an object table that is explicitly defined to hold a specific UDT, usually a VARRAY or NESTED TABLE type, as well as XMLType tables. The standard ANSI style CREATE TABLE statement is supported, but Oracle has added many sophisticated extensions to the command. For example, Oracle allows significant control over the storage and performance parameters of a table. In both the CREATE TABLE and ALTER TABLE statements, you'll see a great deal of nesting and reusable clauses. To make this somewhat easier to read, we have broken Oracle's CREATE TABLE statement into three distinct variations (relational table, object table, and XML table) so that you can more easily follow the syntax. The CREATE TABLE syntax for a standard relational table, which has no object or XML properties, is as follows: CREATE [GLOBAL] [TEMPORARY] TABLE table_name [ ( {column | attribute} [SORT] [DEFAULT expression] [{column_constraint | inline_ref_constraint} ] | {table_constraint_clause | table_ref_constraint} | {GROUP log_group (column [NO LOG] [,...]) [ALWAYS] | DATA (constraints [,...]) COLUMNS} ) ] [ON COMMIT {DELETE | PRESERVE} ROWS] [table_constraint_clause] { [physical_attributes_clause] [TABLESPACE tablespace_name] [storage_clause] [[NO]LOGGING] | [CLUSTER (column [,...] )] | {[ORGANIZATION {HEAP [physical_attributes_clause][TABLESPACE tablespace_name][storage_clause] [COMPRESS | NOCOMPRESS] [[NO]LOGGING] | INDEX [physical_attributes_clause][TABLESPACE tablespace_name][storage_clause] [PCTTHRESHOLD int] [COMPRESS [int] | NOCOMPRESS] [MAPPING TABLE | NOMAPPING][...] [[NO]LOGGING] [[INCLUDING column] OVERFLOW [physical_attributes_clause][TABLESPACE tablespace_name] [storage_clause] [[NO]LOGGING] } ] | EXTERNAL ( [TYPE driver_type] ) DEFAULT DIRECTORY directory_name [ACCESS PARAMETERS {USING CLOB subquery | ( opaque_format ) } ] LOCATION ( [directory_name:]'location_spec' [,...] ) [REJECT LIMIT {int | UNLIMITED} ] } } [{ENABLE | DISABLE} ROW MOVEMENT] [[NO]CACHE] [[NO]MONITORING] [[NO]ROWDEPENDENCIES] [PARALLEL int | NOPARALLEL] [NOSORT] [[NO]LOGGING]] [COMPRESS [int] | NOCOMPRESS] [{ENABLE | DISABLE} [[NO]VALIDATE] {UNIQUE (column [,...] | PRIMARY KEY | CONSTRAINT constraint_name} ] [USING INDEX {index_name | CREATE_INDEX_statement} ] [EXCEPTIONS INTO] [CASCADE] [{KEEP | DROP} INDEX] ] | [partition_clause] [AS subquery] The relational table syntax contains a large number of optional clauses. However, the table definition must contain, at a minimum, either column names and datatypes specifications or the AS subquery clause. The Oracle syntax for an object table follows: CREATE [GLOBAL] [TEMPORARY] TABLE table_name AS object_type [[NOT] SUBSTITUTABLE AT ALL LEVELS] [ ( {column | attribute} [DEFAULT expression] [{column_constraint | inline_ref_constraint} ] | {table_constraint_clause | table_ref_constraint } | {GROUP log_group (column [NO LOG] [,...]) [ALWAYS] | DATA (constraints [,...]) COLUMNS} ) ] [ON COMMIT {DELETE | PRESERVE} ROWS] [OBJECT IDENTIFIER IS {SYSTEM GENERATED | PRIMARY KEY}] [OIDINDEX [index_name] ([physical_attributes_clause] [storage_clause]) ] [physical_attributes_clause] [TABLESPACE tablespace_name] [storage_clause] The Oracle syntax for an XMLType table follows: CREATE [GLOBAL] [TEMPORARY] TABLE table_name OF XMLTYPE [ ( {column | attribute} [DEFAULT expression] [{column_constraint | inline_ref_constraint} ] | {table_constraint_clause | table_ref_constraint } | {GROUP log_group (column [NO LOG] [,...]) [ALWAYS] | DATA (constraints [,...]) COLUMNS} ) ] [XMLTYPE {OBJECT RELATIONAL [xml_storage_clause] | CLOB [lob_segname] [lob_params]}] [xml_schema_spec] [ON COMMIT {DELETE | PRESERVE} ROWS] [OBJECT IDENTIFIER IS {SYSTEM GENERATED | PRIMARY KEY}] [OIDINDEX index_name ([physical_attributes_clause] [storage_clause]) ] [physical_attributes_clause] [TABLESPACE tablespace_name] [storage_clause] The Oracle syntax for ALTER TABLE changes the table or column properties, storage characteristics, LOB or VARRAY properties, partitioning characteristics, and integrity constraints associated with a table and/or its columns. The statement does other things like move an existing table into a different tablespace, free recuperated space, compact the table segment, and adjust the "high-water mark." The ANSI SQL standard uses the ALTER keyword to modify existing elements of a table, while Oracle uses MODIFY to mean the same thing. Since they are essentially the same thing, please consider the behavior of otherwise identical clauses (for example, ANSI's ALTER TABLE...ALTER COLUMN to Oracle's ALTER TABLE...MODIFY COLUMN) to be functionally equivalent. Oracle's ALTER TABLE syntax is: ALTER TABLE table_name
-- alter table characteristics
[physical_attributes_clause] [storage_clause]
[[NO]LOGGING] [[NO]CACHE] [[NO]MONITORING] [[NO]COMPRESS]
[SHRINK SPACE [COMPACT] [CASCADE] ]
[UPGRADE [[NOT] INCLUDING DATA] column_name datatype attributes]
[[NO]MINIMIZE RECORDS_PER_BLOCK]
[PARALLEL int | NOPARALLEL]
[{ENABLE | DISABLE} ROW MOVEMENT]
[{ADD | DROP} SUPPLEMENTAL LOG
{GROUP log_group [(column_name [NO LOG] [,...] ) [ALWAYS] ] |
DATA ( {ALL | PRIMARY KEY | UNIQUE | FOREIGN KEY} [,...] ) COLUMNS} ]
[ALLOCATE EXTENT
[( SIZE int[K|M|G|T] ] [DATATILE 'filename'] [INSTANCE int} ] )]
[DEALLOCATE UNUSED [KEEP int[K|M|G|T] ] ]
[ORGANIZATION INDEX ...
[COALESCE] [MAPPING TABLE | NOMAPPING] [{PCTTHRESHOLD int]
[COMPRESS int | NOCOMPRESS]
[ { ADD OVERFLOW [TABLESPACE tablespace_name] [[NO]LOGGING]
[physical_attributes_clause]] ] |
OVERFLOW { [ALLOCATE EXTENT (SIZE int [K | M]] [DATAFILE
'filename'] [INSTANCE int] ) |
[DEALLOCATE UNUSED [KEEP int [K | M]] } ] ]
[RENAME TO new_table_name]
-- alter column characteristics
[ADD (column_name datatype attributes [,...] ) ]
[DROP { {UNUSED COLUMNS | COLUMNS CONTINUE} [CHECKPOINT int] |
{COLUMN column_name | (column_name [,...] ) } [CHECKPOINT int]
[{CASCADE CONSTRAINTS | INVALIDATE}] } ]
[SET UNUSED {COLUMN column_name | (column_name [,...] ) }
[{CASCADE CONSTRAINTS | INVALIDATE}]
[MODIFY { (column_name datatype attributes [,...] ) |
COLUMN column_name [NOT] SUBSTITUTABLE AT ALL LEVELS [FORCE] } ]
[RENAME COLUMN old_column_name TO new_column_name]
[MODIFY {NESTED TABLE | VARRAY} collection_item [RETURN AS {LOCATOR |
VALUE}]]
-- alter constraint characteristics
[ADD CONSTRAINT constraint_name table_constrain_clause]
[MODIFY CONSTRAINT constraint_name constraint_state_clause]
[RENAME CONSTRAINT old_constraint_name TO new_constraint_name]
[DROP { { PRIMARY KEY | UNIQUE (column [,...]) } [CASCADE]
[{KEEP | DROP} INDEX] |
CONSTRAINT constraint_name [CASCADE] } ]
-- alter table partition characteristics
[alter partition clauses]
-- alter external table characteristics
DEFAULT DIRECTORY directory_name
[ACCESS PARAMETERS {USING CLOB subquery | ( opaque_format ) } ]
LOCATION ( [directory_name:]'location_spec' [,...] )
[ADD (column_name ...)][DROP column_name ...][MODIFY (column_name ...)]
[PARALLEL int | NOPARALLEL]
[REJECT LIMIT {int | UNLIMITED} ]
[PROJECT COLUMN {ALL | REFERENCED} ]} }
-- move table clauses
[MOVE [ONLINE] [physical_attributes_clause]
[TABLESPACE tablespace_name] [[NO]LOGGING] [PCTTHRESHOLD int]
[COMPRESS int | NOCOMPRESS] [MAPPING TABLE | NOMAPPING]
[[INCLUDING column] OVERFLOW
[physical_attributes_clause] [TABLESPACE tablespace_name]
[[NO]LOGGING]] ]
[LOB ...] [VARRAY ...] [PARALLEL int | NOPARALLEL] ]
-- enable/disable attributes and constraints
[{ {ENABLE | DISABLE} [[NO]VALIDATE]] {UNIQUE (column [,...] ) |
PRIMARY KEY | CONSTRAINT constraint_name} ]
[USING INDEX {index_name | CREATE_INDEX_statement |
[TABLESPACE tablespace_name] [physical_attributes_clause]
[storage_clause]
[NOSORT] [[NO]LOGGING]] [ONLINE] [COMPUTE STATISTICS]
[COMPRESS | NOCOMPRESS] [REVERSE]
[{LOCAL | GLOBAL} partition_clause]
[EXCEPTIONS INTO table_name] [CASCADE] [{KEEP | DROP} INDEX] ] |
[{ENABLE | DISABLE}] [{TABLE LOCK | ALL TRIGGERS}] } ] The parameters are as follows:
A global temporary table is available to all user sessions, but the data stored within a global temporary table is visible only to the session that inserted it. The ON COMMIT clause, which is allowed only when creating temporary tables, tells Oracle to either truncate the table after each commit against the table (DELETE ROWS) or to truncate the table when the session terminates (PRESERVE ROWS). For example: CREATE GLOBAL TEMPORARY TABLE shipping_schedule (ship_date DATE, receipt_date DATE, received_by VARCHAR2(30), amt NUMBER) ON COMMIT PRESERVE ROWS; The CREATE TABLE statement shown above creates a global temporary table, shipping_schedule, which retains inserted rows across multiple transactions. The Oracle physical_attributes_clauseThe physical_attributes_clause (as shown in the following code block) defines storage characteristics for an entire local table, or if the table is partitioned, for a specific partition (discussed later). To declare the physical attributes of a new table or change the attributes on an existing table, simply declare the new values: -- physical_attributes_clause [{PCTFREE int | PCTUSED int | INITRANS int | MAXTRANS int | storage_clause}] where:
The Oracle storage_clause and LOBsThe storage_clause controls a number of attributes governing the physical storage of data: -- storage_clause STORAGE ( [ {INITIAL int [K | M] | NEXT int [K | M] | MINEXTENTS int | MAXEXTENTS { int | UNLIMITED} | PCTINCREASE int | FREELISTS int | FREELIST GROUPS int | BUFFER_POOL {KEEP | RECYCLE | DEFAULT} ] [...] ) When delineating the storage clause attributes, enclose them in parentheses and separate them with spaces-for example, (INITIAL 32M NEXT8M). The attributes are as follows:
For example, the table books_sales is defined on the sales tablespace as consuming an initial 8 MB of space, to grow by no less than 8 MB when the first extent is full. The table has no less than 1 and no more than 8 extents, limiting its maximum size to 64 MB: CREATE TABLE book_sales (qty NUMBER, period_end_date DATE, period_nbr NUMBER) TABLESPACE sales STORAGE (INITIAL 8M NEXT 8M MINEXTENTS 1 MAXEXTENTS 8); An example of a LOB table called large_objects with special handling for text and image storage might look like: CREATE TABLE large_objects (pretty_picture BLOB, interesting_text CLOB) STORAGE (INITIAL 256M NEXT 256M) LOB (pretty_picture, interesting_text) STORE AS (TABLESPACE large_object_segment STORAGE (INITIAL 512M NEXT 512M) NOCACHE LOGGING); The exact syntax used to define a LOB, CLOB, or NCLOB column is defined by the lob_parameter_clause. LOBs can appear at many different levels within an Oracle table. For instance, separate LOB definitions could exist in a single table in a partition definition, a subpartition definition, and at the top table-level. The syntax of lob_parameter_clause follows: {TABLESPACE tablespace_name] [{ENABLE | DISABLE} STORAGE IN ROW] [storage_clause] [CHUNK int] [PCTVERSION int] [RETENTION] [FREEPOOLS int] [{CACHE | {NOCACHE | CACHE READS} [{LOGGING | NOLOGGING}] }] } In the lob_parameter_clause, each parameter is identical to those of the wider CREATE TABLE LOB-object level. However, the following four parameters are unique to LOBs:
The following example shows our large_objects LOB table with added parameters to control inline storage and retention of old LOBs: CREATE TABLE large_objects (pretty_picture BLOB, interesting_text CLOB) STORAGE (INITIAL 256M NEXT 256M) LOB (pretty_picture, interesting_text) STORE AS (TABLESPACE large_object_segment STORAGE (INITIAL 512M NEXT 512M) NOCACHE LOGGING ENABLE STORAGE IN ROW RETENTION); The earlier example added parameter values for STORAGE IN ROW and RETENTION. But since we did not set one for CHUNK, that value is set to the Oracle default for the LOB. Oracle nested tablesOracle allows the declaration of a NESTED TABLE in which a table is virtually stored within a column of another table. The STORE AS clause enables a proxy name for the table within a table, but the nested table must be created initially as a user-defined datatype. This capability is valuable for sparse arrays of values, but we don't recommend it for day-to-day tasks. For example: CREATE TYPE prop_nested_tbl AS TABLE OF props_nt; CREATE TABLE proposal_types (proposal_category VARCHAR2(50), proposals PROPS_NT) NESTED TABLE props_nt STORE AS props_nt_table; This example creates a table called proposal_types along with a nested table called props_nt, which is stored as props_nt_table. Oracle compressed tablesOracle 10g (and Oracle 9i Release 2) allows compression of both keys and entire tables. (Oracle 9i allowed only key compression.) Although compression adds a tiny bit of overhead, it significantly reduces that amount of disk space consumed by a table. This is especially useful for databases pushing the envelop in terms of size. Key compression is handled in the ORGANIZE INDEX clause, while table compression is handled in the ORGANIZE HEAP clause. Oracle partitioned and subpartitioned tablesOracle allows tables to be partitioned and subpartitioned. You can also break out LOBs onto their own partition(s). A partitioned table may be broken into distinct parts, possibly placed on separate disk subsystems to improve I/O performance, based on four strategies: range, hash, list, and a composite of the first three. Partitioning syntax is quite elaborate: { PARTITION BY RANGE (column [,...]) (PARTITION [partition_name] VALUES LESS THAN ({MAXVALUE | value} [,...]) [table_partition_description] ) | PARTITION BY HASH (column [,...]) {(PARTITION [partition_name] [partition_storage_clause] [,...] | PARTITIONS hash_partition_qty [STORE IN (tablespace [,...])] [OVERFLOW STORE IN (tablespace [,...])]} | PARTITION BY LIST (column [,...]) (PARTITION [partition_name] VALUES ({MAXVALUE | value} [,...]) [table_partition_description] | PARTITION BY RANGE (column [,...]) {subpartition_by_list | subpartition_by_hash} (PARTITION [partition_name] VALUES LESS THAN ({MAXVALUE | value} [,...]) [table_partition_description] ) } The following example code shows the orders table partitioned by range: CREATE TABLE orders (order_number NUMBER, order_date DATE, cust_nbr NUMBER, price NUMBER, qty NUMBER, cust_shp_id NUMBER) PARTITION BY RANGE(order_date) (PARTITION pre_yr_2000 VALUES LESS THAN TO_DATE('01-JAN-2000', 'DD-MON-YYYY'), PARTITION pre_yr_2004 VALUES LESS THAN TO_DATE('01-JAN-2004', 'DD-MON-YYYY' PARTITION post_yr_2004 VALUES LESS THAN MAXVALUE) ) ; This example creates three partitions on the orders table - one for the orders taken before the year 2000 (pre_yr_2000), one for the orders taken before the year 2004 (pre_yr_2004), and another for the orders taken after the year 2004 (post_yr_2004) - all based on the range of dates that appear in the order_date column. The next example creates the orders table based on a hash value in the customer_shipping_id: CREATE TABLE orders (order_number NUMBER, order_date DATE, cust_nbr NUMBER, price NUMBER, qty NUMBER, cust_shp_id NUMBER) PARTITION BY HASH (cust_shp_id) (PARTITION shp_id1 TABLESPACE tblspc01, PARTITION shp_id2 TABLESPACE tblspc02, PARTITION shp_id3 TABLESPACE tblspc03) ENABLE ROW MOVEMENT; The big difference in how the records are divided among partitions between the hash partition example and the range partition example is that the range partition code explicitly defines where each record goes, while the hash partition example allows Oracle to decide (by applying a hash algorithm) in which partition to place the record. (Note that we also enabled row movement for the table.) Examples of list and composite partitioning are shown with the subpartitioning information detailed in the next paragraph. In addition to breaking tables apart into partitions (for easier backup, recovery, or performance reasons), you may further break it apart into subpartitions. The subpartition_by_list clause syntax follows: SUBPARTITION BY LIST (column) [SUBPARTITION TEMPLATE { (SUBPARTITION subpartition_name [VALUES {DEFAULT | {val | NULL} [,...] } [partitioning_storage_clause] ) | hash_subpartition_qty } ] For example, we'll recreate the orders table once again, this time using a range-hash composite partition. In a range-hash composite partition, the partitions are broken apart by range values, while the subpartitions are broken apart by hashed values. List partitions and subpartitions are broken apart by a short list of specific values. Because you must list out all the values by which the table is partitioned, the partition value is best taken from a small list of values. In this example, we've added a column (shp_region_id) that allows four possible regions: CREATE TABLE orders (order_number NUMBER, order_date DATE, cust_nbr NUMBER, price NUMBER, qty NUMBER, cust_shp_id NUMBER, shp_region VARCHAR2(20) ) PARTITION BY RANGE(order_date) SUBPARTITION BY LIST(shp_region) SUBPARTITION TEMPLATE( (SUBPARTITION shp_region_north VALUES ('north','northeast','northwest'), SUBPARTITION shp_region_south VALUES ('south','southeast','southwest'), SUBPARTITION shp_region_central VALUES ('midwest'), SUBPARTITION shp_region_other VALUES ('alaska','hawaii','canada') (PARTITION pre_yr_2000 VALUES LESS THAN TO_DATE('01-JAN-2000', 'DD-MON-YYYY'), PARTITION pre_yr_2004 VALUES LESS THAN TO_DATE('01-JAN-2004', 'DD-MON-YYYY' PARTITION post_yr_2004 VALUES LESS THAN MAXVALUE) ) ENABLE ROW MOVEMENT; This code example not only sends the records of the table to one of three partitions based on the order_date, it further partitions the records into one of four subpartitions based on the region where the order is being shipped and on the value of the shp_region column. By using the SUBPARTITION TEMPLATE, you apply the same set of subpartitions to each partition. You can manually override this behavior by specifying subpartitions for each partition. You may also subpartition using a hashing algorithm. The subpartition_by_hash clause follows: SUBPARTITION BY HASH (column [,...]) {SUBPARTITIONS qty [STORE IN (tablespace_name [,...]) ] | SUBPARTITION TEMPLATE { (SUBPARTITION subpartition_name [VALUES {DEFAULT | {val | NULL} [,...] } [partitioning_storage_clause] ) | hash_subpartition_qty } The table_partition_description clause referenced in the partitioning syntax (in the earlier section Oracle partitioned and subpartitioned tables) is, in itself, very flexible and supports precise handling of LOB and VARRAY data: [segment_attr_clause] [[NO] COMPRESS [int]] [OVERFLOW segment_attr_clause] [partition_level_subpartition_clause] [{ LOB { (lob_item [,...] ) STORE AS lob_param_clause | (lob_item) STORE AS {lob_segname (log_param_clause) | log_segname | (log_param_clause) } } | VARRAY varray_item {[{[ELEMENT] IS OF [TYPE] (ONLY type_name) | [NOT] SUBSTITUTABLE AT ALL LEVELS}] STORE AS LOB { log_segname | [log_segname] (log_param_clause) } | [{[ELEMENT] IS OF [TYPE] (ONLY type_name) | [NOT] SUBSTITUTABLE AT ALL LEVELS}] The partition_level_subpartition_clause syntax follows: {SUBPARTITIONS hash_subpartition_qty [STORE IN (tablespace_name [,...])] | SUBPARTITION subpartition_name [VALUES {DEFAULT | {val | NULL} [,...] ] [partitioning_storage_clause] } The partition_storage_clause (see the earlier section Oracle partitioned and subpartitioned tables), like the table-level storage_clause defined earlier, defines how elements of a partition (or subpartition) are stored. The syntax follows: [{TABLESPACE tablespace_name] | [OVERFLOW TABLESPACE tablespace_name] | VARRAY varray_item STORE AS LOB log_segname | LOB (lob_item) STORE AS { (TABLESPACE tablespace_name) | Log_segname [(TABLESPACE tablespace_name)] }] In this final partitioning example, we'll again recreate the orders table using a composite range-hash partition, this time with LOB (actually, an NCLOB column) and storage elements: CREATE TABLE orders (order_number NUMBER, order_date DATE, cust_nbr NUMBER, price NUMBER, qty NUMBER, cust_shp_id NUMBER, shp_region VARCHAR2(20), order_desc NCLOB ) PARTITION BY RANGE(order_date) SUBPARTITION BY HASH(cust_shp_id) (PARTITION pre_yr_2000 VALUES LESS THAN TO_DATE('01-JAN-2000', 'DD-MON-YYYY') TABLESPACE tblspc01 LOB (order_desc) STORE AS (TABLESPACE tblspc_a01 STORAGE (INITIAL 10M NEXT 20M) ) SUBPARTITIONS subpartn_a, PARTITION pre_yr_2004 VALUES LESS THAN TO_DATE('01-JAN-2004', 'DD-MON-YYYY') TABLESPACE tblspc02 LOB (order_desc) STORE AS (TABLESPACE tblspc_a02 STORAGE (INITIAL 25M NEXT 50M) ) SUBPARTITIONS subpartn_b TABLESPACE tblspc_x07, PARTITION post_yr_2004 VALUES LESS THAN MAXVALUE (SUBPARTITION subpartn_1, SUBPARTITION subpartn_2, SUBPARTITION subpartn_3 SUBPARTITION subpartn_4) ) ENABLE ROW MOVEMENT; In this somewhat more complex example, we now define the orders table with the added NCLOB table called order_desc. In the pre_yr_2000 and pre_yr_2004 partitions, we specify that all of the non-LOB data goes to tablespaces tblspc01 and tblspc02, respectively. However, the NCLOB values of the order_desc column will be stored in the tblespc_a01 and tblspc_a02, respectively, with their own unique storage characteristics. Note that the subpartition subpartn_b under partition pre_yr_2004 is also stored in its own tablespace, tblspc_x07. Finally, the last partition (post_yr_2004) and its subpartitions are stored in the default tablespace for the orders table because no partition- or subpartition-level TABLESPACE clause overrides the default. Altering partitioned and subpartition tablesAnything about partitions and subpartitions that is explicitly set by the CREATE TABLE statement may be altered after the table is created. Many of the clauses shown later are merely repetitions of clauses that are described in the earlier section about creating partitioned tables; for example, the SUBPARTITION TEMPLATE clause or the MAPPING TABLE clause. Consequently, these clauses will not be repeated. Altering the partitions and/or subpartitions of an Oracle table is governed by this syntax: ALTER TABLE table_name [MODIFY DEFAULT ATTRIBUTES [FOR PARTITION partn_name] [physical_attributes_clause] [storage_clause] [PCTTHRESHOLD int] [{ADD OVERFLOW ... | OVERFLOW ...}] [[NO]COMPRESS] [{LOB (lob_name) | VARRAY varray_name} [(lob_parameter_clause)] ] [COMPRESS int | NOCOMPRESS] [SET SUBPARTITION TEMPLATE {hash_subpartn_quantity | (SUBPARTITION subpartn_name [partn_list] [storage_clause])}] [MODIFY PARTITION partn_name { [table_partition_description] | [[REBUILD] UNUSABLE LOCAL INDEXES] | [ADD [subpartn specification] ] | [COALESCE SUBPARTITION [[NO]PARALLEL] [update_index_clause] ] | [{ADD | DROP} VALUES (partn_value [,...] ) ] | [MAPPING TABLE {ALLOCATE EXTENT ... | DEALLOCATE UNUSUED ...} ] [MODIFY SUBPARTITION subpartn_name {hash_subpartn_attributes | list_subpartn_attributes} ] [MOVE {PARTITION | SUBPARTITION} partn_name [MAPPING TABLE] [table_partition_description] [[NO]PARALLEL] [update_index_clause] ] [ADD PARTITION [partn_name] [p table_partition_description] [[NO]PARALLEL] [update_index_clause] ] [COALESCE PARTITION [[NO]PARALLEL] [update_index_clause] ] [DROP {PARTITION | SUBPARTITION} partn_name [[NO]PARALLEL] [update_index_clause] ] [RENAME {PARTITION | SUBPARTITION} old_partn_name TO new_partn_name] [TRUNCATE {PARTITION | SUBPARTITION} partn_name [{DROP | REUSE} STORAGE] [[NO]PARALLEL] [update_index_clause] ] [SPLIT {PARTITION | SUBPARTITION} partn_name {AT | VALUES} (value [,...]) [INTO (PARTITION [partn_name1] [table_partition_description] , (PARTITION [partn_name2] [table_partition_description] ) ] [[NO]PARALLEL] [update_index_clause] ] [MERGE {PARTITION | SUBPARTITION} partn_name1 , partn_name2 [INTO PARTITION [partn_name] [partn_attributes] ] [[NO]PARALLEL] [update_index_clause] ] [EXCHANGE {PARTITION | SUBPARTITION} partn_name WITH TABLE table_name [{INCLUDING | EXCLUDING} INDEXES] [{WITH | WITHOUT} VALIDATION] [[NO]PARALLEL] [update_index_clause] [EXCEPTIONS INTO table_name] ] where:
{ UPDATE | INVALIDATE} GLOBAL INDEXES] | UPDATE INDEXES [ (index_name ( {index_partn|index_subpartn} ) ) [,...] }
There are a couple of notes to remember about altering a partitioned table. First, altering a partition on a table that serves as the source for one or more materialized views requires that the materialized views be refreshed. Second, bitmap join indexes defined on the partitioned table being altered will be marked UNUSABLE. Third, several restrictions apply if the partitions (or subpartitions) are ever spread across tablespaces that use different block sizes. Refer to the Oracle documentation when attempting these sorts of alterations to a partitioned table. In the next few examples, assume we are using the orders table partitioned as shown below: CREATE TABLE orders (order_number NUMBER, order_date DATE, cust_nbr NUMBER, price NUMBER, qty NUMBER, cust_shp_id NUMBER) PARTITION BY RANGE(order_date) (PARTITION pre_yr_2000 VALUES LESS THAN TO_DATE('01-JAN-2000', 'DD-MON-YYYY'), PARTITION pre_yr_2004 VALUES LESS THAN TO_DATE('01-JAN-2004', 'DD-MON-YYYY' PARTITION post_yr_2004 VALUES LESS THAN MAXVALUE) ) ; The following statement will mark all of the local index partitions as UNUSABLE in the orders table for the post_yr_2004 partition: ALTER TABLE orders MODIFY PARTITION post_yr_2004 UNUSABLE LOCAL INDEXES; However, we've decided to now split the orders table partition post_yr_2004 into two all new partitions, pre_yr_2008 and post_yr_2008. Values that are now less than MAXVALUE will be stored in the post_yr_2008 partition, while values less than `01-JAN-2008' will be stored in pre_yr_2008: ALTER TABLE orders SPLIT PARITION post_yr_2004 AT (TO_DATE('01-JAN-2008','DD-MON-YYYY')) INTO (PARTITION pre_yr_2008, PARTITION post_yr_2008); Assuming that the orders table contained a LOB or a VARRAY column, we could further refine the alteration by including additional details for handling these columns, while also updating the global indexes as the operation completes: ALTER TABLE orders SPLIT PARITION post_yr_2004 AT (TO_DATE('01-JAN-2008','DD-MON-YYYY')) INTO (PARTITION pre_yr_2008 LOB (order_desc) STORE AS (TABLESPACE order_tblspc_a1), PARTITION post_yr_2008) LOB (order_desc) STORE AS (TABLESPACE order_tblspc_a1) ) UPDATE GLOBAL INDEXES; Now, assuming the orders table has been grown at the upper end, lets merge together the partitions at the lower end: ALTER TABLE orders MERGE PARTITIONS pre_yr_2000, pre_yr_2004 INTO PARTITION yrs_2004_and_earlier; After a few years more, we might want to even get rid of the oldest partition, or if not get rid of it, at least give it a better name: ALTER TABLE orders DROP PARTITION yrs_2004_and_earlier; ALTER TABLE orders RENAME PARTITION yrs_2004_and_earlier TO pre_yr_2004; Finally, let's truncate a table partition, delete all of its data, and return the empty space for use by other objects in the tablespace: ALTER TABLE orders TRUNCATE PARTITION pre_yr_2004 DROP STORAGE; As the examples illustrate, anything relating to partitioning and subpartitioning that can be created with the Oracle CREATE TABLE statement can later be changed, augmented, or cut down using the Oracle ALTER TABLE statement. Organized tables: heaps, IOTs, and external tablesOracle 10g allows powerful means of controlling the physical storage behavior of tables. The most useful aspect of the ORGANIZATION HEAP clause is that you can now compress an entire table within Oracle. This is extremely useful for reducing disk storage costs in database environments with multi-terabyte tables. The following example creates the orders table in a compressed and logged heap along with a primary key constraint and storage details: CREATE TABLE orders (order_number NUMBER, order_date DATE, cust_nbr NUMBER, price NUMBER, qty NUMBER, cust_shp_id NUMBER, shp_region VARCHAR2(20), order_desc NCLOB, CONSTRAINT ord_nbr_pk PRIMARY KEY (order_number) ) ORGANIZATION HEAP COMPRESS LOGGING PCTTHRESHOLD 2 STORAGE (INITIAL 4M NEXT 2M PCTINCREASE 0 MINEXTENTS 1 MAXEXTENTS 1) OVERFLOW STORAGE (INITIAL 4M NEXT 2M PCTINCREASE 0 MINEXTENTS 1 MAXEXTENTS 1) ENABLE ROW MOVEMENT; To define the same table using an index-organized table based on the order_date column, we would use this syntax: CREATE TABLE orders (order_number NUMBER, order_date DATE, cust_nbr NUMBER, price NUMBER, qty NUMBER, cust_shp_id NUMBER, shp_region VARCHAR2(20), order_desc NCLOB, CONSTRAINT ord_nbr_pk PRIMARY KEY (order_number) ) ORGANIZATION HEAP INCLUDING order_date PCTTHRESHOLD 2 STORAGE (INITIAL 4M NEXT 2M PCTINCREASE 0 MINEXTENTS 1 MAXEXTENTS 1) OVERFLOW STORAGE (INITIAL 4M NEXT 2M PCTINCREASE 0 MINEXTENTS 1 MAXEXTENTS 1) ENABLE ROW MOVEMENT; Finally, we will create an external table that stores our customer shipping information called cust_shipping_external. The code shown in bold is the opaque_format_spec: CREATE TABLE cust_shipping_external (external_cust_nbr NUMBER(6), cust_shp_id NUMBER, shipping_company VARCHAR2(25) ) ORGANIZATION EXTERNAL (TYPE oracle_loader DEFAULT DIRECTORY dataloader ACCESS PARAMETERS (RECORDS DELIMITED BY newline BADFILE 'upload_shipping.bad' DISCARDFILE 'upload_shipping.dis' LOGFILE 'upload_shipping.log' SKIP 20 FIELDS TERMINATED BY "," OPTIONALLY ENCLOSED BY '"' (client_id INTEGER EXTERNAL(6), shp_id CHAR(20), shipper CHAR(25) ) ) LOCATION ('upload_shipping.ctl') ) REJECT LIMIT UNLIMITED; Examining this example, we see that the external table type is ORACLE_LOADER and the default directory is DATALOADER. The example illustrates the fact that you define the metadata of the table within Oracle and then describe how that metadata references a data source outside of the Oracle database server itself. Oracle XMLType and object-type tablesWhen an Oracle XMLType table is created, Oracle automatically stores the data in a CLOB column, unless you create an XML schema-based table. (For details on Oracle's XML support, see Oracle's XMLDB Developer's Guide.) The following code example first creates an XMLType table, distributors, with the implicit CLOB data storage, and followed by a second code example, suppliers, with a more sophisticated XML-schema definition: CREATE TABLE distributors OF XMLTYPE; CREATE TABLE suppliers OF XMLTYPE XMLSCHEMA "http://www.lookatallthisstuff.com/suppliers.xsd" ELEMENT "vendors"; A key advantage of a table based on an XML schema is that you can create B-Tree indexes on them. In the following example, we create an index on suppliercity: CREATE INDEX supplier-city-index ON suppliers (S."XMLDATA"."ADDRESS"."CITY"); You may similarly create tables using a mix of standard and XMLTYPE columns. In this case, the XMLTYPE column may store its data as a CLOB, or it may store its data in an object-relational column of a structure determined by your specification. For example, we'll recreate the distributors table (this time with some added storage specifications) and the suppliers table with columns of both standard and XMLTYPE: CREATE TABLE distributors (distributor_id NUMBER, distributor_spec XMLTYPE) XMLTYPE distributor_spec STORE AS CLOB (TABLESPACE tblspc_dist STORAGE (INITIAL 10M NEXT 5M) CHUNK 4000 NOCACHE LOGGING); CREATE TABLE suppliers (supplier_id NUMBER, supplier_spec XMLTYPE) XMLTYPE supplier_spec STORE AS OBJECT RELATIONAL XMLSCHEMA "http://www.lookatallthisstuff.com/suppliers.xsd" ELEMENT "vendors" OBJECT IDENTIFIER IS SYSTEM GENERATED OIDINDEX vendor_ndx TABLESPACE tblspc_xml_vendors; When creating XML and object tables, you may refer to inline_ref_constraint and table_ref_constraint clauses. The syntax for an inline_ref_constraint clause is: {SCOPE IS scope_table | WITH ROWID | [CONSTRAINT constraint_name] REFERENCES object [ (column_name) ] [ON DELETE {CASCADE | SET NULL} ] [constraint_state] } The only difference between an inline reference constraint and a table reference constraint is that inline reference constraints operate at the column level and table reference constraints operate at the table level, respectively. (This is essentially the same behavior and coding rule as standard relational constraints like PRIMARY KEY or FOREIGN KEY.) The syntax for a table_ref_constraint follows: {SCOPE FOR (ref_col | ref_attr) IS scope_table | REF (ref_col | ref_attr) WITH ROWID | [CONSTRAINT constraint_name] FOREIGN KEY (ref_col | ref_attr) REFERENCES object [ (column_name) ] [ON DELETE {CASCADE | SET NULL} ] [constraint_state] } The constraint_state clause contains a number of options that have already been defined earlier in the Oracle CREATE TABLE statement. However, these options are only applied to the condition of the scope reference: [NOT] DEFERRABLE INITIALLY {IMMEDIATE | DEFERRED} {ENABLE | DISABLE} {VALIDATE | NOVALIDATE} {RELY | NORELY} EXCEPTIONS INTO table_name USING INDEX {index_name | ( create_index_statement ) | index_attributes} Object-type tables are useful for creating tables containing user-defined types. For example, the following code creates the building_type type: CREATE TYPE OR REPLACE building_type AS OBJECT (building_name VARCHAR2(100), building_address VARCHAR2(200) ); We can then create a table called offices_object_table that contains the object and defines some of its characteristics, such as OID information. In addition, we'll create two more tables, based upon building_type, that reference the object type as an inline_ref_constraint and a table_ref_constraint, respectively: CREATE TABLE offices_object_table OF building_type (building_name PRIMARY KEY) OBJECT IDENTIFIER IS PRIMARY KEY; CREATE TABLE leased_offices (office_nbr NUMBER, rent DEC(9,3), office_ref REF building_type SCOPE IS offices_object_table); CREATE TABLE owned_offices (office_nbr NUMBER, payment DEC(9,3), office_ref REF building_type CONSTRAINT offc_in_bld REFERENCES offices_object_table); In these examples, the SCOPE IS clause defines the inline_ref_constraint, while the CONSTRAINT clause defines the table_ref_constraint. Oracle ALTER TABLEWhen using the Oracle command ALTER TABLE, you are able to ADD, DROP, or MODIFY every aspect of each element of the table. For example, the syntax diagram shows that the method for adding or modifying an existing column includes its attributes, but we need to explicitly state that the attributes include any Oracle-specific extensions. So while the ANSI standard only lets you modify attributes such as DEFAULT or NOT NULL, as well as column-level constraints assigned to the column, Oracle also allows you to alter any special characteristics like LOB, VARRAY, NESTED TABLE, index-organized table, CLUSTER, or PARTITION settings that might exist. For example, the following code adds a new column to a table in Oracle, and also adds a new, unique constraint to that table: ALTER TABLE titles ADD subtitle VARCHAR2(32) NULL CONSTRAINT unq_subtitle UNIQUE; When a foreign key constraint is added to a table, the DBMS verifies that all existing data in the table meets that constraint. If they do not, the ALTER TABLE fails.
Oracle also allows multiple actions, such as ADD or MODIFY, to multiple columns to be performed by enclosing the actions within parentheses. For example, the following command adds several columns to a table with a single statement: ALTER TABLE titles ADD (subtitles VARCHAR2(32) NULL, year_of_copyright INT, date_of_origin DATE); PostgreSQLPostgreSQL supports the ANSI standards for CREATE and ALTER TABLE with a couple of extensions that enable you to quickly build a new table from existing table definitions. Following is the syntax for CREATE TABLE: CREATE [LOCAL][[TEMP]ORARY TABLE table_name (column_name datatype attributes [,...] | CONSTRAINT constraint_name [{NULL | NOT NULL}] {[UNIQUE] | [PRIMARY KEY (column_name [,...])] | [CHECK (expression) ] | REFERENCES reference_table (reference_column [,...]) [MATCH {FULL | PARTIAL | default}] [ON {UPDATE | DELETE} {CASCADE | NO ACTION | RESTRICT | SET NULL | SET DEFAULT value}] [[NOT] DEFERRABLE] [INITIALLY {DEFERRED | IMMEDIATE}] } [,...] | [table_constraint][,...] [WITH[OUT] OIDE] [INHERITS (inherited_table [,...])] [ON COMMIT {DELETE | PRESERVE} ROWS] [AS select_statement] The PostgreSQL syntax for ALTER TABLE is: ALTER TABLE [ONLY] table_name [*] [ADD [COLUMN] column_name datatype attributes [...] ] | [ALTER [COLUMN] column_name {SET DEFAULT value | DROP DEFAULT | SET STATISTICS int}] | [RENAME [COLUMN] column_name TO new_column_name] | [RENAME TO new_table_name] | [ADD table_constraint] | [DROP CONSTRAINT constraint_name RESTRICT ] | [OWNER TO new_owner] The parameters are as follows:
[FOREIGN KEY (column_name [,...]) REFERENCES...]
A PostgreSQL table cannot have more than 1,600 columns. However, you should limit the number of columns to well below 1,600 for performance reasons. For example: CREATE TABLE distributors (name VARCHAR(40) DEFAULT 'Thomas Nash Distributors', dist_id INTEGER DEFAULT NEXTVAL('dist_serial'), modtime TIMESTAMP DEFAULT CURRENT_TIMESTAMP);
PostgreSQL's implementation of ALTER TABLE allows the addition of extra columns using the ADD keyword. Existing columns may have new default values assigned to them using ALTER COLUMN...SET DEFAULT, while ALTER COLUMN...DROP DEFAULT allows the complete erasure of a column-based default. In addition, new defaults may be added to columns using the ALTER clause, but only newly inserted rows will be affected by such new default values. RENAME allows new names for existing columns and tables. SQL ServerSQL Server offers a plethora of options when defining or altering a table, its columns, and its table-level constraints. Its CREATE TABLE syntax is: CREATE TABLE table_name ({column_name datatype [ [DEFAULT default_value] | {IDENTITY [(seed,increment) [NOT FOR REPLICATION]]] [ROWGIDCOL][NULL | NOT NULL] | [{PRIMARY KEY | UNIQUE} [CLUSTERED | NONCLUSTERED] [WITH FILLFACTOR = int] [ON {filegroup | DEFAULT}] ] | [[FOREIGN KEY] REFERENCES reference_table[(reference_column[,...])] [ON {DELETE | UPDATE} {CASCADE | NO ACTION}] [NOT FOR REPLICATION] | [CHECK [NOT FOR REPLICATION] (expression) | [COLLATE collation_name] | column_name AS computed_column_expression } [,...] | [table_constraint] [,...] ) [ON {filegroup | DEFAULT} ] [TEXTIMAGE_ON {filegroup | DEFAULT} ] The SQL Server version of ALTER TABLE is: ALTER TABLE table_name [ALTER COLUMN column_name new_datatype attributes {ADD | DROP} ROWGUIDCOL] | [ADD [COLUMN] column_name datatype attributes][,...] | [WITH CHECK | WITH NOCHECK] ADD table_constraint][,...] | [DROP { [ CONSTRAINT ] constraint_name | COLUMN column_name }] [,...] | [{ CHECK | NOCHECK } CONSTRAINT { ALL | constraint_name [,...] }] | [{ ENABLE | DISABLE } TRIGGER { ALL | trigger_name [,...] }] The parameters are as follows:
CREATE TABLE people (people_id CHAR(4) CONSTRAINT pk_dist_id PRIMARY KEY CLUSTERED CONSTRAINT ck_dist_id CHECK (dist_id LIKE ' [A-Z][A-Z][A-Z][A-Z]'), people_name VARCHAR(40) NULL, people_addr1 VARCHAR(40) NULL, people_addr2 VARCHAR(40) NULL, city VARCHAR(20) NULL, state CHAR(2) NULL CONSTRAINT def_st DEFAULT ("CA") CONSTRAINT chk_st REFERENCES states(state_ID), zip CHAR(5) NULL CONSTRAINT ck_dist_zip CHECK(zip LIKE '[0-9][0-9][0-9][0-9][0-9]'), phone CHAR(12) NULL, sales_rep empid NOT NULL DEFAULT USER) GO The CHECK constraint on the people_id ensures an all-alphabetic ID, while the one on zip ensures an all-numeric value. The REFERENCES constraint on state performs a look-up on the states table. The REFERENCES constraint is essentially the same as a CHECK constraint, except that it derives its list of acceptable values from the values stored in another column. This example illustrates how column-level constraints are named using the CONSTRAINT constraint_name syntax.
SQL Server allows any column-level constraint to be named by specifying CONSTRAINT constraint_name..., and then the text of the constraint. Several constraints may be applied to a single column, as long as they are not mutually exclusive (for example, PRIMARY KEY and NULL). SQL Server also allows a local temporary table to be created, but not using ANSI syntax. A local temporary table, which is stored in the tempdb database, requires a prefix of a single pound sign (#) to the name of the table. The local temporary table is usable by the person or process that created it, and is deleted when the person logs out or the process terminates. A global temporary table, which is usable to all people and processes that are currently logged in, can be established by prefixing two pound signs (##) to the name of the table. The global temporary table is deleted when its process terminates or its creator logs out. SQL Server also allows the creation of tables with columns that contain a computed value. Such a column does not actually contain data. Instead, it is a virtual column containing an expression using other columns already in the table. For example, a computed column could have an expression, such as order_cost AS (price * qty). Computed columns also can be constants, functions, variables, noncomputed columns, or any of these combined with each other using operators. Any of the column-level constraints shown earlier also may be declared at the table level. That is, PRIMARY KEY constraints, FOREIGN KEY constraints, CHECK constraints, and others may be declared after all the columns have been defined in the CREATE TABLE statement. This is very useful for constraints that cover more than one column. For example, a column-level UNIQUE constraint can be applied only to that column. However, declaring the constraint at the table level allows it to span several columns. Here is an example of both column-level and table-level constraints: -- Creating a column-level constraint CREATE TABLE favorite_books (isbn CHAR(100) PRIMARY KEY NONCLUSTERED, book_name VARCHAR(40) UNIQUE, category VARCHAR(40) NULL, subcategory VARCHAR(40) NULL, pub_date DATETIME NOT NULL, purchase_date DATETIME NOT NULL) GO -- Creating a table-level constraint CREATE TABLE favorite_books (isbn CHAR(100) NOT NULL, book_name VARCHAR(40) NOT NULL, category VARCHAR(40) NULL, subcategory VARCHAR(40) NULL, pub_date DATETIME NOT NULL, purchase_date DATETIME NOT NULL, CONSTRAINT pk_book_id PRIMARY KEY NONCLUSTERED (isbn) WITH FILLFACTOR=70, CONSTRAINT unq_book UNIQUE CLUSTERED (book_name,pub_date)) GO These two commands provide nearly the same results, except that the table-level UNIQUE constraint has two columns, whereas only one column is included in the column-level UNIQUE constraint. The following example adds a new CHECK constraint to a table, but does not check to ensure that the existing values in the table pass the constraint: ALTER TABLE favorite_book WITH NOCHECK ADD CONSTRAINT extra_check CHECK (ISBN > 1) GO In this example, we further add a new column with an assigned DEFAULT value that is placed in each existing record in the table: ALTER TABLE favorite_book ADD reprint_nbr INT NULL CONSTRAINT add_reprint_nbr DEFAULT 1 WITH VALUES GO -- Now, disable the constraint ALTER TABLE favorite_book NOCHECK CONSTRAINT add_reprint_nbr GO See Also
A trigger is a special kind of stored procedure that fires automatically (hence the term "trigger") when a specific data-modification statement is executed against a table. The trigger is directly associated with the table and is considered a dependent object. For example, you might want all of the part_numbers in the sales table to be updated when a part_number is changed in the products table, thus ensuring that part numbers are always in sync.
SQL2003 SyntaxCREATE TRIGGER trigger_name {BEFORE | AFTER} {DELETE | INSERT | UPDATE [OF column [,...]} ON table_name [REFERENCING {OLD {[ROW] | TABLE} [AS] old_name | NEW {ROW | TABLE} [AS] new_name}] [FOR EACH { ROW | STATEMENT }] [WHEN (conditions)] [BEGIN ATOMIC] code_block [END] Keywords
Rules at a GlanceTriggers, by default, fire once at the statement level. That is, a single INSERT statement might insert 500 rows into a table, but an insert trigger on that table fires only one time. Some vendors allow a trigger to fire for each row of the data-modification operation. So, a statement that inserts 500 rows into a table that has a row-level insert trigger fires 500 times, once for each inserted row. In addition to being associated with a specific data-modification statement (INSERT, UPDATE, or DELETE) on a given table, triggers are associated with a specific time of firing. In general, triggers can fire BEFORE the data-modification statement is processed, AFTER it is processed, or (when supported by the vendor) INSTEAD OF processing the statement. Triggers that fire before or instead of the data-modification statement do not see the changes that the statement renders, while those that fire afterwards can see and act upon the changes that the data-modification statement renders. Triggers make use of two pseudotables. They are pseudotables in the sense that they are not declared with a CREATE TABLE statement, but they exist logically on the database. The pseudotables have different names on the different platforms, but we'll call them Before and After here. They are structured exactly the same as the table a trigger is associated with. However, they contain snapshots of the table's data. The Before table contains a snapshot of all the records in the table before the trigger fired, while the After table contains a snapshot of all the records in the table after the event occurred that fired the trigger. You can then use comparison operations to compare the data in the table before and after the event fired the trigger to determine exactly what you want to happen. Here is an Oracle BEFORE trigger that uses the OLD and NEW pseudotables to compare values. (By way of comparison, SQL Server uses the DELETED and INSERTED pseudotables in the same way, while DB2 requires that you provide a REFERENCING clause to give names to the pseudotables.) This trigger creates an audit record before changing an employee's pay record: CREATE TRIGGER if_emp_changes BEFORE DELETE OR UPDATE ON employee FOR EACH ROW WHEN (new.emp_salary <> old.emp_salary) BEGIN INSERT INTO employee_audit VALUES ('old', :old.emp_id, :old.emp_salary, :old.emp_ssn); END; You can also take advantage of capabilities within each database platform to make your triggers more powerful and easier to program. For example, Oracle has a special IF...THEN clause for use just in triggers. This IF...THEN clause takes the form: IF DELETING THEN, IF INSERTING THEN, or IF UPDATING THEN. The following example builds an Oracle DELETE and UPDATE trigger that uses the IF DELETING THEN clauses: CREATE TRIGGER if_emp_changes BEFORE DELETE OR UPDATE ON employee FOR EACH ROW BEGIN IF DELETING THEN INSERT INTO employee_audit VALUES ('DELETED', :old.emp_id, :old.emp_salary, :old.emp_ssn); ELSE INSERT INTO employee_audit VALUES ('UPDATED', :old.emp_id, :new.emp_salary, :old.emp_ssn); END IF; END; This SQL Server example adds a new table called contractor to the database. All records in the employee table that indicate that the employee is a contractor were moved into the contractor table. Now all new employees inserted into the employee table will go into the contractor table instead through an INSTEAD OF INSERT trigger: CREATE TRIGGER if_emp_is_contractor INSTEAD OF INSERT ON employee BEGIN INSERT INTO contractor SELECT * FROM inserted WHERE status = 'CON' INSERT INTO employee SELECT * FROM inserted WHERE status = 'FTE' END GO Adding triggers to a table that already has data in it does not cause the triggers to fire. A trigger will fire only for data-modification statements declared in the trigger definition that occur after the trigger is created. A trigger generally ignores structural changes to tables, such as an added column or an altered datatype on an existing column, after the trigger has been created unless a table modification directly interferes with the operation of the trigger. For example, adding a new column to a table will result in any existing trigger ignoring the new column, except for UPDATE triggers. On the other hand, removing a column from a table that is used by a trigger will cause the trigger to fail every time it executes. Programming Tips and GotchasOne of the key programming issues associated with triggers is the inappropriate and uncontrolled use of nested triggers and recursive triggers. A nested trigger is a trigger that invokes a data-manipulation operation that causes other triggers to fire. For example, assume we have three tables, T1, T2, and T3. Table T1 has a BEFORE INSERT trigger that inserts a record into table T2. Table T2 also has a BEFORE INSERT trigger that inserts a record into table T3. Although this is not necessarily a bad thing if your logic is well considered and fully thought out, it does introduce two problems. First, an insert into table T1 requires many more I/O operations and transactions than a simple INSERT statement against table T1. Second, you can get yourself into hot water if, in fact, table T3 performs an INSERT operation against table T1. In a case like that, you may have a looping trigger process that can consume all available disk space and even shut down the server. Recursive triggers are triggers that can fire themselves; for example an INSERT trigger that performs an INSERT against its own base table. If the procedural logic within the code_body is not properly constructed, you can cause a looping trigger error. However, recursive triggers often require setting a special configuration flag before they can be used on the various database platforms. DB2DB2 supports a superset of the ANSI SQL standard for the CREATE TRIGGER statement. It does not support an ALTER TRIGGER statement. The CREATE TRIGGER syntax is: CREATE TRIGGER trigger_name {NO CASCADE BEFORE | AFTER | INSTEAD OF} {[DELETE] | [INSERT] | [UPDATE [OF (column [,...]) ]} ON table_name [REFERENCING {OLD[_TABLE] [AS] old_name | NEW[_TABLE] [AS] new_name} DEFAULTS NULL] [FOR EACH { ROW | STATEMENT }] MODE DB2SQL [WHEN (conditions)] code_block where:
DB2 does not prevent a new trigger from being created that has the same definition as an older trigger. Thus, it's possible to have two (or more) AFTER DELETE triggers on a single table. In a situation like this, DB2 will always fire the triggers in the order they were created. Be careful declaring a BEFORE DELETE trigger on a table with a self-referencing constraint and a delete rule of CASCADE. Such a trigger may produce results that you did not expect!
DB2 does not specifically drop the triggers associated with a table or view when the table or view itself is dropped. Consequently, you should first issue a DROP TRIGGER statement followed by a DROP TABLE statement to avoid leaving orphaned triggers behind. Conversely, triggers are not reinstated if a table is dropped and then recreated. The triggers should be recreated as well. BEFORE and AFTER triggers are allowed on typed tables at any level of a typed-table hierarchy. Triggers may activate on rows of subtables. If a SQL statement activates multiple triggers, the triggers will be executed in their creation order, even if they are attached to different tables in the typed-table hierarchy. MySQLNot supported. OracleOracle supports the ANSI standard for CREATE TRIGGER, with several additions and variations: CREATE [OR REPLACE] TRIGGER trigger_name {BEFORE | AFTER | INSTEAD OF} { {[object_event] [database_event] [...] ON {DATABASE | schema.SCHEMA} } | {[DELETE] [OR] [INSERT] [OR] [UPDATE [OF column [,...] ]] [...]} ON {table_name | [NESTED TABLE column_name OF] view_name} [REFERENCING {[OLD [AS] old_name] [NEW [AS] new_name] [PARENT [AS] parent_name]} [FOR EACH ROW] } [WHEN (conditions)] code_block The syntax for ALTER TRIGGER, which allows you to rename, enable, or disable a trigger without dropping and recreating it, is shown below: ALTER TRIGGER trigger_name { {ENABLE | DISABLE} | RENAME TO new_name | COMPILE [compiler_directives] [DEBUG] [REUSE SETTINGS]} where:
When referencing values in the OLD and NEW pseudotables, the values must be prefaced with a colon (:), except in the trigger's WHEN clause, where no colons are used. In this example, we'll call a procedure in the code_body and use both :OLD and :NEW values as arguments: CREATE TRIGGER scott.sales_check BEFORE INSERT OR UPDATE OF ord_id, qty ON scott.sales FOR EACH ROW WHEN (new.qty > 10) CALL check_inventory(:new.ord_id, :new.qty, :old.qty); Multiple trigger types may be combined into a single trigger command if they are of the same level (row or statement) and they are on the same table. When triggers are combined in a single statement, the clauses IF INSERTING THEN, IF UPDATING THEN, and IF DELETING THEN may be used in the PL/SQL block to break the code logic into distinct segments. An ELSE clause also can be used in this structure. Following is an example of a database_event-style trigger: CREATE TRIGGER track_errors AFTER SERVERERROR ON DATABASE BEGIN IF (IS_SERVERERROR (04030)) THEN INSERT INTO errors ('Memory error'); ELSE (IS_SERVERERROR (01403)) THEN INSERT INTO errors ('Data not found'); END IF; END; In another example, we can create a trigger that is SCHEMA-wide in scope: CREATE OR REPLACE TRIGGER create_trigger AFTER CREATE ON scott.SCHEMA BEGIN RAISE_APPLICATION_ERROR (num => -20000, msg => 'Scott created an object'); END; PostgreSQLPostgreSQL's implementation of CREATE TRIGGER offers a subset of features found in the ANSI standard. On PostgreSQL, a trigger may fire BEFORE a data-modification operation is attempted on a record and before any constraints are fired or it may fire AFTER a data-manipulation operation fires (and after constraints have been checked), making all operations involved in the transaction visible to the trigger. Finally, the trigger may fire INSTEAD OF the data-modification operation and completely replace the INSERT, UPDATE, or DELETE statement with some other behavior. The CREATE TRIGGER syntax is: CREATE TRIGGER trigger_name { BEFORE | AFTER } { {[DELETE] [OR | ,] [INSERT] [OR | ,] [UPDATE]} [OR ...] } ON table_name FOR EACH { ROW | STATEMENT } EXECUTE PROCEDURE function_name (parameters) The parameters are:
The following is an example of a PostgreSQL BEFORE trigger that checks at a row level to ensure that the specified distributor code exists in the distributors table before inserting or updating a row in the sales table: CREATE TRIGGER if_dist_exists BEFORE INSERT OR UPDATE ON sales FOR EACH ROW EXECUTE PROCEDURE check_primary_key ('dist_id', 'distributors', 'dist_id'); BEFORE and AFTER triggers are supported by the ANSI standard. INSTEAD OF triggers on PostgreSQL completely skip the data-modification operation that triggered them in favor of code that you substitute for the data-modification operation. SQL ServerSQL Server supports the basics of the ANSI standard with the addition of the INSTEAD OF trigger type and a column change check. It does not support the REFERENCING or WHEN clauses. Its syntax is: {CREATE | ALTER} TRIGGER trigger_name ON table_name [WITH ENCRYPTION] {FOR | AFTER | INSTEAD OF} {[DELETE] [,] [INSERT] [,] [UPDATE]} [WITH APPEND] [NOT FOR REPLICATION] AS [IF UPDATE(column) [{AND | OR} UPDATE(column)][...] ] code_block where:
SQL Server allows multiple triggers for a given data-manipulation operation on a table or view. Thus, three UPDATE triggers are possible on a single table. Multiple AFTER triggers are possible on a given table. Their specific order is undefined, though the first and last triggers can be explicitly declared using the sp_settriggerorder system stored procedure. Only one INSTEAD OF trigger is possible per INSERT, UPDATE, or DELETE statement on a given table. In SQL Server, any combination of triggers is made possible in a single trigger definition statement by separating each option with a comma. (When doing so, the same code fires for each statement in the combination definition.) SQL Server implicitly fires in the FOR EACH STATEMENT style of the ANSI standard. SQL Server instantiates two important pseudotables when a trigger is fired: deleted and inserted. They are equivalent to the before and after pseudotables, respectively, described earlier in the ANSI Rules at a Glance section. These tables are identical in structure to the table on which the triggers are defined, except that they contain the old data before the data-modification statement fired (deleted) and the new values of the table after the data-modification statement fired (inserted). The AS IF UPDATE(column) clause tests specifically for INSERT or UPDATE actions on a given column or columns, in the way the ANSI statement uses the UPDATE(column) syntax. Specify multiple columns by adding separate UPDATE(column) clauses after the first. Follow the AS IF UPDATE(column) clause with a Transact-SQL BEGIN...END block to allow the trigger to fire multiple Transact-SQL operations. This clause is functionally equivalent to the IF...THEN...ELSE operation. In addition to intercepting data modification statements as shown in the ANSI SQL example, SQL Server allows you to perform other sorts of actions when a data modification operation occurs. In the following example, we've decided that the table sales_archive_2002 is off limits and that anyone who attempts to alter data in these tables is to be notified of that restriction: CREATE TRIGGER archive_trigger ON sales_archive_2002 FOR INSERT, UPDATE AS RAISERROR (50009, 16, 10, 'No changes allowed to this table') GO SQL Server does not allow the following statements within the Transact-SQL code_block of a trigger: ALTER, CREATE, DROP, DENY, GRANT, REVOKE, LOAD, RESTORE, RECONFIGURE, or TRUNCATE. In addition, it does not allow any DISK statements or the UPDATE STATISTICS command. SQL Server also allows triggers to fire recursively using the recursive triggers setting of the sp_dboption system stored procedure. Recursive triggers, by their own action, cause themselves to fire again. For example, if an INSERT trigger on table T1 performs an INSERT operation on table T1, it might perform a recursive operation. Since recursive triggers can be dangerous, this functionality is disabled by default. Similarly, SQL Server allows nested triggers up to 32 levels deep. If any one of the nested triggers performs a ROLLBACK operation, no further triggers execute. An example of nested triggers is a trigger on table T1 firing an operation against table T2, which also has a trigger that fires an operation against table T3. The triggers cancel if an infinite loop is encountered. Nested triggers are enabled with the nested triggers setting of the system stored procedure sp_configure. If nested triggers are disabled, recursive triggers are disabled as well, despite the recursive triggers setting of sp_dboption. In the following example, we want to reroute the user activity that occurs on the people table, especially update transactions, so that changes to records in the people table are instead written to the people_reroute table. (A more elaborate version of the people table is shown in SQL Server in the CREATE/ALTER TABLE Statement section.) Our update trigger will record any changes to columns 2, 3, or 4 of the people table and write it to the people_reroute table. The trigger will also record which user issued the update transaction and the time of the transaction: CREATE TABLE people (people_id CHAR(4), people_name VARCHAR(40), people_addr VARCHAR(40), city VARCHAR(20), state CHAR(2), zip CHAR(5), phone CHAR(12), sales_rep empid NOT NULL) GO CREATE TABLE people_reroute (reroute_log_id UNIQUEIDENTIFIER DEFAULT NEWID( ), reroute_log_type CHAR (3) NOT NULL, reroute_people_id CHAR(4), reroute_people_name VARCHAR(40), reroute_people_addr VARCHAR(40), reroute_city VARCHAR(20), reroute_state CHAR(2), reroute_zip CHAR(5), reroute_phone CHAR(12), reroute_sales_rep empidNOT NULL, reroute_user sysname DEFAULT SUSER_SNAME( ), reroute_changed datetime DEFAULT GETDATE( ) ) GO CREATE TRIGGER update_person_data ON people FOR update AS IF (COLUMNS_UPDATED(people_name) OR COLUMNS_UPDATEE(people_addr) OR COLUMNS_UPDATED(city) ) BEGIN -- Audit OLD record. INSERT INTO people_reroute (reroute_log_type, reroute_people_id, reroute_people_name, reroute_people_addr, reroute_city) SELECT 'old', d.people_id, d.people_name, d.people_addr, d.city FROM deleted AS d -- Audit NEW record. INSERT INTO people_reroute (reroute_log_type, reroute_people_id, reroute_people_name, reroute_people_addr, reroute_city) SELECT 'new', n.people_id, n.people_name, n.people_addr, n.city FROM inserted AS n END GO Note that SQL Server CREATE statements allow deferred name resolution, meaning that a command is processed even if it refers to a database object that does not yet exist in the database. See Also
The CREATE TYPE statement allows you to create a user-defined type (UDT); that is, a user-defined datatype or "class" in object-oriented terms. UDTs extend SQL capabilities into the realm of object-oriented programming by allowing inheritance and other object-oriented features. You can also create something called typed tables with the CREATE TABLE statement using a previously created type made with the CREATE TYPE statement. Typed tables are based on UDTs and are equivalent to "instantiated classes" from object-oriented programming.
SQL2003 SyntaxCREATE TYPE type_name [UNDER supertype_name] [AS [new_udt_name] datatype [attribute] [,...] {[REFERENCES ARE [NOT] CHECKED [ON DELETE {NO ACTION | CASCADE | RESTRICT | SET NULL | SET DEFAULT} ] ] | [DEFAULT value] | [COLLATE collation_name] } ] [ [NOT] INSTANTIABLE] [ [NOT] FINAL] [REF IS SYTEM GENERATED | REF USING datatype [CAST {(SOURCE AS REF) | (REF AS SOURCE) WITH identifier} ] | REF new_udt_name [,...] } ] [CAST {(SOURCE AS DISTINCT) | (DISTINCT AS SOURCE)} WITH identifier] [method_definition [,...] ] The syntax below alters an existing user-defined datatype: ALTER TYPE type_name {ADD ATTRIBUTE type_definition | DROP ATTRIBUTE type_name} Keywords
Rules at a GlanceYou are able to create user-defined types (UDTs) as a way to further ensure data integrity in your database and to ease the work involved in doing so. An important concept of UDTs is that they allow you to easily create subtypes. Subtypes are UDTs built upon other UDTs (the supertype). Subtypes inherit the characteristics of their supertypes. The UDT that subtypes are dependent on is called a parent type or supertype. Assume, for example, that you want to define a general UDT for phone numbers called phone_nbr. You could then easily build new subtypes of phone_nbr called home_phone, work_phone, cell_phone, pager_phone, etc. Each of the subtypes could then inherit the general characteristics of the parent type, but then have characteristics of their own. In the following example, we create a general root UDT called money and then several subtypes: CREATE TYPE money (phone_number DECIMAL (10,2) ) NOT FINAL; CREATE TYPE dollar UNDER money AS DECIMAL(10,2) (conversion_rate DECIMAL(10,2) ) NOT FINAL; CREATE TYPE euro UNDER money AS DECIMAL(10,2) (dollar_conversion_rate DECIMAL(10,2) ) NOT FINAL; CREATE TYPE pound UNDER euro (euro_conversion_rate DECIMAL(10,2) ) FINAL; Programming Tips and GotchasThe biggest programming gotcha for user-defined types is that they are simply seldom used or well understood by most database developers and database administrators. Consequently, they can be problematic due to simple ignorance. They offer, however, a consistent and labor-saving approach for representing commonly reused conceptual elements in a database, such as address (e.g., street1, street2, city, state, postal code). DB2DB2 supports most elements of the ANSI standard with a few specific enhancements. The CREATE TYPE syntax is: CREATE [DISTINCT] TYPE type_name [UNDER supertype_name] [AS [new_udt_name] datatype [WITH COMPARISONS] [[NOT] INSTANTIABLE] [INLINE LENGTH int] [WITHOUT COMPARISONS] [NOT FINAL] MODE DB2SQL [REF USING datatype] [CAST {(SOURCE AS REF) | (REF AS SOURCE)} WITH identifier } ] [[OVERRIDING] METHOD method_definition [,...] ] The syntax below alters an existing user-defined datatype on DB2: ALTER TYPE type_name {ADD ATTRIBUTE type_definition | DROP ATTRIBUTE type_name [RESTRICT] | ADD METHOD method_definition | ALTER METHOD method_name [[NOT] FENCED] [[NOT] THREADSAFE] | DROP METHOD method_name [RESTRICT] } The parameters are:
In this example, we create a supertype for employee and a subtype for manager: CREATE TYPE employee AS (last_name VARCHAR(32), emp_id INT, dept REF(dept), salary DECIMAL(10,2)) MODE DB2SQL; CREATE TYPE manager UNDER emp AS (bonus DECIMAL(10,2)) MODE DB2SQL; The following example shows a more elaborate type hierarchy for addresses: CREATE TYPE address_type AS (street1 VARCHAR(30), street2 VARCHAR(30), city VARCHAR(30), state VARCHAR(10), locality_code CHAR(15) ) NOT FINAL MODE DB2SQL METHOD SAMEZIP (addr address_type) RETURNS INTEGER LANGUAGE SQL DETERMINISTIC CONTAINS SQL NO EXTERNAL ACTION METHOD DISTANCE (address_type) RETURNS FLOAT LANGUAGE C DETERMINISTIC PARAMETER STYLE SQL NO SQL NO EXTERNAL ACTION CREATE TYPE spain_address_type UNDER address_type AS (family_name VARCHAR(30)) NOT FINAL MODE DB2SQL CREATE TYPE usa_address_type UNDER address_type AS (zip VARCHAR(10)) NOT FINAL MODE DB2SQL In the above example, we created a type called address_type with two subtypes: spain_address_type and usa_address_type. The example that follows shows how to create a distinct type in DB2 used to identify an employee mailstop. CREATE DISTINCT TYPE mailstop AS CHAR(5) WITH COMPARISONS; In essence, the type mailstop now functions like a regular datatype and may be used to create columns in tables, variables in stored procedures, etc. The WITH COMPARISONS clause tells DB2 to create the standard pre-existing system operators for use with the distinct type. Specific functions, such as AVG, MIN, or LENGTH, may not work unless the CREATE FUNCTION statement is used to register the function for the distinct type. MySQLNot supported. OracleOracle has CREATE TYPE and ALTER TYPE statements, but they are not ANSI standard. Instead of a single CREATE TYPE statement, Oracle uses CREATE TYPE BODY to define the code that makes up the UDT, while CREATE TYPE defines the argument specification for the type. The syntax for CREATE TYPE is: CREATE [OR REPLACE] TYPE type_name [AUTHID {DEFINER | CURRENT_USER} ] { [ UNDER supertype_name] | {AS | IS} [OID 'object_identifier'] {OBJECT | TABLE OF datatype | VARRAY ( limit ) OF datatype} ] } EXTERNAL NAME java_ext_name LANGUAGE JAVA USING data_definition { [ (attribute datatype [,...] [EXTERNAL NAME 'name'] | [[NOT] OVERRIDING] [[NOT] FINAL] [[NOT] INSTANTIABLE] [{ {MEMBER | STATIC} {function_based | procedure_based} | constructor_clause | map_clause } [...] ] [pragma_clause] ] } Once the type has been declared, you encapsulate all of the UDT logic in the type body declaration. The type_name for both the CREATE TYPE and CREATE TYPE BODY should be identical. The syntax for the type body is shown below: CREATE [OR REPLACE] TYPE BODY type_name {AS | IS} {{MEMBER | STATIC} {function_based | procedure_based | constructor_clause}}[...] [map_clause] Oracle's implementation of ALTER TYPE enables you to drop or add attributes and methods from the type: ALTER TYPE type_name {COMPILE [DEBUG] [{SPECIFICATION | BODY}] [compiler_directives] [REUSE SETTINGS] | REPLACE [AUTHID {DEFINER | CURRENT_USER}] AS OBJECT (attribute datatype [,...] [element_definition [,...] ] ) | [[NOT] OVERRIDING] [[NOT] FINAL] [[NOT] INSTANTIABLE] { {ADD | DROP} { {MAP | ORDER} MEMBER FUNCTION function_name (parameter datatype [,...]) } | { {MEMBER | STATIC} {function_based | procedure_based} | constructor_clause | map_clause } [...] | {ADD | DROP | MODIFY} ATTRIBUTE (attribute [datatype] [,...] ) | MODIFY {LIMIT int | ELEMENT TYPE datatype} } [ {INVALIDATE | CASCADE [{ [NOT] INCLUDING TABLE DATA | CONVERT TO SUBSTITUTABLE}] [FORCE] [EXCEPTIONS INTO table_name] } ] Parameters for the three statements are as follows:
In this example, we create a Java SQLJ object type called address_type: CREATE TYPE address_type AS OBJECT EXTERNAL NAME 'scott.address' LANGUAGE JAVA USING SQLDATA ( street1 VARCHAR(30) EXTERNAL NAME 'str1', street2 VARCHAR(30) EXTERNAL NAME 'str2', city VARCHAR(30) EXTERNAL NAME 'city', state CHAR(2) EXTERNAL NAME 'st', locality_code CHAR(15) EXTERNAL NAME 'lc', STATIC FUNCTION square_feet RETURN NUMBER EXTERNAL VARIABLE NAME 'square_feet', STATIC FUNCTION create_addr (str VARCHAR, City VARCHAR, state VARCHAR, zip NUMBER) RETURN address_type EXTERNAL NAME 'create (java.lang.String, java.lang.String, java.lang.String, int) return scott.address', MEMBER FUNCTION rtrims RETURN SELF AS RESULT EXTERNAL NAME 'rtrim_spaces ( ) return scott.address' ) NOT FINAL; We could create a UDT using a VARRAY type with four elements: CREATE TYPE employee_phone_numbers AS VARRAY(4) OF CHAR(14); In the following example, we alter the address_type that we created earlier by adding a VARRAY called phone_varray: ALTER TYPE address_type ADD ATTRIBUTE (phone phone_varray) CASCADE; In this last example, we'll create a supertype and subtype called menu_item_type and entry_type, respectively: CREATE OR REPLACE TYPE menu_item_type AS OBJECT (id INTEGER, title VARCHAR2(500), NOT INSTANTIABLE MEMBER FUNCTION fresh_today RETURN BOOLEAN) NOT INSTANTIABLE NOT FINAL; In the above example, we created a type specification (but not the type body) that defines items that may appear on a menu at a café. Included with the type specification is a subprogram method called fresh_today, a Boolean indicator that tells whether the menu item is made fresh that day. The NOT FINAL clause that appears at the end of the code tells Oracle that this type may serve as the supertype (or base type) to other subtypes that we might derive from it. So now, let's create the entre_type: CREATE OR REPLACE TYPE entry_type UNDER menu_item_type (entre_id INTEGER, desc_of_entre VARCHAR2(500), OVERRIDING MEMBER FUNCTIOIN fresh_today RETURN BOOLEAN) NOT FINAL; PostgreSQLPostgreSQL supports a CREATE TYPE statement used to create a new datatype, but does not support an ALTER TYPE statement. PostgreSQL's implementation of the CREATE TYPE statement is nonstandard: CREATE TYPE type_name ( INPUT = input_function_name, OUTPUT = output_function_name, INTERNALLENGTH = { int | VARIABLE } [, DEFAULT = value] [, ELEMENT = array_element_datatype ] [, DELIMITER = delimiter_character ] [, PASSEDBYVALUE ] [, ALIGNMENT = {CHAR | INT2 | INT4 | DOUBLE} ] [, STORAGE = {PLAIN | EXTERNAL | EXTENDED | MAIN} ] ) The parameters are as follows:
When you create a new datatype in PostgreSQL, it is available only in the current database. The user who created the datatype is the owner. In order to create a new datatype, you must create at least two functions before defining the type (see CREATE/ALTER FUNCTION/PROCEDURE Statements"). In summary, you must create an INPUT function that provides the type with external values that can be used by the operators and functions defined for the type. You must also create an OUTPUT function that renders a usable external representation of the datatype. There are some additional requirements when creating the input and output functions:
For example, we can create a UDT called floorplan and use it to define a column in two tables, one called house and one called condo: CREATE TYPE floorplan (INTERNALLENGTH=12, INPUT=squarefoot_calc_proc, OUTPUT=out_floorplan_proc); CREATE TABLE house (house_plan_id int4, size floorplan, descrip varchar(30) ); CREATE TABLE condo (condo_plan_id INT4, size floorplan, descrip varchar(30) location_id varchar(7) ); SQL ServerNot supported. New datatypes can be added to SQL Server using the non-ANSI system stored procedure sp_addtype. See Also
This statement creates a view, also known as a virtual table. A view acts just like a table but is actually defined as a query. When a view is referenced in a statement, the result set of the query becomes the content of the view for the duration of that statement. Almost any valid SELECT statement can define the contents of a view, though some platforms restrict the certain clauses of the SELECT statement and certain set operators. In some cases, views can be updated, causing the view changes to be translated to the underlying data in the base tables. Some database platforms support a materialized view; that is, a physically created table that is defined with a query just like a view.
SQL2003 SyntaxCREATE [RECURSIVE] VIEW view_name {[(column [,...])] | [OF udt_name [UNDER supertype_name [REF IS column_name {SYSTEM GENERATED | USER GENERATED | DERIVED} ] [column_name WITH OPTIONS SCOPE table_name] ] } AS select_statement [WITH [CASCADED | LOCAL] CHECK OPTION] Keywords
Rules at a GlanceViews are usually as effective as the queries upon which they are based. That is why it is important to be sure that the defining SELECT statement is speedy and well-written. The simplest view is based on the entire contents of a single table: CREATE VIEW employees AS SELECT * FROM employee_tbl; A column list also may be specified after the view name. The optional column list contains aliases serving as names for each element in the result set of the SELECT statement. If you use a column list, then you must provide a name for every column returned by the SELECT statement. If you don't use a column list, then the columns of the view will be named whatever the columns in the SELECT statement are called. You will sometimes see complex SELECT statements within a view that make heavy use of AS clauses for all columns, because that allows the developer of the view to put meaningful names on the columns without including a column list. The ANSI standard defines that you must use a column list or an AS clause. However, some vendors allow more flexibility, so follow these rules for when to use an AS clause:
For example, the following two view declarations have the same functional result: -- using a column-list CREATE VIEW title_and_authors (title, author_order, author, price, avg_monthly_sales, publisher) AS SELECT t.title, ta.au_ord, a.au_lname, t.price, (t.ytd_sales / 12), t.pub_id FROM authors AS a JOIN titleauthor AS ta ON a.au_id = ta.au_id JOIN titles AS t ON t.title_id = ta.title_id WHERE t.advance > 0; -- using the AS clause with each column CREATE VIEW title_and_authors AS SELECT t.title AS title, ta.au_ord AS author_order, a.au_lname AS author, t.price AS price, (t.ytd_sales / 12) AS avg_monthly_sales, t.pub_id AS publisher FROM authors AS a JOIN titleauthor AS ta ON a.au_id = ta.au_id JOIN titles AS t ON t.title_id = ta.title_id WHERE t.advance > 0 You can alternately change the title of columns using the column list. In this case, we'll change avg_monthly_sales to avg_sales. Note that the code overrides the default column names provided by the AS clauses (in bold): CREATE VIEW title_and_authors (title, author_order, author, price, avg_sales, publisher) AS SELECT t.title AS title, ta.au_ord AS author_order, a.au_lname AS author, t.price AS price, (t.ytd_sales / 12) AS avg_monthly_sales, t.pub_id AS publisher FROM authors AS a JOIN titleauthor AS ta ON a.au_id = ta.au_id JOIN titles AS t ON t.title_id = ta.title_id WHERE t.advance > 0; ANSI standard view can update the base table(s) it is based upon if it meets the following conditions:
This example shows a view named california_authors that allows data modifications to apply only to authors within the state of California: CREATE VIEW california_authors AS SELECT au_lname, au_fname, city, state FROM authors WHERE state = 'CA' WITH LOCAL CHECK OPTION The view shown in this example would accept INSERT, DELETE, and UPDATE statements against the base table, but still maintain that all inserted, updated, or deleted records contain a state of 'CA' using the WITH ... CHECK clause. The most important rule to remember when updating a base table through a view is that all columns in a table that are defined as NOT NULL must receive a not null value when receiving a new or changed value. You can do this explicitly by directly inserting or updating a not null value into the column or by relying on a default value. In addition, views do not lift constraints on the base table. Thus, the values being inserted or updated into the base table must meet all the constraints placed on it by unique indexes, primary keys, CHECK constraints, etc. Programming Tips and GotchasViews also can be built upon other views, but this is inadvisable and usually considered bad practice. Depending on the platform, such a view may take longer to compile, but may offer the same performance as a transaction against the base table(s). On other platforms, where each view is dynamically created as it is invoked, nested views may take a long time to return a result set because each level of nesting means another query must be processed before a result set is returned to the user. So in this worst case scenario, a three-level nested view must make three correlated query calls before it can return results to the user. Although materialized views are defined like views, they take up space more like tables. Ensure that you have enough space available for the creation of materialized views. DB2DB2 supports a number of extensions to the ANSI standard CREATE VIEW statement: CREATE VIEW view_name { [ (column [,...]) ] | [OF type_name MODE DB2SQL { (REF IS objectid_column_name USER GENERATED [UNCHECKED] [, with_options] ) | UNDER parent_view_name INHERIT SELECT PRIVILEGES [ (with_options) ] } AS [WITH common_table_expression [, ...] ] (select_statement) [WITH {CASCADE | LOCAL} CHECK OPTION] The ALTER VIEW syntax is: ALTER VIEW view_name ALTER [COLUMN] column_name ADD SCOPE type_name Parameters for both statements are as follows:
{column_name WITH OPTIONS {READ ONLY | SCOPE typed_name} [,...] } [,...] The parameters are:
The SELECT statement of a view should be a standard SELECT statement and cannot contain any of the following:
You can get around the limitation on parameterized views by making a user-defined function based on a SQL table. A typed view or subview must follow some specific rules:
The following is an example including types, a typed table, and a view hierarchy, built around people_type and emp_type: CREATE TYPE people_type AS (name VARCHAR(20), address VARCHAR(100)) MODE DB2SQL CAST (SOURCE AS REF) WITH people_func; CREATE TYPE emp_type UNDER people_type AS (title VARCHAR(20), salary DECIMAL(8,2)) MODE DB2SQL CAST (SOURCE AS REF) WITH emp_func; CREATE TABLE people OF people_type (REF IS people_oid USER GENERATED); INSERT INTO people (people_oid, name, address) VALUES (people_func('0'), 'Sammy', '123 Lane'); CREATE TABLE emp OF emp_type UNDER people INHERIT SELECT PRIVILEGES; INSERT INTO emp (people_oid, name, address, title, salary) VALUES (emp_func('1'), 'Josh', '123 Street', 'Director', 1000); CREATE VIEW people_view OF people_type MODE DB2SQL (REF IS people_view_oid USER GENERATED) AS SELECT people_func (VARCHAR(people_oid)), name, address FROM ONLY (people); CREATE VIEW emp_view OF emp_type MODE DB2SQL UNDER people_view INHERIT SELECT PRIVILEGES AS SELECT emp_func (VARCHAR(people_oid)), name, address, title, salary FROM emp; SELECT * FROM people_view; PEOPLE_VIEW_OID NAME ADDRESS --------------- ----- --------------- x'30' Sammy 123 Lane x'31' Josh 123 Street SELECT * FROM emp_view; PEOPLE_VIEW_OID NAME ADDRESS TITLE SALARY --------------- ---- ---------------- -------------------- ---------- x'31' Josh 123 Street Director 1000.00 The WITH CHECK OPTION clause can be tricky because you are, in effect, telling the views to combine WHERE clauses either from upward in the dependency chain (via CASCADE) or downward in the dependency chain (via LOCAL). Consider the following:
The following table shows the search conditions against which inserted or updated rows are checked.
MySQLNot supported. OracleOracle supports extensions to the ANSI standard to create object-oriented views, XMLType views, and views that support LOB and object types: CREATE [OR REPLACE] [ [NO] FORCE] VIEW view_name {[(column [,...]) [constraint_clause] | [OF type_name {UNDER parent_view | WITH OBJECT IDENTIFIER {DEFAULT | (attribute [,...] ) } [(constraint_clause)] ] | [OF XMLTYPE [ [XMLSCHEMA xml_schema_url] ELEMENT {element | xml_schema_url # element} ] WITH OBJECT IDENTIFIER {DEFAULT | (attribute [,...]} ] AS (select statement) [WITH [READ ONLY | CHECK OPTION [CONSTRAINT constraint_name] ] ] Oracle's implementation of the ALTER VIEW statement supports added capabilities, such as adding, dropping, or modifying constraints associated with the view. In addition, the ALTER VIEW statement will explicitly recompile a view that is invalid, enabling you to locate recompile errors before runtime. This recompiling feature enables you to determine if a change in a base table negatively impacts any dependent views: ALTER VIEW view_name {ADD constraint_clause | MODIFY CONSTRAINT constraint_clause [NO]RELY] | DROP {PRIMARY KEY | CONSTRAINT constraint_clause | UNIQUE (column [,...])} } COMPILE The parameters are:
Any dblinks in the view's SELECT statement must be declared using the CREATE DATABASE LINK...CONNECT TO statement. Views containing flashback queries will have their AS OF clause evaluated at each invocation of the view, not when the view is compiled. In this example, we create a view that has an added constraint: CREATE VIEW california_authors (last_name, first_name, author_ID UNIQU RELY DISABLE NOVALIDATE, CONSTAINT id_pk PRIMARY KEY (au_id) RELY DISABLE NOVALIDATE) AS SELECT au_lname, au_fname, au_id FROM authors WHERE state = 'CA'; We might also wish to create an object view on an Oracle database and schema. This example creates the type and the object view: CREATE TYPE inventory_type AS OBJECT ( title_id NUM(6), warehouse wrhs_typ, qty NUM(8) ); CREATE VIEW inventories OF inventory_type WITH OBJECT IDENTIFIER (title_id) AS SELECT i.title_id, wrhs_typ(w.wrhs_id, w.wrhs_name, w.location_id), i.qty FROM inventories i JOIN warehouses w ON i.wrhs_id = w.wrhs_id; You could recompile the inventory_type view like this: ALTER VIEW inventory_type COMPILE: An updateable view in Oracle cannot contain any of the following:
There are some restrictions on how subviews and materialized views can be defined in Oracle:
Note that older versions of Oracle supported partitioned views. This feature has been deprecated. You should use explicitly declared partitions instead. PostgreSQLPostgreSQL supports a no-frills subset of the ANSI standard: CREATE VIEW view_name [ (column [,...] ) ] AS select_statement PostgreSQL's CREATE VIEW does not support some of the more complex options that other vendors do. However, it does allow views to be built on tables and other defined class objects. PostgreSQL views are built only upon other tables, not upon other views. PostgreSQL views are always read-only and cannot be used to perform data modifications on the underlying base tables. SQL ServerSQL Server supports some extensions to the ANSI standard, but does not offer object views or subviews: CREATE VIEW view_name [(column [,...])] [WITH {ENCRYPTION | SCHEMABINDING | VIEW_METADATA} [,...]] AS select_statement [WITH CHECK OPTION] SQL Server's implementation of ALTER VIEW allows you to change an existing view without affecting the permissions or dependent objects of the view: ALTER VIEW view_name [(column [,...])] [WITH {ENCRYPTION | SCHEMABINDING | VIEW_METADATA} [,...]] AS select_statement [WITH CHECK OPTION] The parameters are as follows:
The SELECT clause of a SQL Server view cannot:
Here, we define a SQL Server view with both ENCRYPTION and CHECK OPTION: CREATE VIEW california_authors (last_name, first_name, author_id) WITH ENCRYPTION AS SELECT au_lname, au_fname, au_id FROM authors WHERE state = 'CA' WITH CHECK OPTION GO SQL Server allows multiple SELECT statements in a view, as long as they are linked with UNION or UNION ALL clauses. SQL Server also allows functions and hints in the SELECT statement of a view. A SQL Server view is updateable if all of the items in the following list are true.
SQL Server allows indexes to be created on views (see CREATE INDEX). By creating a unique, clustered index on a view, you cause SQL Server to store a physical copy of the view on the database. Changes to the base table are automatically updated in the indexed view. Indexed views consume extra disk space, but provide a boost in performance. Indexed views must be built using the SCHEMABINDING clause. SQL Server also allows the creation of local partitioned views and distributed partitioned views. A local partitioned view is a partitioned view where all views are present on the same SQL Server. A distributed partitioned view is a partitioned view where one or more views are located on remote servers. Partitioned views must very clearly derive their data from different sources, with each distinct data source joined with a UNION ALL statement to the next. Furthermore, all columns of the partitioned views should be selected and identical. (The idea is that you have split the data out logically by means of a frontend application. SQL Server then recombines the data through the partitioned view.) This example shows how the data in the view comes from three separate SQL Servers: CREATE VIEW customers AS --Select from a local table on server 'New_York' SELECT * FROM sales_archive.dbo.customers_A UNION ALL SELECT * FROM houston.sales_archive.dbo.customers_K UNION ALL SELECT * FROM los_angeles.sales_archive.dbo.customers_S Note that each remote server (New_York, houston, and los_angeles) has to be defined as a remote server on all of the SQL Servers using the distributed partitioned view. Partitioned views can greatly boost performance because they can split I/O and user loads across many machines. But they are also difficult to plan, create, and maintain. Be sure to read the vendor documentation for complete details about all the permutations available with partitioned views. When altering an existing view, SQL Server acquires and holds an exclusive schema lock on the view until the alteration is finished. ALTER VIEW drops any indexes that might be associated with a view. You must manually recreate them using CREATE INDEX. See Also
The DECLARE command is one of four commands used in cursor processing, along with FETCH, OPEN, and CLOSE. Cursors allow you to process queries one row at a time, rather than in a complete set. The DECLARE CURSOR command specifies the exact records to be retrieved and manipulated one row at a time from a specific table or view. In other words, cursors are especially important for relational databases because databases are set-based, while most client-centric programming languages are row-based. This is important for two reasons. First, cursors allow programmers to program using methodologies supported by their favorite row-based programming language. Second, cursors run counter to the default behavior of some relational database platforms, which operate on a set of records and, on those specific platforms, may be noticeably slower than standard set-based operations.
SQL2003 SyntaxDECLARE cursor_name [ {SENSITIVE | INSENSITIVE | ASENSITIVE} ] [[NO] SCROLL] CURSOR [{WITH | WITHOUT} HOLD] [{WITH | WITHOUT} RETURN}] FOR select_statement [FOR {READ ONLY | UPDATE [OF column [,...] ]} ] Keywords
Rules at a GlanceThe DECLARE CURSOR command enables the retrieval and manipulation of records from a table one row at a time. This provides row-by-row processing, rather than the traditional set processing offered by SQL. At the highest level, these steps are followed when working with a cursor:
The DECLARE CURSOR command works by specifying a SELECT statement. Each row returned by the SELECT statement may be individually retrieved and manipulated. In this Oracle example, the cursor is declared in the declaration block along with some other variables. The cursor is then opened, fetched against, and then closed in the BEGIN...END block that follows: DECLARE CURSOR title_price_cursor IS SELECT title, price FROM titles WHERE price IS NOT NULL; title_price_val title_price_cursor%ROWTYPE; new_price NUMBER(10,2); BEGIN OPEN title_price_cursor; FETCH title_price_cursor INTO title_price_val; new_price := "title_price_val.price" * 1.25 INSERT INTO new_title_price VALUES (title_price_val.title, new_price) CLOSE title_price_cursor; END; Because this example uses PL/SQL, much of the code is beyond the scope of this book. However, the DECLARE block clearly shows that the cursor is declared. In the PL/SQL execution block, the cursor is initialized with the OPEN command, values are retrieved with the FETCH command, and the cursor finally is terminated with the CLOSE command. The SELECT statement is the heart of your cursor, so it is a good idea to test it thoroughly before embedding it in the DECLARE CURSOR statement. The SELECT statement may operate against a base table or a view. For that matter, read-only cursors may operate against non-updateable views. The SELECT statement can have subclauses like ORDER BY, GROUP BY, and HAVING if it is not updating the base table. If the cursor is FOR UPDATE, then it is a good idea to exclude these clauses from the SELECT statement. Local cursors are sometimes used as output parameters of stored procedures. Thus, a stored procedure could define and populate a cursor and pass that cursor to a calling batch or stored procedure. In this simple DB2 example, we'll declare a cursor that looks at department numbers, names, and manager numbers for all departments in the admin_group 'X01': EXEC SQL DECLARE dept_cursor CURSOR FOR SELECT dept_nbr, dept_name, mgr_nbr FROM department WHERE admin_group = 'X01' ORDER BY dept_name ASC, dept_nbr DESC, mgr_nbr DESC; In this next example from Microsoft SQL Server, a cursor from the publishers table is declared and opened. The cursor takes the first record from publishers that matches the SELECT statement and inserts it into another table; it then moves to the next record and the next, until all records are processed. Finally, the cursor is closed and deallocated (DEALLOCATE is used only with Microsoft SQL Server): DECLARE @publisher_name VARCHAR(20) DECLARE pub_cursor CURSOR FOR SELECT pub_name FROM publishers WHERE country <> 'USA' OPEN pub_cursor FETCH NEXT FROM pub_cursor INTO @publisher_name WHILE @@FETCH_STATUS = 0 BEGIN INSERT INTO foreign_publishers VALUES(@publisher_name) END CLOSE pub_cursor DEALLOCATE pub_cursor In this example, you see how the cursor moves through a set of records. (The example was intended only to demonstrate this concept, since there is actually a better way to accomplish the same task, namely an INSERT...SELECT statement.) Programming Tips and GotchasMost platforms do not support dynamically executed cursors. Rather, cursors are embedded within an application program, stored procedure, user-defined function, etc. In other words, the various statements for creating and using a cursor are usually used only in the context of a stored procedure or other database-programming object, not in a straight SQL script. For ease of programming and migration, don't use the SCROLL clause or the various sensitivity keywords. Some platforms support only forward-scrolling cursors, so you can avoid headaches later by just sticking to the simplest form of cursor. Cursors behave differently on the various database platforms when crossing a transaction boundary - for example, when a set of transactions is rolled back in the midst of a cursor. Be sure to get familiar with exactly how each database platform behaves in these circumstances. MySQL does not support server-side cursors in the ANSI SQL style, but does support extensive C-programming extensions that provide the same functionality. DB2DB2 supports the basic DECLARE CURSOR statement along with some variations on the WITH HOLD and WITH RETURN clauses, as in the following example: DECLARE cursor_name CURSOR [WITH HOLD] [WITH RETURN [{TO CALLER | TO CLIENT}] ] FOR select_statement where:
DB2 provides syntax to explicitly declare a cursor as read-only or updateable as part of the SELECT statement rather than as part of the DECLARE CURSOR statement. When not specified, the updateability of a cursor is implicitly determined according to a variety of rules that improve locking. First of all, a cursor is assumed to be read-only if it is not deleteable. A DB2 cursor is deleteable when it has the FOR UPDATE clause plus:
The columns of a DB2 cursor are updateable when it meets the requirements to be deleteable plus:
MySQLNot supported. OracleOracle has a rather interesting implementation of cursors. In reality, all Oracle data modification statements (INSERT, UPDATE, DELETE, and SELECT) implicitly open a cursor. For example, a C program accessing Oracle would not need to issue a DECLARE CURSOR statement to retrieve data on a record-by-record basis because that is the implicit behavior of Oracle. Because of this behavior, DECLARE CURSOR is something you use only in PL/SQL constructs, like stored procedures, and not something you would use in a script that is purely SQL.
Oracle utilizes a variation of the DECLARE CURSOR statement that supports parameterized inputs, as in the following syntax: DECLARE CURSOR cursor_name [ ( parameter datatype [,...] ) ] IS select_statement [FOR UPDATE [OF column_name [,...]]}] where:
In Oracle, variables are not allowed in the WHERE clause of the SELECT statement unless they are first declared as variables. The parameters are not assigned at the DECLARE; instead, they are assigned values at the OPEN command. This is important since any system function will return an identical value for all rows in the cursor result set. PostgreSQLPostgreSQL does not support the WITH clauses. It does allow you to return result sets in BINARY rather than text format. Although the compiler will not cause errors with many of the ANSI keywords, PostgreSQL's implementation of DECLARE CURSOR is more limited than it might first appear: DECLARE cursor_name [BINARY] [INSENSITIVE] [SCROLL] CURSOR FOR select_statement [FOR {READ ONLY | UPDATE [OF column_name [,...]]}] where:
PostgreSQL closes any existing cursor when a newly declared cursor is created with the same name. Binary cursors tend to be faster because PostgreSQL stores data as binary on the backend. However, user applications are only text aware. Therefore, you would have to build in binary handling for any BINARY cursors. PostgreSQL allows cursors only within a transaction. You should enclose a cursor within a BEGIN and COMMIT or ROLLBACK transaction block. PostgreSQL does not support an explicit OPEN cursor statement. Cursors are considered open when they are declared. So to declare and open a cursor in PostgreSQL, you could use code like this: DECLARE pub_cursor CURSOR FOR SELECT pub_name FROM publishers WHERE country <> 'USA'; SQL ServerSQL Server supports the ANSI standard, as well as a good many extensions that provide flexibility in how a cursor scrolls through a result set and manipulates data, as in the following syntax: DECLARE cursor_name [INSENSITIVE] [SCROLL] CURSOR [LOCAL | GLOBAL] [FORWARD_ONLY | SCROLL] [STATIC | KEYSET | DYNAMIC | FAST_FORWARD] [READ_ONLY | SCROLL_LOCKS | OPTIMISTIC] [TYPE_WARNING] FOR select_statement [FOR {READ ONLY | UPDATE [OF column_name [,...] ]} ] The parameters are as follows:
The SQL-92 compatibility syntax for DECLARE CURSOR is: DECLARE cursor_name [ INSENSITIVE ] [ SCROLL ] CURSOR FOR select_statement [ FOR { READ ONLY | UPDATE [ OF column_name [,...] ] } ] While the Transact-SQL extensioned syntax for DECLARE CURSOR is: DECLARE cursor_name CURSOR
[ LOCAL | GLOBAL ] [ FORWARD_ONLY | SCROLL ]
[ STATIC | KEYSET | DYNAMIC | FAST_FORWARD ]
[ READ_ONLY | SCROLL_LOCKS | OPTIMISTIC ]
[ TYPE_WARNING ]
FOR select_statement
[ FOR UPDATE [ OF column_name [,...] ] ] The SQL92 compatability syntax is supported to enable your code to be more transportable. The Transact-SQL extensions syntax enables you to define cursors using the same cursor types that are available in popular database APIs like ODBC, OLE-DB, and ADO, but may only be used in SQL Server stored procedures, user-defined functions, triggers, and ad hoc queries. If you do define a Transact-SQL extension cursor but do not define concurrency behavior using the OPTIMISTIC, READ_ONLY, or SCROLL_LOCKS keywords, then:
Note that variables may be used in the select_statement of a SQL Server cursor, but the variables are evaluated when the cursor is declared. Thus, a cursor containing the column based on the system function GETDATE( ) will have the same date and time for every record in the cursor result set. In the following SQL Server example, we use a KEYSET cursor to change blank spaces to dashes in the phone column of the authors table: SET NOCOUNT ON DECLARE author_name_cursor CURSOR LOCAL KEYSET TYPE_WARNING FOR SELECT au_fname FROM pubs.dbo.authors DECLARE @name varchar(40) OPEN author_name_cursor FETCH NEXT FROM author_name_cursor INTO @name WHILE (@@fetch_status <> -1) BEGIN -- @@fetch_status checks for 'record not found' conditions and errors IF (@@fetch_status <> -2) BEGIN PRINT 'updating record for ' + @name UPDATE pubs.dbo.authors SET phone = replace(phone, ' ', '-') WHERE CURRENT OF author_name_cursor END FETCH NEXT FROM author_name_cursor INTO @name END CLOSE author_name_cursor DEALLOCATE author_name_cursor GO See Also
The DELETE statement erases records from a specified table or tables. DELETE statements acting against tables are sometimes called search deletes. The DELETE statement may also be used in conjunction with a cursor. DELETE statements acting upon the rows of a cursor are sometimes called positional deletes.
SQL2003 SyntaxDELETE FROM { table_name | ONLY (table_name) } [{ WHERE search_condition | WHERE CURRENT OF cursor_name }] Keywords
Rules at a GlanceThe DELETE statement erases rows from a table or view. Space released by erased rows will be returned to the database where the table is located, though it may not happen immediately. A simple DELETE statement that erases all records in a given table has no WHERE clause, as in the following: DELETE FROM sales; You can use any valid WHERE clause to filter records that you do not want to delete. All three of the following examples show valid DELETE statements, and since all are search deletes, they all include the FROM clause: DELETE FROM sales WHERE qty IS NULL; DELETE FROM suppliers WHERE supplierid = 17 OR companyname = 'Tokyo Traders'; DELETE FROM distributors WHERE postalcode IN (SELECT territorydescription FROM territories); In some cases, you may wish to delete a specific row that is being processed by a declared and open cursor: DELETE titles WHERE CURRENT OF title_cursor; The above query assumes that you have declared and opened a cursor named title_cursor. Whichever row the cursor is on will be deleted when the command, shown above, is executed. Note that in the positional delete shown earlier, the FROM clause is not required. Programming Tips and GotchasIt is rare to issue a DELETE statement without a WHERE clause, because this results in deleting all rows from the affected table. You should first issue a SELECT statement with the same WHERE clause you intend to use on the DELETE statement. That way you can be sure exactly which records will be deleted. If it becomes necessary to remove all the rows in a table, you should consider using the non-ANSI TRUNCATE TABLE statement. In those databases that support the command, TRUNCATE TABLE is usually a faster method to physically remove all rows. TRUNCATE TABLE is faster than DELETE because the deletion of individual records is not logged. On some platforms, this makes rollback of a TRUNCATE statement impossible. The reduction of logging overhead saves considerable time when erasing a large number of records. On some database platforms, all foreign keys on the table must be dropped before issuing a TRUNCATE statement. DB2DB2 supports the ANSI standard, with the addition of transaction isolation control and the ability to assign an alias to the table from which records will be deleted, as in the following: DELETE FROM { table_name | ONLY (table_name) } [AS alias] [WHERE {search_conditions | CURRENT OF cursor_name}] [WITH { RR | RS | CS | UR }] The parameters are:
On DB2, the target may be a table, updateable view, or nickname. As the ANSI standard dictates, rows will be deleted from any subtables of the target table unless you use the ONLY keyword. If ONLY is used, you must enclose the table_name in parentheses. If you use ONLY in combination with WHERE CURRENT OF, then the SELECT statement that defined the cursor must use the FROM ONLY clause as well. For example: DECLARE CURSOR emp_cursor FOR SELECT name, address FROM ONLY (employee); DELETE FROM ONLY (employee) WHERE CURRENT OF emp_cursor; The previous code assumes that employee is the base table, with subtables below it. The cursor will only select rows that are contained in employee and not the other types contained in the subtables (e.g., parttime, salaried, student). Similarly, deletes will only take place at this level of the hierarchy as well. MySQLMySQL allows a number of extensions over the ANSI standard, but does not support the WHERE CURRENT OF clause, as in the following: DELETE [LOW_PRIORITY] [QUICK] [table_name[.*] [,...] ] {FROM table_name[.*] [,...] | [USING table_name[.*] [,...] ] } [WHERE search_condition] [ORDER BY clause] [LIMIT nbr_of_rows] The parameters are:
MySQL allows deletion from more than one table at a time. For example, the following two DELETE statements are functionally equivalent: DELETE orders FROM customers, orders WHERE customers.customerid = orders.customerid AND orders.orderdate BETWEEN '19950101' AND '19951231' DELETE FROM orders USING customers, orders WHERE customers.customerid = orders.customerid AND orders.orderdate BETWEEN '19950101' AND '19951231' In the examples above, we delete all orders made by customers during the year 1995. Note that you cannot use ORDER BY or LIMIT clauses in a multitable delete like those shown above. You can also delete records in orderly batches using the ORDER BY clause in conjunction with the LIMIT clause: DELETE FROM sales WHERE customerid = 'TORTU ORDER BY customerid LIMIT 5 MySQL exhibits several behaviors that speed up the delete operation. For example, MySQL returns the number of records deleted when it completes the operation. However, MySQL will return zero as the number of deleted records when you delete all records from a table because it is faster for MySQL to return zero than to count the rows deleted. In AUTOCOMMIT mode, MySQL will even substitute a TRUNCATE statement for a DELETE statement without a WHERE clause because TRUNCATE is faster. Note that the speed of a MySQL delete operation is directly related to the number of indexes on the table and the available index cache. You can speed up delete operations by executing the command against tables with few or no indexes or by increasing the size of the index cache. OracleOracle allows you to delete rows from tables, views, materialized views, nested subqueries, and partitioned views and tables, as follows: DELETE [FROM] {table_name | ONLY (table_name)} [alias] [{PARTITION (partition_name) | SUBPARTITION (subpartition_name)}] | (subquery [WITH {READ ONLY | CHECK OPTION [CONSTRAINT constraint_name]}] ) | TABLE (collection_expression ) [ (+) ] } [WHERE search_condition] [RETURNING expression [,...] INTO variable [,...] ] The parameters are:
When you execute a DELETE statement, Oracle releases space from the target table (or the base table of a target view) back to the table or index that owned the data. When deleting from a view, the view cannot contain a set operator, the DISTINCT keyword, joins, an aggregate function, an analytic function, a subquery in the SELECT list, a collection expression in the SELECT list, a GROUP BY clause, an ORDER BY clause, a CONNECT BY clause, or a START WITH clause. Here's an example where we delete records from a remote server: DELETE FROM scott.sales@chicago; In the following query, we delete from a derived table that is a collection expression: DELETE TABLE(SELECT contactname FROM customers c WHERE c.customerid = 'BOTTM') s WHERE s.region IS NULL OR s.country = 'MEXICO'; Here's an example where we delete from a partition: DELETE FROM sales PARTITION (sales_q3_1997) WHERE qty > 10000; Finally, in the example below, we use the RETURNING clause to look at the values that are deleted: DELETE FROM employee WHERE job_id = 13 AND hire_date + TO_YMINTERVAL('01-06') =< SYSDATE; RETURNING job_lvl INTO :int01; The previous example deletes records from the employee table and returns the job_lvl values into the predefined :into1 variable. PostgreSQLPostgreSQL uses the DELETE command to remove rows and any defined subclasses from the table. It is otherwise identical to the ANSI standard, as follows: DELETE [FROM] [ONLY] table_name [ WHERE search_condition | WHERE CURRENT OF cursor_name ] When deleting rows from only the table specified, use the optional ONLY clause. Otherwise, PostgreSQL will also delete records from any explicitly defined subtable. To delete all records from the titles table: DELETE titles To delete all records in the authors table where the last name starts with "Mc": DELETE FROM authors WHERE au_lname LIKE 'Mc%' To delete all titles with an old ID number: DELETE titles WHERE title_id >= 40 To delete all titles that have no sales: DELETE titles WHERE ytd_sales IS NULL To delete all records in one table based on the results of a subquery against another table (in this case, the records that have a match concerning "computers" in the titles table are erased from the titleauthor table): DELETE FROM titleauthor WHERE title_id IN (SELECT title_id FROM titles WHERE title LIKE '%computers%') DISCONNECT SQL ServerMicrosoft SQL Server allows records to be deleted both from tables and from views that describe a single table. SQL Server also allows a second FROM clause to allow JOIN constructs, as in the following example: DELETE [TOP number [PERCENT]] [FROM] table_name [[AS] alias] [FROM table_source [,...]] [ [{INNER | CROSS | [{LEFT | RIGHT | FULL] OUTER}] JOIN joined_table ON condition][,...] [WHERE search_condition | WHERE CURRENT OF [GLOBAL] cursor_name ] [OPTION (query_hint[,...n])] The syntax elements are as follows:
The primary addition to DELETE that SQL Server offers over and above the ANSI standard is a second FROM clause. The second FROM allows the use of the JOIN statement and makes it quite easy to delete rows from the table in the first FROM by correlating rows of a table declared in the second FROM. For example, you can use a rather complex subquery to erase all the sales records of computer books with this command: DELETE sales WHERE title_id IN (SELECT title_id FROM titles WHERE type = 'computer') But SQL Server allows a more elegant construction using a second FROM clause and a JOIN clause: DELETE s FROM sales AS s INNER JOIN titles AS t ON s.title_id = t.title_id AND type = 'computer' The following example deletes all rows with an order_date of 2003 or earlier in batches of 2,500: WHILE 1 = 1 BEGIN DELETE TOP (2500) FROM sales_history WHERE order_date <= '20030101' IF @@rowcount < 2500 BREAK END The TOP clause should be used in favor of the old SET ROWCOUNT statement because the TOP clause is open to many more query optimization algorithms. (Prior to SQL Server 2005, the SET ROWCOUNT statement was often used with data modification transactions to process large numbers of rows in batches, thus preventing the transaction log from filling up and ensuring that row locks would not escalate to a full table lock.) See Also
The DISCONNECT statement terminates one or more connections created between the current SQL process and the database server.
SQL2003 SyntaxDISCONNECT {CURRENT | ALL | connection_name | DEFAULT} Keywords
Rules at a GlanceDISCONNECT is used to disconnect a named SQL session (connection_name), the CURRENT connection, the DEFAULT connection, or ALL connections held by the user. For example, we can disconnect a single session called new_york: DISCONNECT new_york In the next example, we might want to disconnect all currently open sessions for the current user process: DISCONNECT ALL Programming Tips and GotchasDISCONNECT is not universally supported across platforms. Do not build cross-platform applications based on DISCONNECT unless you've made provisions to disconnect SQL sessions using each platform's preferred disconnection methodology. DB2DB2 supports the ANSI standard for DISCONNECT in embedded SQL only. DISCONNECT can drop one or more connections only after a commit or rollback operation succeeds for the sessions to be dropped. You can also use the RELEASE statement to place a connection in a disconnect-pending condition. MySQLNot supported. OracleOracle allows DISCONNECT only in its ad hoc query tool, SQL*Plus, using this syntax: DISC[ONNECT] In this usage, the command ends the current session with the database server but otherwise allows work in SQL*Plus to continue. For example, a programmer can continue to edit the buffer, save run files, and so on. However, you must establish a new connection to issue any SQL commands. Exiting SQL*Plus and returning to the filesystem requires the EXIT or QUIT commands. For example, to end the current connection with an Oracle server: DISCONNECT; PostgreSQLNot supported. Instead, every programming interface supports a disconnect operation; for example, SPI_FINISH is available under the Server Programming Interface, and PG_CONNECT is available under the PL/TCL programming package. SQL ServerMicrosoft SQL Server supports the ANSI syntax for DISCONNECT in Embedded-SQL (ESQL) only, not within its ad hoc querying tool, SQL Query Analyzer. To disconnect cleanly from Microsoft SQL Server in an ESQL program, use the DISCONNECT ALL statement. See Also
All of the database objects created with CREATE statements may be destroyed using complementary DROP statements. On some platforms, a ROLLBACK statement after a DROP statement will recover the dropped object. However, on other database platforms, the DROP statement is irreversible and permanent, so it is advisable to use the command with care.
SQL2003 SyntaxCurrently, the SQL2003 standard supports the ability to drop a lot of object types that are largely unsupported by most vendors. The ANSI SQL2003 syntax follows this format: DROP {object_type} object_name {RESTRICT | CASCADE} Keywords
Rules at a GlanceFor rules about the creation or modification of each of the object types, refer to the corresponding CREATE/ALTER statements. The DROP statement destroys a pre-existing object. The object is permanently destroyed, and all users who had access to the object immediately lose the ability to access the object. The object may be qualified - that is, you may fully specify the schema where the dropped object is located. For example: DROP TABLE scott.sales_2004 CASCADE; The above statement would not only drop the table scott.sales_2004, but also any views, triggers, or constraints built on it. On the other hand, a DROP statement may include an unqualified object name, and then the current schema context is assumed. For example: DROP TRIGGER before_ins_emp; DROP ROLE sales_mgr; Although not required by the SQL2003 standard, most implementations cause the DROP command to fail if the database object is in use by another user. Programming Tips and GotchasDROP will only work when it is issued against a pre-existing object of the appropriate type where the user has appropriate permissions, usually the DROP TABLE permission. (Refer to the GRANT Statement section for more information.) The SQL standard requires only that the owner of an object may drop it, but most database platforms allow variations on that requirement. For example, the database superuser can usually drop any object on the database server. With some vendors, the DROP command fails if the database object has extended properties. For example, Microsoft SQL Server will not drop a table that is replicated unless you first remove the table from replication.
You may have noticed that the ANSI standard does not support certain common DROP commands like DROP DATABASE and DROP INDEX, even though every vendor covered in this book (and just about every one in the market) supports these commands. The exact syntax for each of these commands is covered in the platform-specific sections below. DB2DB2 does not support the CASCADE option. It supports many of the ANSI object types in addition to many of its own object types, as follows: DROP {BUFFERPOOL | EVENT MONITOR | [SPECIFIC] FUNCTION | FUNCTION MAPPING | INDEX | INDEX EXTENSION | [SPECIFIC] METHOD | NICKNAME | DATABASE PARTITION GROUP | PACKAGE | [SPECIFIC] PROCEDURE | SCHEMA | SEQUENCE | SERVER | TABLE [HIERARCHY] | TABLESPACE[S] | TRANSFORM[S] [{ALL | group_name}] FOR | TRIGGER | [DISTINCT] TYPE | TYPE MAPPING | USER MAPPING FOR {authorization_name | USER} SERVER | VIEW [HIERARCHY] | WRAPPER } object_name [RESTRICT] The rules for DB2's DROP statements are less consistent than the ANSI standard's rules, so the full syntax of each DROP variant is shown below:
DROP FUNCTION sales_summary_1992 DROP FUNCTION sales_summary (int, float) DROP SPECIFIC FUNCTION products.inventory_level
DROP TABLESPACE sales_2004 DROP TABLESPACE sales_2003 DROP DATABASE PARTITION GROUP sales_archive_0304
DROP TRANSFORM sales_calcs FOR orders
DROP USER MAPPING FOR dylan SERVER oracle50
If you do not specify a schema along with the object name when performing a drop, DB2 will assume the current schema in dynamic SQL statements. DB2 will apply the QUALIFIER precompile/bind option for static SQL statements. MySQLMySQL supports a very limited number of objects via the CREATE/ALTER statement. Consequently, the syntax for DROP is also limited, as in the following: DROP { DATABASE | [TEMPORARY] TABLE | INDEX } object_name [,...] [ON table_name] [IF EXISTS] object_name [,...] [RESTRICT |CASCADE] The syntax elements are:
MySQL supports only the ability to drop a database, a table (or tables), or an index from a table. Although the DROP statement will not fail with the RESTRICT and CASCADE optional keywords, they also don't do anything. You can use the IF EXISTS clause to prevent MySQL from returning an error message by trying to delete an object that doesn't exist. OracleOracle supports most of the ANSI keywords for the DROP statements, as well as many additional keywords corresponding to objects uniquely supported by Oracle, as follows: DROP {CLUSTER | CONTEXT | DATABASE | DATABASE LINK | DIMENSION | DIRECTORY | FUNCTION | INDEX | INDEXTYPE | JAVA | LIBRARY | MATERIALIZED VIEW | MATERIALIZED VIEW LOG | OPERATOR | OUTLINE | PACKAGE | PROCEDURE | PROFILE | ROLE | ROLLBACK SEGMENT | SEQUENCE | SYNONYM | TABLE | TABLESPACE | TRIGGER | TYPE | TYPE BODY | USER | VIEW } object_name The rules for Oracle DROP statements are less consistent than the ANSI standard's rules, so the full syntax of each DROP variant is shown below:
DROP CLUSTER hr INCLUDE TABLES CASCADE CONSTRAINTS;
DROP CONTEXT hr_context
DROP DATABASE LINK hq.southeast.tenn;
DROP DIRECTORY image_files;
Non-IOT indexes are secondary objects and can be dropped and recreated without any loss of user data. IOTs, because they combine both table and index data in the same structure, cannot be dropped and recreated in this manner. IOTs should be dropped using the DROP TABLE syntax. When you drop a partitioned index, all partitions are dropped. When you drop a composite-partitioned index, all index partitions and subpartitions are dropped. When you drop a domain index, any statistics associated with the domain index are removed and any statistic types are disassociated. The optional keyword FORCE applies only when dropping domain indexes. FORCE allows you to drop a domain index marked IN PROGRESS, or to drop a domain index when its indextype routine invocation returns an error. For example: DROP INDEX ndx_sales_salesperson_quota;
DROP INDEXTYPE imageindextype FORCE;
DROP JAVA SOURCE "MyJavaSource";
DROP MATERIALIZED VIEW salespeople_at_quota;
DROP PACKAGE sales_quota_calc; DROP PACKAGE BODY regional_sales_calc;
DROP PROFILE web_user CASCADE;
DROP ROLE sales_mgr:
DROP ROLLBACK SEGMENT rbseg_sales_tblspc;
DROP PUBLIC SYNONYM users;
The DROP TABLE statement is effective for standard tables, index-organized tables, and object tables. The table being dropped is only moved to the recycling bin, unless you add the optional keyword PURGE, which tells Oracle to immediate free all space consumed by the table. (Oracle also supports a non-ANSI SQL command called PURGE that lets you remove tables from the recycling bin outside of the DROP TABLE statement.) DROP TABLE erases only the metadata of an external table. You must use an external operating system command to drop the file associated with an external table and reclaim its space. Use the optional CASCADE CONSTRAINTS clause to drop all referential integrity constraints elsewhere in the database that depend on the primary or unique key of the dropped table. You cannot drop a table with dependent referential integrity constraints without using the CASCADE CONSTRAINTS clause. The following example drops the job_desc table in the emp schema, then drops the job table and all referential integrity constraints that depend on the primary key and unique key of the job table: DROP TABLE emp.job_desc; DROP TABLE job CASCADE CONSTRAINTS;
DROP TABLESPACE sales_tblspc INCLUDING CONTENTS CASCADE CONSTRAINTS; DROP TABLESPACE sales_image_tblspc INCLUDING CONTENTS AND DATAFILES;
DROP TYPE salesperson_type;
DROP USER cody CASCADE;
DROP VIEW hr.active_employees; In all of the syntax shown above, object_name can be replaced with [schema_name.]object_name. If you leave off the schema name, the default schema of the user session is assumed. Thus, the following DROP statement drops the specified view from the sales_archive schema: DROP VIEW sales_archive.sales_1994; However, if your personal schema is scott, then the following command is assumed to be against scott.sales_1994: DROP VIEW sales_1994; PostgreSQLPosgreSQL does not support the RESTRICT or CASCADE optional keywords supported by the ANSI standard. It supports a wide variety of DROP variants, as follows: DROP { AGGEGATE | DATABASE | FUNCTION | GROUP | INDEX | LANGUAGE | OPERATOR |RULE | SEQUENCE | TABLE | TRIGGER | TYPE | USER | VIEW } object_name Following is the full syntax for each variant:
DROP AGGREGATE sales_avg(int4);
DROP DATABASE sales_archive;
DROP FUNCTION median_distribution (int, int, int, int);
DROP INDEX ndx_titles, ndx_authors;
DROP LANGUAGE php;
DROP OPERATOR ^ (int4, int4); DROP OPERATOR ! (NONE, bool);
DROP RULE phone_rule;
DROP TABLE authors, titles;
DROP TRIGGER insert_trigger ON authors;
DROP USER emily; Note that PostgreSQL drop operations do not allow you to specify the target database where the operation will take place (except for DROP DATABASE). Therefore, you should execute any drop operation from the database where the object you want to drop is located. SQL ServerSQL Server supports several standard variants of the DROP statement. SQL Server's main difference compared to the ANSI DROP statement is the ability to drop multiple objects with a single statement: DROP { DATABASE | DEFAULT | FUNCTION | INDEX | PROCEDURE | RULE | STATISTICS | TABLE | TRIGGER | TYPE | VIEW } object_name Following is the full syntax for each variant:
DROP DATABASE northwind, pubs GO
DROP DEFAULT sales_date_default GO EXEC sp_unbindefault 'sales.ord_date' DROP DEFAULT order_date_default GO
DROP PROCEDURE calc_sales_quota GO
DROP RULE phone_rule GO EXEC sp_unbindrule 'authors.phone' DROP RULE phone_rule GO
CREATE STATISTICS title_stats ON sales (title_id, ord_num) WITH SAMPLE 30 PERCENT GO CREATE STATISTICS zip_stats ON stores (zip) WITH FULLSCAN GO DROP STATISTICS sales.title_stats, stores.zip_stats GO
See Also
The EXCEPT set operator retrieves the result sets of two or more queries including all the records retrieved by the first query that are not also found in subsequent queries. Whereas JOIN clauses are used to return the rows of two or more queries that are in common, EXCEPT is used to filter out the records that are present in only one of multiple, but similar, tables. EXCEPT is in a class of keywords called a set operator. Other set operators include INTERSECT and UNION. (MINUS is Oracle's equivilent to the EXCEPT keyword. EXCEPT is the ANSI standard.) All set operators are used to simultaneously manipulate the result set of two or more queries, hence the term "set operators."
SQL2003 SyntaxThere are technically no limits to the number of queries that you may combine with the EXCEPT operator. The general syntax is: {SELECT statement1 | VALUES (expr1 [,...] ) } EXCEPT [ALL | DISTINCT] [CORRESPONDING [BY (column1, column2, ...)] ] {SELECT statement2 | VALUES (expr2 [,...] ) } EXCEPT [ALL | DISTINCT] [CORRESPONDING [BY (column1, column2, ...)] ] ... Keywords
Rules at a GlanceThere is only one significant rule to remember when using EXCEPT:
The datatypes do not have to be identical, but they must be compatible. For example, CHAR and VARCHAR are compatible datatypes. By default, the result set will default to the largest datatype size of each column in each ordinal position. For example, a query retrieving from columns containing a VARCHAR(10) and a VARCHAR(15) column will use the VARCHAR(15) datatype and size. Programming Tips and GotchasNone of the platforms support the CORRESPONDING [BY column1, column2, ...] clause.
According to the ANSI standard, the UNION and EXCEPT set operators evaluate with equal precedence. However, the INTERSECT set operator evaluates first before the other set operators. We recommend that you explicitly control the precedence of set operators, using parentheses as a general best practice. According to the ANSI standard, only one ORDER BY clause is allowed in the entire query. Include it at the end of the last SELECT statement. In order to avoid column and table ambiguity, be sure to alias each column for each table with the same respective alias. For example: SELECT au_lname AS 'lastname', au_fname AS 'firstname' FROM authors EXCEPT SELECT emp_lname AS 'lastname', emp_fname AS 'firstname' FROM employees ORDER BY lastname, firstname Also, while each of your column lists may list columns with correspondingly compatible datatypes, there may be some variation in behavior across the DBMS platforms with regard to varying length of the columns. For example, if the au_lname column in the previous example's first query is markedly longer than the emp_lname column in the second query, each platform may apply different rules as to which length is used for the final result. In general, though, the platforms will choose the longer (and less restrictive) column size for use in the result set. Each DBMS may apply its own rules as to which column name is used if the names vary across column lists. In general, the column names of the first query are used. DB2DB2 supports the EXCEPT and EXCEPT ALL keywords of the ANSI standard, plus the VALUES extension: { SELECT statement1 | VALUES (expression1, expression2 [,...] ) } EXCEPT [ALL] { SELECT statement2 | VALUES (expression1, expression2 [,...] ) } EXCEPT [ALL] ... where:
Although EXCEPT DISTINCT is not supported, EXCEPT is the functional equivalent. The CORRESPONDING clause is not supported. In addition, the datatypes LONG VARCHAR, LONG VARGRAPHIC, BLOB, CLOB, DBCLOB, DATALINK, and the structured types are not usable with EXCEPT, but are useable with EXCEPT ALL. When the result set has the same column name for a given column across all SELECT statements, then that name is used as the final column name returned by the statement. If, however, the given column is named differently in different SELECT statements, then you must rename the column in each query using the same AS alias_name. If multiple set operations are used in a single query, those enclosed in parentheses are evaluated first. Then, the queries are evaluated in left-to-right order. However, all INTERSECT operations are performed before any UNION and EXCEPT operations. For example: (SELECT empno FROM employee WHERE workdept LIKE 'E%' EXCEPT SELECT empno FROM emp_act WHERE projno IN ('IF1000', 'IF2000', 'AD3110') ) UNION VALUES ('AA0001'), ('AB0002'), ('AC0003') The example above retrieves all IDs for all employees from the employee table who are in any department that starts with "E," then it excludes any employee IDs from the employee accounts (emp_act) table representing employees working in the projects IF1000, IF200', and AD3110. Finally, it adds in three extra employee IDs - AA0001, AB0002, and AC0003 using the UNION set operator. MySQLEXCEPT is not supported in MySQL. However, you can use the NOT IN or NOT EXISTS operations as alternatives. OracleOracle does not support the EXCEPT set operator. However, it has an alternative set operator, MINUS, with identical functionality. <SELECT statement1> MINUS <SELECT statement2> MINUS ... MINUS DISTINCT and MINUS ALL are not supported. MINUS is the functional equivalent of MINUS DISTINCT. Oracle does not support MINUS on queries under the following circumstances:
If the first query in a set operation contains any expressions in the select item list, then include AS clauses to associate aliases with those expressions. Also, only the last query in the set operation may contain an ORDER BY clause. For example, you could generate a list of all store IDs that do not have any records in the sales table: SELECT stor_id FROM stores MINUS SELECT stor_id FROM sales The MINUS command is functionally similar to a NOT IN query. The following query would retrieve the same results as the earlier query: SELECT stor_id FROM stores WHERE stor_id NOT IN (SELECT stor_id FROM sales) PostgreSQLPostgreSQL supports the EXCEPT and EXCEPT ALL set operators using the basic ANSI SQL syntax: <SELECT statement1> EXCEPT [ALL] <SELECT statement2> EXCEPT [ALL] ... PostgreSQL does not support EXCEPT or EXCEPT ALL on queries with a FOR UPDATE clause. PostgreSQL does not support the CORRESPONDING clause. EXCEPT DISTINCT is not supported, but EXCEPT is the functional equivalent. The first query in the set operation may not contain an ORDER BY clause or a LIMIT clause. Subsequent queries in the EXCEPT or EXCEPT ALL set operation may contain these clauses, but such queries must be enclosed in parentheses. Otherwise, the right-most occurence of ORDER BY or LIMIT will be applied to the entire set operation. PostgreSQL evaluates SELECT statements in a multi-EXCEPT statement from top to bottom unless you use parentheses to change the evaluation hierarchy of the statements. Normally, duplicate rows are eliminated from the two result sets, unless you add the ALL keyword. For example, you could find all titles in the authors table that have no records in the sales table: SELECT title_id FROM authors EXCEPT ALL SELECT title_id FROM sales; SQL ServerEXCEPT is not supported. However, you can use NOT IN or NOT EXISTS operations in conjunction with a correlated subquery as alternatives. The following queries are examples of how you can achieve EXCEPT functionality using NOT EXISTS and NOT IN: SELECT DISTINCT a.city FROM pubs..authors AS a WHERE NOT EXISTS (SELECT * FROM pubs..publishers AS p WHERE a.city = p.city) SELECT DISTINCT a.city FROM pubs..authors AS a WHERE a.city NOT IN (SELECT p.city FROM pubs..publishers AS p WHERE p.city IS NOT NULL) In general, NOT EXISTS is faster than NOT IN. In addition, there is a subtle issue with NULLs that differentiates the IN and NOT IN operators and the EXISTS and NOT EXISTS set operator. In order to get around this different handling of NULLs, simply add the IS NOT NULL clause to the WHERE clause, as we have in the preceding example. See Also
The EXISTS operator tests a subquery for the existence of rows.
SQL2003 SyntaxSELECT ... WHERE [NOT] EXISTS (subquery) The parameters and keywords are as follows:
Rules at a GlanceThe EXISTS operator checks a subquery for the existence of one or more records against the records in the parent query. For example, we want to see if we have any jobs where no employee is filling the position: SELECT * FROM jobs WHERE NOT EXISTS (SELECT * FROM employee WHERE jobs.job_id = employye.job_id) This example tests for the absence of records in the subquery using the optional NOT keyword. The next example looks for specific records in the subquery to retrieve the main result set: SELECT au_lname FROM authors WHERE EXISTS (SELECT * FROM publishers WHERE authors.city = publishers.city) This query returns the last name of authors who live in the same city as their publishers. Note that the asterisk in the subquery is acceptable, since the subquery only needs to return a single record to provide a Boolean TRUE value. Columns are irrelevant in these cases. The first example selects only a single column. The key point is whether a row exists. Programming Tips and GotchasEXISTS, in many queries, does the same thing as ANY. EXISTS is usually most effective with correlated subqueries.
The EXISTS subquery usually searches for only one of two things. Your first option is to use the asterisk wildcard (e.g., SELECT * FROM...) so that you are not retrieving any specific column or value. In this case, the asterisk means "any column." The second option is to select only a single column in the subquery (e.g., SELECT au_id FROM...). Some individual database platforms allow a subquery against more than one column (e.g., SELECT au_id, au_lname FROM...). However, this feature is rare and should be avoided on code that needs to be transportable across platforms. Platform DifferencesAll of the platforms support EXISTS in the manner described above. See Also
The FETCH statement is one of four commands used in cursor processing, along with DECLARE, OPEN, and CLOSE. Cursors allow you to process queries one row at a time, rather than as a complete set. FETCH positions a cursor on a specific row and retrieves that row from the result set. Cursors are especially important in relational databases because they are set-based, while most client-centric programming languages are row-based. So cursors allow you to perform operations a single row at a time, to better fit what a client program can do, rather than operate on a whole set of records at once.
SQL2003 SyntaxFETCH [ { NEXT | PRIOR | FIRST | LAST | { ABSOLUTE | RELATIVE int } } FROM ] cursor_name [INTO variable1 [, ...] ] Keywords
Rules at a GlanceAt the highest level, a cursor must be:
By following these steps, you create a result set similar to that of a SELECT statement, except that you can operate against each individual row within the result set separately. For example, assume you've created and opened a cursor on a DB2 database called employee_cursor containing three columns: DECLARE employee_cursor CURSOR FOR SELECT lname, fname, emp_id FROM employee WHERE hire_date >= '2005-02-15'; OPEN employee_cursor; You can now retrieve values from the cursor using FETCH: FETCH FROM employee_cursor INTO :emp_last_name, :emp_first_name, :emp_id The FETCH statement allows you to position the current row within the cursor. Since you have not yet accessed the cursor in this example, you will be given the first record in the result set. You can then use any procedural code you want to operate against the values in that row.
A cursor rests either directly on a row, before the first row, or after the last row. When a cursor is resting directly in a row, then that row is known as the current row. You can use cursors to position an UPDATE or DELETE statement in the current row using the WHERE CURRENT OF clause. It is important to remember that a cursor result set does not wrap around. Thus if you have a result set containing 10 records and tell the cursor to move forward 12 records, it will not wrap back around to the beginning and continue its count from there. Instead, the default cursor behavior is to stop after the last record in the result set when scrolling forward, and to stop before the first record when scrolling back. For example, on SQL Server: FETCH RELATIVE 12 FROM employee_cursor INTO @emp_last_name, @emp_first_name, @emp_id When fetching values from the database into variables, make sure that the datatypes and number of columns and the variables match. Otherwise, you'll get an error. For example: FETCH PRIOR FROM employee_cursor INTO @emp_last_name, @emp_id The above example will fail since the employee_cursor contains three columns while the fetch operation has only two variables. Programming Tips and GotchasThe most common errors encountered with the FETCH statement are a mismatch of variables to the values in the cursor, whether that is in the exact number, order, or datatype. So take a little time to make sure you remember exactly what values are in the cursor and what their datatypes are. Typically, the database will lock at least the current row and possibly all rows held by the cursor. According to the ANSI standard, cursor locks are not held through ROLLBACK or COMMIT operations, although this varies from platform to platform. Although the FETCH statement is detailed in isolation here, it should always be managed as a group with the DECLARE, OPEN, and CLOSE statements. For example, every time you open a cursor, the server consumes memory. If you forget to close your cursors, you could create memory management problems. So make sure that every declared and opened cursor is eventually closed. Cursors are also often used in stored procedures or batches of procedural code. The reason for this is because sometimes you need to perform actions on individual rows rather than on entire sets of data at a time. But because cursors operate on individual rows and not sets of data, they are often much slower than other means of accessing your data. Just make sure that you perform strong analysis on your approach. Many challenges, such as a convoluted DELETE operation or a very complex UPDATE, can be solved by using clever WHERE and JOIN clauses instead of cursors. DB2DB2 cursors are implicitly forward-only cursors that always scroll forward one record at a time. A DB2 cursor, when compared to the ANSI standard, is essentially a FETCH NEXT 1 cursor. DB2 does not support keywords like PRIOR, ABSOLUTE, and RELATIVE. DB2 FETCH syntax is: FETCH [ FROM ] cursor_name { [INTO variable1 [, ...] ] | [USING DESCRIPTOR descriptor_name] } where:
When saving fetched values INTO variables, DB2 allows LOB values to be inserted into regular host variables such as VARCHAR (when they are big enough to hold the value), locator variables, or file-reference variables. When saving fetched values with the USING DESCRIPTOR clause, you should remember several rules about descriptor areas:
For example, the following FETCH statement uses an SQLDA: FETCH employee_cursor USING DESCRIPTOR :sqlda2 In the above example, the descriptor :sqlda2 and the cursor employee_cursor must already exist. MySQLNot supported. OracleOracle cursors are implicitly forward-only cursors that always scroll forward one record at a time. An Oracle cursor, when compared to the ANSI standard, is essentially a FETCH NEXT 1 cursor. Oracle cursors must either insert the retrieved values into matching variables, or use the BULK COLLECT clause to insert all of the records of the result set into an array. Oracle does not support keywords like PRIOR, ABSOLUTE, and RELATIVE. However, Oracle does support both forward-only and scrollable cursors in the database via the Oracle Call Interface (OCI). OCI also supports features like PRIOR, ABSOLUTE, and RELATIVE for read-only cursors whose result set is based on a read consistent snapshot. Oracle's FETCH syntax is: FETCH cursor_name { INTO variable_name1 [,...] | BULK COLLECT INTO [collection_name [,...] [LIMIT int] } where:
Oracle supports dynamic SQL-based cursors whose text can be built at runtime. Oracle also supports cursor_names that may be any allowable variable, parameter, or host-array type. In fact, you may also use user-defined or %ROWTYPE record variables for the INTO clause as well. This allows you to construct flexible, dynamic cursors in a template-like structure. For example: DECLARE TYPE namelist IS TABLE OF employee.lname%TYPE; names namelist; CURSOR employee_cursor IS SELECT lname FROM employee; BEGIN OPEN employee_cursor; FETCH employee_cursor BULK COLLECT INTO names; ... CLOSE employee_cursor; END; Oracle FETCH often is paired with a PL/SQL FOR loop (or other kind of loop) to cycle through all the rows in the cursor. You should use the cursor attributes %FOUND or %NOTFOUND to detect the end of the rowset. For example: DECLARE TYPE employee_cursor IS REF CURSOR RETURN employee%ROWTYPE; employee_cursor EmpCurTyp; employee_rec employee%ROWTYPE; BEGIN LOOP FETCH employee _cursor INTO employee_rec; EXIT WHEN employee _cursor%NOTFOUND; ... END LOOP; CLOSE employee_cursor; END; This example uses a standard PL/SQL loop with an EXIT clause to end the loop when there are no more rows in the cursor to process. PostgreSQLPostgreSQL supports both forward and backward scrolling cursors. It covers the ANSI standard except ABSOLUTE, FIRST, or LAST. The syntax for FETCH in PostgreSQL is: FETCH { [ FORWARD | BACKWARD | ABSOLUTE | RELATIVE] } { [int | ALL | NEXT | PRIOR ] } { IN | FROM } cursor_name [ INTO :variable1 [,...] ] where:
PostgreSQL cursors must be used with transactions explicitly declared using BEGIN, and must be closed with COMMIT or ROLLBACK. The following PostgreSQL statement retrieves five records from the employee table and displays them: FETCH FORWARD 5 IN employee_cursor; It is important to note that PostgreSQL also supports a separate command, MOVE, to move to a specific cursor position. It differs from FETCH only by not returning values into variables: MOVE { [ FORWARD | BACKWARD | ABSOLUTE | RELATIVE] } { [int | ALL | NEXT | PRIOR ] } { IN | FROM } cursor_name For example, the following code declares a cursor, skips forward five records, and then returns the values in the sixth record: BEGIN WORK; DECLARE employee_cursor CURSOR FOR SELECT * FROM employee; MOVE FORWARD 5 IN employee_cursor; FETCH 1 IN employee_cursor; CLOSE employee_cursor; COMMIT WORK; The above code will return a single row from the employee_cursor result set. SQL ServerSQL Server supports a variation of FETCH that is very close to the ANSI standard: FETCH [ { NEXT | PRIOR | FIRST | LAST | { ABSOLUTE | RELATIVE int } } [FROM] [GLOBAL] cursor_name [INTO @variable1 [, ...] ] The differences between SQL Server's implementation and the ANSI standard are very small. First, SQL Server allows you to use variables in place of the int and cursor_name values. Second, SQL Server allows the declaration and use of GLOBAL cursors that can be accessed by any user or session, not just the one that created it. There are some rules that apply to how you use the FETCH command based upon how you issued the DECLARE CURSOR command:
Here's a full example that goes from declaring and opening a cursor to initiating several fetches and then finally closing and deallocating the cursor: DECLARE @vc_lname VARCHAR(30), @vc_fname VARCHAR(30), @i_emp_id CHAR(5) DECLARE employee_cursor SCROLL CURSOR FOR SELECT lname, fname, emp_id FROM employee WHERE hire_date <= 'FEB-14-2004' OPEN employee_cursor -- Fetch the last row in the cursor. FETCH LAST FROM employee_cursor -- Fetch the row immediately prior to the current row in the cursor. FETCH PRIOR FROM employee_cursor -- Fetch the fifth row in the cursor. FETCH ABSOLUTE 5 FROM employee_cursor -- Fetch the row that is two rows after the current row. FETCH RELATIVE 2 FROM employee_cursor -- Fetch values eight rows prior to the current row into variables. FETCH RELATIVE -8 FROM employee_cursor INTO @vc_lname, @vc_fname, @i_emp_id CLOSE employee_cursor DEALLOCATE employee_cursor GO Remember that in SQL Server you must not only CLOSE the cursor, but you must also DEALLOCATE it. In some rare cases, you might wish to reopen a closed cursor. You can reuse any cursor that you have closed but not deallocated. The cursor is permanently destroyed only when it is deallocated. See Also
The GRANT statement assigns privileges to users and roles, which allows them to access and use database objects (that is, object privileges). In addition, most database platforms use the GRANT statement to authorize users and roles to create database objects and execute stored procedures, functions, and so on (particularly usage privileges).
SQL2003 SyntaxThe SQL2003 syntax for GRANT includes only the assignment of object privileges and roles to a specific user: GRANT { {object privilege [,...] | role [,...]} } [ON database_object_name] [TO grantee [,...] ] [WITH HIERARCHY OPTION] [WITH GRANT OPTION] [WITH ADMIN OPTION] [FROM { CURRENT_USER | CURRENT_ROLE } ] Keywords
{[TABLE] object_name | DOMAIN object_name | COLLATION object_name | CHARACTER SET object_name | TRANSLATION object_name | SPECIFIC ROUTINE routine_name}
Rules at a GlanceYou may grant many privileges on a single object with one statement, but do not mix the ALL PRIVILEGES privilege with the individual SQL statement keywords in a single GRANT statement. You can grant a specific privilege on a specific database object to a single user using: GRANT privilege_name ON object_name TO grantee_name For example: GRANT SELECT ON employee TO Dylan; You can grant a specific privilege on a specific object to all users via the PUBLIC user. When a grant is issued to PUBLIC, that means everyone has permission to the object without the need for a grant specifically to the object. For example: GRANT SELECT ON employee TO PUBLIC; When granting privileges to multiple grantees, simply place a comma between each: GRANT SELECT ON employee TO Dylan, Matt The previous example shows that you may grant privileges to multiple users, in this case Dylan and Matt, and to the PUBLIC user in a single GRANT statement. When granting privileges on a table, the privileges may extend to the column level by showing a list of columns enclosed in parentheses after the table name. For example: GRANT SELECT ON employee(emp_id, emp_fname, emp_lname, job_id) TO Dylan, Matt The previous example shows that Dylan and Matt now have select privileges on several columns of the employee table. Programming Tips and GotchasDepending on the specific database implementation, views may or may not have independent access privileges from their base tables. Note that all of the optional WITH clauses control extensions to the base privilege(s) granted in the statement. Thus, the following command grants the privilege to SELECT records from the employee table: GRANT SELECT ON employee TO Dylan; but the following command grants the privilege to SELECT records from the employee table and also to grant that SELECT privilege to other users. GRANT SELECT ON employee TO Dylan WITH GRANT OPTION; Similarly, you can use the REVOKE statement to revoke only the WITH GRANT OPTION, the individual SELECT privilege, or both. Most of the platforms isolate privileges at the user and role level. Thus, an individual user who is a member of two roles may have an individual set of permissions four times - once for themselves, once for PUBLIC, and once with each of the roles for which they have permission. In this situation, you should be very careful. In order to remove a specific privilege completely from such a user, you have to revoke the user's privileges directly, and then in both roles. DB2DB2 offers many options in its implementation of the GRANT command. It is important to note that privileges on database objects (such as SELECT privileges on a specific table) are handled by the command, as are a variety of other privileges: GRANT { [database_privilege [,...] ON DATABASE ] | [ CONTROL ON INDEX index_name] | [ {BIND | CONTROL | EXECUTE} ON PACKAGE package_name ] | [ EXECUTE ON [SPECIFIC] {FUNCTION | METHOD {* | FOR} | PROCEDURE} object_name ] | [ {ALTERIN | CREATEIN | DROPIN} ON SCHEMA schema_name ] | [ ON SEQUENCE sequence_name] | [ PASSTHRU ON SERVER server_name ] | [ object_privilege [,...] ON [TABLE] object_name ] } TO [ {USER |GROUP} ] { grantee_name [,...] | group_name [,...] | PUBLIC } ] [WITH GRANT OPTION] | [ USE OF TABLESPACE tablespace_name ] The parameters are as follows:
In situations when a group and a user of the same name exist, you must use either GROUP or KEYWORD to inform DB2 whether you want the privileges assigned to the group or to the user. If you do not, DB2 will return an error and abort the GRANT statement. For example, to grant CONNECT and BINDADD permissions to the user emily and the group datareaders: GRANT CONNECT, BINDADD ON DATABASE TO USER emily, GROUP datareaders To give the user Dylan, and all users who are members of the Admin role, control over a specific index: GRANT CONTROL ON INDEX emp_ndx TO dylan, admin; You can give a user rights on a package to execute it and bind (or rebind it), without the privilege to drop it (via CONTROL) as follows: GRANT BIND, EXECUTE ON PACKAGE d02.sales_processing TO USER dylan The following example grants a user a variety of privileges on the sales table, as well as the privilege to pass those privileges on to other users: GRANT DELETE, INSERT, SELECT, UPDATE ON sales TO USER sam WITH GRANT OPTION Similarly, you can give another user privileges on a specific object (a view in this case) that includes only specific columns in the object: GRANT UPDATE (sales_qty, sales_discount) ON sales TO USER jake MySQLMySQL provides additional access privileges, primarily relating to object manipulation within a database. GRANT in MySQL is available from Version 3.22.11 and on. The syntax is as follows: GRANT [ { ALL [PRIVILEGES] | {SELECT | INSERT | UPDATE} [ (column_name [,...]) ] | DELETE | REFERENCES [ (column_name [,...]) ] } | [ USAGE ] | [{ALTER | CREATE | DROP}] | [FILE] | [INDEX] | [PROCESS] | [RELOAD] | [SHUTDOWN] | [CREATE TEMPORARY TABLES] | [LOCK TABLES] | [REPLICATION CLIENT] | [REPLICATION SLAVE] | [SHOW DATABASES] | [SUPER] }[,...] ON {table_name | * | *.* | database_name.*} {TO grantee_name [IDENTIFIED BY 'password']} [,...] [WITH GRANT OPTION] where:
Since MySQL is focused on speed, you can implement server-wide features that provide high-speed performance. For example, you can enable the SKIP_GRANT_TABLES startup option to disable checks for privileges. This can speed up queries, but obviously no permission checking is done. This means that all users have full access to all resources in the database. The following are access privileges that are usable with tables: SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, GRANT, INDEX, and ALTER. At the column level, you can apply INSERT, UPDATE, and SELECT. In any situation, the host, table, database, and column names may each be up to 60 characters in size. The username (a.k.a. the grantee_name) may be up to 16 characters. MySQL also supports the possibility of granting rights to a specific user on a specific host if the grantee_name is in the form USER@HOST. Wildcards can be included in the hostname of a grantee_name to provide the access privilege to a large number of users at one time. Also, a missing hostname is the same as the "%" wildcard. For example: GRANT SELECT ON employee TO katie@% IDENTIFIED BY 'audi', annalynn IDENTIFIED BY 'cadillac', cody@us% IDENTIFIED BY 'bmw'; The preceding GRANT statement grants read privileges on the employee table to the user katie explicitly at any hostname. Similarly, the GRANT statements grants read privileges to the user annalynn implicitly at any hostname. And finally, the GRANT statement grants read privileges to the user cody explicitly at any hostname starting with "US". The grantee_name must be 16 or fewer characters. When specifying the user, password protection may be enforced by including the IDENTIFIED BY clause. The following example grants permissions to three users with passwords: GRANT SELECT ON employee TO dylan IDENTIFIED BY 'porsche', kelly IDENTIFIED BY 'mercedes', emily IDENTIFIED BY 'saab'; In MySQL, if you grant permissions to a user that doesn't exist, it creates that user. The two statements that follow create a user and password with global privileges and another user that has no privileges (the USAGE privilege): GRANT SELECT ON *.* TO tony IDENTIFIED BY 'humbolt'; GRANT USAGE ON sales.* TO alicia IDENTIFIED BY 'dakota';
If you do create a new user using the GRANT command, and you forget to specify a password, you can, and probably should, set a password using the SET PASSWORD command. MySQL also allows you to issue grants on remote hosts, specified with @hostname, and include the wildcard character percent (%). For example, the following grant provides all permissions on all tables of the current database across all hosts in the notforprofit.org domain to the user annalynn: GRANT ALL ON * TO annalynn@'%.notforprofit.org'; Privileges are written directly to system tables at four different levels:
Because of the usage and system tables involved, you may grant redundant permissions at different levels. For example, you could grant a user SELECT privileges on a specific table, and then grant global SELECT privileges on that same table to all users of the database. You would then have to revoke those privileges separately to ensure the user no longer had the privilege of SELECTing from the table. The system tables are also somewhat unprotected, so it is possible to change permissions by issuing INSERT, UPDATE, and DELETE statements against the tables rather than by using GRANT or REVOKE. Normally, all privileges are read into memory when MySQL starts. Database, table, and column privileges issued by a GRANT statement are available immediately and take effect right away. User privileges issued by a GRANT statement are noticed immediately and take effect the next time the user connects. If you directly modify permissions in the system tables, none of the changes are noticed until you restart MySQL, or until you issue a FLUSH PRIVILEGES command. OracleOracle's implementation of the GRANT statement supports an enormous number of variations and permutations. The syntax is as follows: GRANT { [object_privilege] [,...] | [system_privilege] [,...] | [role] [,...] } [ ON { [schema_name.][object] [,...] | [DIRECTORY directory_object_name] | [JAVA [ { SOURCE | RESOURCE } ] [schema_name.][object] } TO {{grantee_name [,...] | role_name [,...] | PUBLIC} [WITH { GRANT | HIERARCY } OPTION] [IDENTIFIED BY password][WITH ADMIN OPTION]; You can grant multiple privileges with a single invocation of the command, but they must all be of the same type (object, system, or role). For example, you could grant a user three object privileges on a single table in one GRANT statement, and then issue a separate grant of two system privileges to a role, and yet a third grant of several roles to a user, but not all of them in one GRANT statement. Following are the parameters to Oracle's GRANT statement:
Oracle also supports two additional keywords for special cases:
Grants to users take effect right away. Grants to roles take effect right away, assuming the roles are enabled. Otherwise, a grant to a role takes effect as the role is enabled. Note that you can grant roles to users and to other roles (including PUBLIC). For example: GRANT sales_reader TO sales_manager; To grant privileges on a view, you must have the specific privilege and the WITH GRANT OPTION on all of the base tables for the view. Any time you want a privilege available to all users, simply grant it to PUBLIC: GRANT SELECT ON work_schedule TO public; However, there are some restrictions when granting system privileges and roles:
You can grant multiple privileges of the same type in a single statement. However, the grants must be on the same type of objects: GRANT UPDATE (emp_id, job_id), REFERENCES (emp_id), ON employees TO sales_manager; As an aside, granting any of the object permissions on a table allows the user (or role) to lock the table in any lock mode using Oracle's LOCK TABLE statement. Nearly every supported Oracle feature or command is assignable with a GRANT command (as shown in Table 3-2). Privileges can be granted not only on database objects (such as tables and views) and system commands (such as CREATE ANY TABLE), but also on schema objects (such as DIRECTORY, JAVA SOURCE, and RESOURCE ). The ANY option, shown in Table 3-2, grants the privilege to execute a given statement against objects of a specific type owned by any user within the schema. Without the ANY option, the user can only execute the statement against objects in their own schema. A more complete list of Oracle system privileges is shown in Table 3-2.
Any of the privileges shown in Table 3-2 containing the ANY keyword have special meaning. In effect, ANY gives a user the privilege to execute the specified command or operation in any schema. If you want to include all user schemas, but exclude the SYS system schema, keep the O7_DICTIONARY_ACCESSIBILITY initialization parameter at its default value of FALSE. PostgreSQLPostgreSQL supports a subset of the ANSI GRANT commands supporting object privileges against database objects such as tables. It does not support any of the WITH options available under the ANSI standard nor specialty features like USAGE and UNDER. PostgreSQL's GRANT syntax is as follows: GRANT { ALL [PRIVILEGES] | SELECT | INSERT | DELETE | UPDATE | RULE | REFERENCES | TRIGGERS | USAGE } [,...] ON [TABLE] { object_name } [,...] TO {grantee_name | GROUP group_name | PUBLIC } [,...] where:
Multiple privileges may be granted at one time, as long as commas separate them. Do not combine ALL [PRIVILEGES] with other privileges, but the remainder may be combined in any order. PostgreSQL does not support the WITH clauses or column-level permissions. Currently, if you want column-level permissions, you must set those up using a view showing only the columns of a table that you want a user to access. Under PostgreSQL, the creator of an object has all privileges on the object, though they may revoke their own privileges on an object. However, the privilege for the creator of an object to assign privileges on that object cannot be revoked. Similarly, the right to drop an object is permanently assigned to the creator of the object, and cannot be revoked. Users other than the creator of an object do not have permissions on an object until specifically granted. PostgreSQL's implementation of GRANT behaves as if WITH GRANT OPTION is always enabled. Any user granted privilege to a table is able to grant their privileges to other users. PostgreSQL's support for the GRANT statement is elementary. In the following examples, INSERT privileges are granted on the publishers table to the PUBLIC role and SELECT and UPDATE privileges are granted on the sales table to the users Emily and Dylan: GRANT INSERT ON publishers TO PUBLIC; GRANT SELECT, UPDATE ON sales TO emily, dylan; SQL ServerSQL Server's implementation of GRANT offers few variations from the ANSI standard. It does not support the ANSI standard's FROM clause, nor the HIERARCHY or ADMIN options. But it does offer the ability to grant specific system privileges and to grant them under another user's security context. The syntax is as follows: GRANT { [object_privilege] [,...] | [system_privilege] } [ON { [object] [(column [,...])] ] TO {grantee_name [,...] | role [,...] | PUBLIC | GUEST } [WITH GRANT OPTION] [AS {group | role}] where:
The privilege to issue a CREATE statement also implies the privilege to issue the equivalent ALTER or DROP statement.
Privileges may not be granted in any database other than the current database, nor may privileges be granted in more than one database at a time.
SQL Server applies precedence to permissions. Thus, if a user has a privilege granted at the user level, but the permission is revoked at the group level (when the user is a member of a group), the permission is revoked at both levels. SQL Server also contains a number of fixed system roles with preset privileges on both objects and commands. The SQL Server system roles are:
The SYSADMIN system role can grant any permission in any database on the server. Members of the DB_OWNER and DB_SECURITYADMIN system roles can grant any permission on any statement or object in databases they own. Members of the DB_DDLADMIN system role, the SYSADMIN system role, and the database owner may grant system privileges. Members of the system roles may add other logins or users to their roles, but not via a GRANT statement. Instead, to add a user to a system role, you must use a SQL Server system stored procedure called sp_addsrvrolemember. In addition to the server-wide system roles, there are a number of database-wide system roles. That is, the previous system roles have privileges that span every database on the server, whereas the following system roles exist in every database and have privileges within the scope of a specific database, as follows:
Like the server-wide system roles, database-wide system roles cannot be granted to a user via the GRANT statement. Instead, you must use a SQL Server system stored procedure called sp_addrolemember. In the following example, the CREATE DATABASE and CREATE TABLE system privileges are granted to the users emily and sarah. Next, numerous permissions are granted on the titles table to the editors group. The editors are then able to grant permission to others: GRANT CREATE DATABASE, CREATE TABLE TO emily, sarah GO GRANT SELECT, INSERT, UPDATE, DELETE ON titles TO editors WITH GRANT OPTION GO The following example grants permissions to the database user sam and the Windows user jacob: GRANT CREATE TABLE TO sam, [corporate\jacob] GO The following example shows how to grant permissions using the optional AS keyword. In this example, the user emily owns the sales_detail table and she grants SELECT privileges to the sales_manager role. The user kelly, who is a member of the sales_manager role, wants to grant SELECT privileges to sam, but cannot because the permissions were granted to the sales_manager role and not to her explicitly. kelly can use the AS clause to get around this hurdle: -- Initial grant GRANT SELECT ON sales_detail TO sales_manager WITH GRANT OPTION GO -- Kelly passes the privilege to Sam as a member of the sale_manager role GRANT SELECT ON sales_detail TO sam AS sales_manager GO See Also
The IN operator provides a way to delineate a list of values, either explicity listed or from a subquery, and compare a value against that list in a WHERE or HAVING clause. In other words, it gives you a way to say "Is value A in this list of values?"
SQL2003 Syntax{WHERE | HAVING | {AND | OR} } value [NOT] IN ({comp_value1, comp_value2 [,...] | subquery } ) Keywords
In the following example, generated on SQL Server, we look for all employees in the employee table of the HR database who have a home state of Georgia, Tennessee, Alabama, or Kentucky: SELECT * FROM hr..employee WHERE home_state IN ('AL','GA','TN','KY') Similarly, we can look for all employees in the HR database who are authors in the PUBS database: SELECT * FROM hr..employee WHERE emp_id IN (SELECT au_id FROM pubs..authors) You can also use the NOT keyword to return a result set based upon the absense of a value. In the following case, the company headquarters is located in New York, and many workers commute in from neighboring states. We want to see all such workers: SELECT * FROM hr..employee WHERE home_state NOT IN ('NY','NJ','MA','CT','RI','DE','NH')
See Also
The INSERT statement adds rows of data to a table or view.
The INSERT statement allows rows to be written to a table through one of several methods:
SQL2003 SyntaxINSERT INTO [ONLY] {table_name | view_name} [(column1 [,...] )] [OVERRIDE {SYSTEM | USER} VALUES] {DEFAULT VALUES | VALUES (value1 [,...]) | SELECT_statement } Keywords
Rules at a GlanceYou may insert values into tables and into views built upon a single source table. The INSERT . . . VALUES statement adds a single row of data to a table using literal values supplied in the statement. In the following example, a new row in the authors table is inserted for the author Jessica Rabbit: INSERT INTO authors (au_id, au_lname, au_fname, phone, address, city, state, zip, contract ) VALUES ('111-11-1111', 'Rabbit', 'Jessica', DEFAULT, '1717 Main St', NULL, 'CA', '90675', 1) Every column in the table is assigned a specific, literal value except the phone column, which is assigned the default value (as defined during the CREATE TABLE or ALTER TABLE statement), and the city column, which is set to NULL. An important concept to remember is that you may skip columns in the table and set them to NULL, assuming they allow NULL values. Inserts that leave some columns NULL are called partial INSERTs. Here is a partial INSERT that performs the same work as the preceding example: INSERT INTO authors (au_id, au_lname, au_fname, phone, contract ) VALUES ('111-11-1111', 'Rabbit', 'Jessica', DEFAULT, 1) The INSERT statement, combined with a nested SELECT statement, allows a table to be quickly populated with one or more rows from the result set of the SELECT statement. When using INSERT . . . SELECT between a target table and a source table, it is important to ensure that the datatypes returned by the SELECT statement are compatible with the datatypes in the target table. For example, to load data from the sales table into the new_sales table: INSERT INTO sales (stor_id, ord_num, ord_date, qty, payterms, title_id) SELECT CAST(store_nbr AS CHAR(4)), CAST(order_nbr AS VARCHAR(20)), order_date, quantity, SUBSTRING(payment_terms,1,12), CAST(title_nbr AS CHAR(1)) FROM new_sales WHERE order_date >= 01-JAN-2005 -- retrieve only the newer records You must specify the columns in the table that receive data by enclosing their names in parentheses, in a comma-delimited list. This column_list can be omitted, but all columns that are defined for the table are then assumed, in their ordinal position. Any column with an omitted value will be assigned its default value (according to any DEFAULT column setting on the table) or, if no DEFAULT column setting exists, NULL. The columns in the column list may be in any order, but you may not repeat any column in the list. Furthermore, the columns and their corresponding value entries must agree in terms of datatype and size. The first of the following two examples leaves off the column list, while the second uses only DEFAULT values: INSERT INTO authors VALUES ('111-11-1111', 'Rabbit', 'Jessica', DEFAULT, '1717 Main St', NULL, 'CA', '90675', 1) INSERT INTO temp_details DEFAULT VALUES The first statement would succeed only as long as all the values in the value list correspond correctly with the datatypes and size limitations of the columns of the target table. Any inconsistencies would generate an error. The second statement would succeed only as long as defaults were declared for the columns of the target table, or those columns allowed NULL values.
Programming Tips and GotchasINSERT statements will always fail under the following circumstances:
The most common error encountered when executing INSERT statements is a mismatch between the number of columns and the number of values. Accidentally leave out a value that corresponds to a column, and you are likely to encounter an error that will cause the statement to fail. INSERT statements also fail when an inserted value is of a datatype that is a mismatch with the column of the target table. For example, an attempt to insert a string like "Hello world" into an integer column would fail. On the other hand, some database platforms automatically and implicitly convert certain datatypes. For example, SQL Server will automatically convert a date value to a character string for insertion into a VARCHAR column. Another common problem encountered with the INSERT statement is a size mismatch between a value and its target column. For example, inserting a long string into a CHAR(5) target column, or inserting a very large integer into a TINYINT column, can cause problems. Depending on the platform you are using, the size mismatch may cause an outright error and rollback of the transaction or the database server may simply trim the extra data. Either result is undesirable. Similarly, a problem can arise when an INSERT statement attempts to insert a NULL value into a target column that does not accept NULL.
DB2DB2 supports the ANSI standard form of INSERT, with the exception of the OVERRIDE clause, and includes some added syntax to control transaction isolation settings: INSERT INTO table_name [(column1 [,...] )] {VALUES | VALUES (value1 [,...]) | SELECT_statement } [ WITH { RR | RS | CS | UR } where:
DB2 strictly enforces these rules:
DB2 supports identity columns; that is, columns that automatically generate a monotonically increasing value. When inserting into an identity column, DB2 generates a new value for the identity column. Identity columns are defined as either GENERATED ALWAYS or GENERATED BY DEFAULT upon creation. If defined as GENERATED ALWAYS, you should not specify a value for the column - leave it to default. If defined as GENERATED BY DEFAULT, you may insert your own value (explicitly or via a subquery), or you can allow DB2 to generate the value for you. In the following examples, assume the emp_id column is an identity column with the GENERATED ALWAYS property: INSERT INTO employee (emp_id, emp_lname, emp_fname) VALUES (DEFAULT, :var_lname, :var_fname) INSERT INTO employee (emp_lname, emp_fname) VALUES (:var_lname, :var_fname) Both of the example statements shown above will work without error. MySQLMySQL supports several INSERT syntax options that engender MySQL's reputation for high speed: INSERT [LOW_PRIORITY | DELAYED] [IGNORE] [INTO] [[database_name.]owner.] table_name [(column1 [,...])] {VALUES (value1 [,...]) | SELECT_statement | SET column1=value1, column2=value2 [,...]} where:
MySQL trims any portion of a value that has a size or datatype mismatch. Thus, inserting "10.23 X" into a decimal datatype column will cause "...X" to be trimmed from the inserted value. If you attempt to insert a numeric value into a column that is beyond the range of the column, MySQL will trim the value. Inserting an illegal time or date value in a column will result in a zero value for the target column. Inserting the string "Hello World" into a CHAR(5) column will result in the value being trimmed to just the five characters in "Hello". This string trimming feature applies to CHAR, VARCHAR, TEXT, and BLOB columns. OracleOracle's implementation of the INSERT statement allows data insertion into a given table, view, partition, subpartition, or object table. It also supports additional extensions such as inserting records into many tables at once and conditional inserts. The syntax is: -- standard INSERT statement INSERT [INTO] {table_name [ [SUB]PARTITION (prtn_name)] | (subquery) [WITH {READ ONLY | CHECK OPTION [CONSTRAINT constr_name] }] | TABLE (collection) [ (+) ] } [alias] [(column1 [,...])] {VALUES (value1[,...]) [RETURNING expression1 [,...] INTO variable1 [,...]] | SELECT_statement [WITH {READ ONLY | CHECK OPTION [CONSTRAINT constr_name]} } -- conditional INSERT statement INSERT {[ALL | FIRST]} WHEN condition THEN standard_insert_statement ELSE standard_insert_statement where:
You may optionally identify the schema and remote address (via @db_link) of the target, but the current schema and local database are assumed if you do not otherwise specify. You may optionally identify the PARTITION or SUBPARTITION, through the prtn_name parameter, into which the record(s) will be inserted, as long as the target is not an object table or object view.
Oracle allows the standard INSERT operations as described under the ANSI implementation, such as INSERT...SELECT and INSERT...VALUES. But it has a great many special variations. When inserting into tables that have assigned sequences, but sure to use the sequence_name.nextval function call to insert the next logical number in the sequence. For example, assume you want to use the authors_seq sequence to set the value of au_id when inserting a new row into the authors table: INSERT authors (au_id, au_lname, au_fname, contract ) VALUES (authors_seq.nextval, 'Rabbit', 'Jessica', 1) When retrieving values during an INSERT operation, check for a one-for-one match between the expressions in the RETURNING clause and the variables of the INTO clause. The expressions returned by the clause do not necessarily have to be those mentioned in the VALUES clause. For example, the following INSERT statement places a record into the sales table, but places a completely distinct value into a bind variable: INSERT authors (au_id, au_lname, au_fname, contract ) VALUES ('111-11-1111', 'Rabbit', 'Jessica', 1) RETURNING hire_date INTO :temp_hr_dt; Notice that the RETURNING clause returns the hire_date even though hire_date is not one of the values listed in the VALUES clause. (In this example, it is reasonable to assume a default value was established for the hire_date column.) An unconditional multitable INSERT statement into a lookup table that contains a list of all the approved jobs in our company looks like this: INSERT ALL INTO jobs(job_id, job_desc, min_lvl, max_lvl) VALUES(job_id, job_desc, min_lvl, max_lvl) INTO jobs(job_id+1, job_desc, min_lvl, max_lvl) VALUES(job_id, job_desc, min_lvl, max_lvl) INTO jobs(job_id+2, job_desc, min_lvl, max_lvl) VALUES(job_id, job_desc, min_lvl, max_lvl) INTO jobs(job_id+3, job_desc, min_lvl, max_lvl) VALUES(job_id, job_desc, min_lvl, max_lvl) SELECT job_identifier, job_title, base_pay, max_pay FROM job_descriptions WHERE job_status = 'Active'; And just to make things more complex, Oracle allows multitable INSERT statements that are conditional: INSERT ALL WHEN job_status = 'Active' INTO jobs WHEN job_status = 'Inactive' INTO jobs_old WHEN job_status = 'Terminated' INTO jobs_cancelled ELSE INTO jobs SELECT job_identifier, job_title, base_pay, max_pay FROM job_descriptions; Note that in the above example, you would have to follow each INTO clause with a VALUES clause if you were skipping NOT NULL columns in the target table. The following example shows this syntax: INSERT FIRST WHEN job_status = 'Active' INTO jobs VALUES(job_id, job_desc, min_lvl, max_lvl) WHEN job_status = 'Inactive' INTO jobs_old VALUES(job_id, job_desc, min_lvl, max_lvl) WHEN job_status = 'Terminated' INTO jobs_cancelled VALUES(job_id, job_desc, min_lvl, max_lvl) WHEN job_status = 'Terminated' INTO jobs_outsourced VALUES(job_id, job_desc, min_lvl, max_lvl) ELSE INTO jobs VALUES(job_id, job_desc, min_lvl, max_lvl) SELECT job_identifier, job_title, base_pay, max_pay FROM job_descriptions; Notice that in the above example, the FIRST clause also tells Oracle to execute the first occurance of job_status = 'Terminated' by inserting the records into the jobs_cancelled table and skipping the jobs_outsourced INSERT operation. Oracle allows you to insert data into a table, partition, or view (also known as the target) using a regular or direct-path INSERT statement. Under a regular insert, Oracle maintains referential integrity and reuses free space in the target. Under direct-path, Oracle appends data at the end of the target table without filling in any free space gaps elsewhere in the table. This method bypasses the buffer cache and writes directly to the datafiles; hence the term "direct-path."
Direct-path enhances performance on long, multirecord insert operations. However, the following is a list of restrictions that cause an INSERT statement to perform a regular INSERT, instead of direct-path:
In addition, Oracle will not allow you to query (e.g., using SELECT) a table later in the same transaction after you perform a direct-path INSERT into that table until a COMMIT has been performed. Oracle allows you to use parallel direct-path inserts into multiple tables, but you may only use subqueries to insert the data into the tables, not a standard VALUES clause.
PostgreSQLPostgreSQL supports the ANSI standard for the INSERT statement. It does not support the ANSI SQL clause OVERRIDE SYSTEM GENERATED VALUES, but is otherwise identical in syntax and behavior. Refer to SQL2003 Syntax, earlier, for the syntax and usage.
SQL ServerSQL Server supports a few extensions to the ANSI standard for INSERT. It supports several rowset functions (explained below), as well as the capability to insert the results from stored procedures and extended procedures directly into the target table. SQL Server's syntax is: INSERT [INTO] table_name [(column1 [,...]) {[DEFAULT] VALUES | VALUES (variable1 [,...]) | SELECT_statement | EXEC[UTE] proc_name { [[@param =] value {[OUTPUT]} [,...] ] } where:
Although SQL Server automatically assigns values to IDENTITY columns and TIMESTAMP columns, it does not do so for UNIQUEIDENTIFIER columns. With either of the former datatype columns, you may simply skip them in the columns and values lists. However, you cannot do that with a UNIQUEIDENTIFIER column. You must use the NEWID( ) function to obtain and insert a globally unique ID (GUID): INSERT INTO guid_sample (global_ID, sample_text, sample_int) VALUES (NEWID( ), 'insert first record','10000') GO When migrating between platforms, remember that inserting an empty string ('') into a SQL Server TEXT or VARCHAR column results in a zero-length string. This is not the same as a null value, as some platforms interpret it. An example of the INSERT...EXEC statements, below, first creates a temporary table called #ins_exec_container. Then, the INSERT operation retrieves a listing of the c:\temp directory and stores it in the temporary table, while the second INSERT executes a dynamic SELECT statement: CREATE TABLE #ins_exec_container (result_text VARCHAR(300) NULL) GO INSERT INTO #ins_exec_container EXEC master..xp_cmdshell "dir c:\temp" GO INSERT INTO sales EXECUTE ('SELECT * FROM sales_2002_Q4') GO This functionality can be very useful in stored procedures where you wish to build business logic using Transact-SQL stored procedures, for example, to determine the state of objects in the database or outside of the database, and then act on those results using Transact-SQL.
The INTERSECT set operator retrieves the rows of two or more queries where the rows of the result sets are identical in both the first and second (and possibly more) queries. In some ways, INTERSECT is a lot like an INNER JOIN operation. INTERSECT is in a class of keywords called set operators. Other set operators include EXCEPT and UNION. All set operators are used to simultaneously manipulate the result set of two or more queries; hence the term "set operators."
SQL2003 SyntaxThere is technically no limit to the number of queries that you may combine with the INTERSECT set operator. The general syntax is: <SELECT statement1> INTERSECT [ALL | DISTINCT] [CORRESPONDING [BY (column1, column2, ...)] ] <SELECT statement2> INTERSECT [ALL | DISTINCT] [CORRESPONDING [BY (column1, column2, ...)] ] ... Keywords
Rules at a GlanceThere is only one significant rule to remember when using INTERSECT:
The datatypes do not have to be identical, but they should be compatible. For example, CHAR and VARCHAR are compatible datatypes. By default, the result set will default to the largest of each column in each ordinal position. Programming Tips and GotchasNone of the platforms support the ANSI CORRESPONDING [BY column1, column2, ...] clause.
The ANSI standard evaluates INTERSECT as higher priority than other set operators, while some platforms vary in how they evaluate set operator precedence. You can explicitly control the precedence of set operators using parentheses. Otherwise, the DBMS might evaluate them from leftmost to rightmost expression or from first to last expression. According to the standard, only one ORDER BY clause is allowed in the entire query. Include it at the end of the last SELECT statement. In order to avoid column and table ambiguity, be sure to alias each column of each table with the same respective alias. For example: SELECT a.au_lname AS 'lastname', a.au_fname AS 'firstname' FROM authors AS a INTERSECT SELECT e.emp_lname AS 'lastname', e.emp_fname AS 'firstname' FROM employees AS e ORDER BY lastname, firstname Also, while your column datatypes may be compatible throughout the queries in the INTERSECT, there may be some variation in behavior across the DBMS platforms with regard to varying length of the columns. For example, if the au_lname column in the first query is markedly longer than the emp_lname column in the second query, each platform may apply different rules as to which length is used for the final result. In general, though, the platforms will choose the longer (and less restrictive) column size for use in the result set. Each DBMS may apply its own rules as to which column name is used if the columns across the tables have different names. In general, the column names from the first query are used. DB2DB2 supports the INTERSECT and INTERSECT ALL keywords of the ANSI standard, plus the VALUES extension: {SELECT statement1 | VALUES (expr1 [,...] ) } INTERSECT [ALL] [CORRESPONDING [BY (column1, column2, ...)] ] {SELECT statement2 | VALUES (expr2 [,...] ) } INTERSECT [ALL] [CORRESPONDING [BY (column1, column2, ...)] ] ... Although ANSI's INTERSECT DISTINCT is not supported, INTERSECT is the functional equivalent. The CORRESPONDING clause is not supported. When using INTERSECT (but not INTERSECT ALL), the datatypes LONG VARCHAR, LONG VARGRAPHIC, BLOB, CLOB, DBCLOB, DATALINK and structured types are not usable. When the same column name is used across queries, then that name is used in the final result set. If, however, the queries use different names for a column, then DB2 generates a new column name for the final result. That column is then unusable in the ORDER BY and FOR UPDATE clauses. If multiple set operations are used in a single query, those enclosed in parentheses are evaluated first. Then, the queries are evaluated in left-to-right order. However, all INTERSECT operations are performed before any UNION and EXCEPT operations. For example: SELECT empno FROM employee WHERE workdept LIKE 'E%' INTERSECT (SELECT empno FROM emp_act WHERE projno IN ('IF1000', 'IF2000', 'AD3110') UNION VALUES ('AA0001'), ('AB0002'), ('AC0003') ) The example above retrieves all employee IDs from the employee table for employees who are in any department that starts with "E." However, employee IDs are returned only if they also exist in the employee accounts table, named emp_act, or have projects of IF1000, IF2000, or AD3110. MySQLNot supported. OracleOracle supports the INTERSECT and INTERSECT ALL set operators using the basic ANSI SQL syntax: <SELECT statement1> INTERSECT <SELECT statement2> INTERSECT ... Oracle does not support the CORRESPONDING clause. The INTERSECT DISTINCT is not supported, but INTERSECT is the functional equivalent. Oracle does not support INTERSECT on queries under the following circumstances:
If the first query in the set operation contains any expressions in the select item list, then include the AS keyword to associate an alias with the column resulting from the expression. Also, only the first query in the set operation may contain an ORDER BY clause. For example, you could find all store IDs that also have sales: SELECT stor_id FROM stores INTERSECT SELECT stor_id FROM sales PostgreSQLPostgreSQL supports the INTERSECT and INTERSECT ALL set operators using the basic ANSI SQL syntax: <SELECT statement1> INTERSECT [ALL] <SELECT statement2> INTERSECT [ALL] ... PostgreSQL does not support INTERSECT or INTERSECT ALL on queries with a FOR UPDATE clause. PostgreSQL does not support the CORRESPONDING clause. The INTERSECT DISTINCT is not supported, but INTERSECT is the functional equivalent. The first query in the set operation may not contain an ORDER BY clause or a LIMIT clause. Subsequent queries in the INTERSECT or INTERSECT ALL set operation may contain these clauses, but such queries must be enclosed in parentheses. Otherwise, the rightmost occurance of ORDER BY or LIMIT will be assumed to apply to the entire set operation. For example, you could find all authors who are also employees and whose last last name starts with "P": SELECT a.au_lname FROM authors AS a WHERE a.au_lname LIKE 'P%' INTERSECT SELECT e.lname FROM employee AS e WHERE e.lname LIKE 'W%'; SQL ServerNot supported. However, you can use NOT IN or NOT EXISTS operations in conjunction with a correlated subquery as alternatives. Refer to the EXCEPT Set Operator section for examples. See also
The IS operator determines whether a value is NULL or not.
SQL2003 Syntax{WHERE | {AND | OR} } expression IS [NOT] NULL Keywords
Rules at a GlanceBecause the value of NULL is unknown, you cannot use comparison expressions to determine if a value is NULL. For example, the expressions X = NULL and X <> NULL cannot be resolved because no value can equal, or not equal, an unknown. Instead, you must use the IS NULL operator. Be sure that you do not put the word NULL within quote marks, because if you do that, the DBMS will interpret the value as the word "NULL" and not the special value of NULL. Programming Tips and GotchasSome platforms support the use of a comparison operator to determine if an expression is NULL. However, all platforms covered by this book now support the ANSI IS [NOT] NULL syntax. Sometimes, checking for NULL will make your WHERE clause only slightly more complex. For example, rather than a simple predicate to test the value of stor_id: SELECT stor_id, ord_date FROM sales WHERE stor_id IN (6630, 7708) you need to add a second predicate to accomodate the possibility that stor_id might be NULL: SELECT stor_id, ord_date FROM sales WHERE stor_id IN (6630, 7708) OR stor_id IS NULL See Also
The JOIN subclause enables you to retrieve rows from two or more logically related tables. You are able to define many different join conditions and types of joins, though the types of joins supported by the different platforms vary greatly.
SQL2003 SyntaxFROM table [AS alias] {CROSS JOIN | { [NATURAL] [join_type] JOIN joined_table [AS alias] { ON join_condition1 [{AND|OR} join_condition2] [...] ] | USING (column1 [,...]) } } [...] Keywords
Join_conditions are syntactically depicted in the following form (note that join types are intentionally excluded in this example): FROM table_name1 JOIN table_name2 ON table_name1.column1 = table_name2.column2 [{AND|OR} table_name1.column3 = table_name2.column4] [...] JOIN table_name3 ON table_name1.columnA = table_name3.columnA [{AND|OR} table_name1.column3 = table_name2.column4] [...] [JOIN...] Use the AND operator and the OR operator to issue a JOIN with multiple conditions. It is also a good idea to use brackets around each pair of joined tables if more than two tables are involved. This makes reading the query much easier.
Rules at a GlanceJoins enable you to retrieve records from two (or more) logically related tables in a single result set. You can use an ANSI JOIN (detailed here) to perform this operation, or something called a theta join. Theta joins, which use the WHERE clause to establish the filtering criteria, are the "old" way to do join operations. For example, you might have a table called employee that tracks information about everyone employed in your company. The employee table, however, doesn't have extensive information about the job an employee holds. Instead, the employee table holds only a job_id. All information about the job, such as description and title, are stored in a table called job. Using a JOIN, you can easily return columns from both tables in a single set of records. The following sample queries illustrate the difference between a theta and an ANSI JOIN: /* Theta join */ SELECT emp_lname, emp_fname, job_title FROM employee, jobs WHERE employee.job_id = jobs. job_id; /* ANSI join */ SELECT emp_lname, emp_fname, job_title FROM employee JOIN jobs ON employee.job_id = jobs.job_id; Whenever you reference multiple columns in a single query, the columns must be unambiguous. In other words, the columns must either be unique to each table, or they must be referenced with a table identifier. In the example above, both tables have a job_id column, so references to job_id must be qualified with a table identifier. The columns in the query that don't exist in both tables don't need to be qualified by table identifiers. However, queries like this are often very hard to read. The following variation of the previous ANSI join is in better form, because it uses the short, and easy to read, alias e to refer to the table: SELECT e.emp_lname, e.emp_fname, j.job_title FROM employee AS e JOIN jobs AS j ON e.job_id = j.job_id; Previous examples were limited to equi-joins - joins using equality and an equal sign (=). However, most other comparison operators are allowed. You can perform joins on >, <, >=, <=, <> , and so forth. You cannot join on large object binary datatypes (i.e., BLOB) or other large object datatypes (e.g., CLOB, NLOB, etc.). Other datatypes are usually allowed in a join comparison. Following are examples of each type of join:
SELECT * FROM employee, jobs; SELECT * FROM employee CROSS JOIN jobs; SELECT * FROM employee JOIN jobs; SELECT a.au_lname AS 'first name', a.au_fname AS 'last name', p.pub_name AS 'publisher' FROM authors AS a INNER JOIN publishers AS p ON a.city = p.city ORDER BY a.au_lname DESC There are lots of authors in the authors table, but very few of them have a city that matches the city of the publishers in the publishers table. For example, the above query executed in the pubs database on a SQL Server produces results like the following: first name last name publisher --------------- -------------------- ------------------ Carson Cheryl Algodata Infosystems Bennet Abraham Algodata Infosystems The join is called an inner join because only those records that meet the join condition in both tables are said to be "inside" the join. You could also issue the same query, on platforms that support it, by substituting the USING clause for the ON clause: SELECT a.au_lname AS 'first name', a.au_fname AS 'last name', p.pub_name AS 'publisher' FROM authors AS a INNER JOIN publishers AS p USING (city) ORDER BY a.au_lname DESC The results for the above query would be the same.
In the following example, we can show a LEFT OUTER JOIN by asking for the publisher for each author (we could also substitute the USING clause for the ON clause, as shown in the example for INNER JOIN shown earlier): SELECT a.au_lname AS 'first name', a.au_fname AS 'last name', p.pub_name AS 'publisher' FROM authors AS a LEFT OUTER JOIN publishers AS p ON a.city = p.city ORDER BY a.au_lname DESC In the above example, every author will be returned along with the publisher's name, where there is a match, or a NULL value, where there is no match. For example, in the SQL Server pubs database, the query above returns: first name last name publisher --------------- -------------------- ---------------- Yokomoto Akiko NULL White Johnson NULL Stringer Dirk NULL Straight Dean NULL ... All of the data is returned in the lefthand columns (those from the authors table) and any column on the righthand side (those from the publishers table) that does not have a match returns a NULL value. The result set shows NULL values where no data matches in the join.
SELECT a.au_lname AS 'first name', a.au_fname AS 'last name', p.pub_name AS 'publisher' FROM authors AS a RIGHT OUTER JOIN publishers AS p ON a.city = p.city ORDER BY a.au_lname DESC returns the following result set: first name last name publisher ---------------- ---------------- ------------------------ Carson Cheryl Algodata Infosystems Bennet Abraham Algodata Infosystems NULL NULL New Moon Books NULL NULL Binnet & Hardley All of the data is returned on the righthand side of the query (hence the term "right outer join") and any columns from the left-hand side that do not have a match return a NULL value. The result set shows NULL values where no data matches in the join.
SELECT a.au_lname AS 'first name', a.au_fname AS 'last name', p.pub_name AS 'publisher' FROM authors AS a NATURAL RIGHT OUTER JOIN publishers AS p ORDER BY a.au_lname DESC The preceding query will work as the earlier examples, but only if both tables possess a column called city and that is the only column that they hold in common. You could similarly perform any of the other types of joins (INNER, FULL, OUTER) using the NATURAL prefix.
SELECT a.au_lname AS 'first name', a.au_fname AS 'last name', p.pub_name AS 'publisher' FROM authors AS a FULL JOIN publishers AS p ON a.city = p.city ORDER BY a.au_lname DESC The result set returned by the query is actually the accumulation of the result sets of issuing separate INNER, LEFT, and RIGHT join queries (some records have been excluded for brevity): first name last name publisher -------------------- -------------------- -------------------- Yokomoto Akiko NULL White Johnson NULL Stringer Dirk NULL ... Dull Ann NULL del Castillo Innes NULL DeFrance Michel NULL Carson Cheryl Algodata Infosystems Blotchet-Halls Reginald NULL Bennet Abraham Algodata Infosystems NULL NULL Binnet & Hardley NULL NULL Five Lakes Publishin NULL NULL New Moon Books ... NULL NULL Scootney Books NULL NULL Ramona Publishers NULL NULL GGG&G As you can see above, with the FULL JOIN you get some records with the NULLs on the right and data on the left (LEFT JOIN), some with all data (INNER JOIN), and some with NULLs on the left and data on the right (RIGHT JOIN). Programming Tips and GotchasIf an explicit join_type is omitted, then an INNER JOIN is assumed. Note that there are many types of joins, each with their own rules and behaviors, described in the "Rules at a Glance." In general, you should favor the JOIN clause over the WHERE clause for describing join expressions. This not only keeps your code cleaner, making it easy to differentiate join conditions from search conditions, but you also avoid the possibility of buggy behavior resulting from some platform-specific implementations of outer joins specified using the WHERE clause. In general, we do not recommend the use of labor-saving keywords like NATURAL, since the subclause will not automatically update itself when the structure of the underlying tables change. Consequently, statements using these constructs may fail when a table change is introduced without also changing the query. Not all join types are supported by all platforms, so refer to the sections below for full details on platform-specific join support.
DB2DB2 supports INNER and OUTER joins using the ON condition clause. It does not support CROSS or NATURAL join sytax, nor the USING clause. DB2's JOIN clause syntax is: FROM table [AS alias] { {[INNER] | [ {LEFT | RIGHT | FULL} [OUTER]]} JOIN joined_table [AS alias] { ON join_condition1 [{AND|OR} join_condition2] [...] ] } [...] Refer to the "Rules at a Glance" section for examples. MySQLMySQL supports most ANSI syntax, except that natural joins are only supported on outer joins, not on inner joins. MySQL's JOIN syntax is: FROM table [AS alias] {[STRAIGHT_JOIN joined_table] | { {[INNER] | | [CROSS] | [NATURAL] [ {LEFT | RIGHT | FULL} [OUTER]]} JOIN joined_table [AS alias] { ON join_condition1 [{AND|OR} join_condition2] [...] ] | USING (column1 [,...]) } } } [...] where:
The STRAIGHT_JOIN keyword is functionally equivalent to JOIN, except that it forces the join order from left to right. This option was supplied because MySQL might, rarely, join the tables in the wrong order. Refer to the Rules at a Glance section for examples. OracleOracle fully supports the entire ANSI standard JOIN syntax. However, Oracle only began to support ANSI standard JOIN syntax in Version 9. Consequently, any older Oracle SQL code exclusively uses WHERE clause joins. Oracle's old syntax for outer theta joins included adding (+) to column names on the side opposite of the outer table. Oracle's old syntax for outer joins comes from the fact that the table supplying the null value rows in effect has null value rows added to it. Oracle represents this by appending the expression "(+)" to the column name on the opposite side of the direction of the join. For example, the following query does a RIGHT OUTER JOIN on the authors and publishers tables. The old Oracle syntax looks like this: SELECT a.au_lname AS 'first name', a.au_fname AS 'last name', p.pub_name AS 'publisher' FROM authors a, publishers p WHERE a.city(+) = p.city ORDER BY a.au_lname DESC while the same query in the ANSI syntax would look like: SELECT a.au_lname AS 'first name', a.au_fname AS 'last name', p.pub_name AS 'publisher' FROM authors AS a RIGHT OUTER JOIN publishers AS p ON a.city = p.city ORDER BY a.au_lname DESC Refer to the Rules at a Glance section for more JOIN examples. Oracle is unique in offering partitioned outer joins, which are useful for filling gaps in result sets due to sparse data storage. For example, assume we store production records in a manufacturing table keyed on day and product_id. The table holds a row showing quantity produced for each product for any day on which it is made, but there are no rows for the days it is not produced. This is considered sparse data, since a list of all rows will not show every day for every product. For calculation and reporting purposes, it's very useful to create result sets where each product has a row for every day, whether or not it was manufactured. Partition outer join makes it simple to do that, since it lets you define a logical partition and apply an outer join to each partition value. Here is the manufacturing example, which does a partitioned outer join with a times table to make sure each product_id has the full set of dates in a specified time range: SELECT times.time_id AS time, product_id AS id, quantity AS qty FROM manufacturing PARTITION BY (product_id) RIGHT OUTER JOIN times ON (manufacturing.time_id = times.time_id) WHERE manufacturing.time_id BETWEEN TO_DATE('01/10/05', 'DD/MM/YY') AND TO_DATE('06/10/05', 'DD/MM/YY') ORDER BY 2, 1; Following is the output from this query: time id qty --------- ------ --- 01-OCT-05 101 10 02-OCT-05 101 03-OCT-05 101 04-OCT-05 101 17 05-OCT-05 101 23 06-OCT-05 101 01-OCT-05 102 02-OCT-05 102 03-OCT-05 102 43 04-OCT-05 102 99 05-OCT-05 102 06-OCT-05 102 87 To get these results without using a partitioned outer join would require more complex and less efficient SQL. PostgreSQLPostgreSQL fully supports the ANSI standard. Refer to the Rules at a Glance section for examples. SQL ServerSQL Server supports INNER, OUTER, and CROSS joins using the ON clause. SQL Server does not support NATURAL join sytax, nor the USING clause. SQL Server's JOIN syntax is: FROM table [AS alias] { {[INNER] | | [CROSS] | [ {LEFT | RIGHT | FULL} [OUTER]]} JOIN joined_table [AS alias] { ON join_condition1 [{AND|OR} join_condition2] [...] ] } [...] Refer to the Rules at a Glance section for examples. See Also
The LIKE operator enables specified string patterns in SELECT, INSERT, UPDATE, and DELETE statements to be matched, specifically in the WHERE clause. A specified pattern may include special wildcard characters. The specific wildcards supported vary from platform to platform.
SQL2003 SyntaxWHERE expression [NOT] LIKE string_pattern [ESCAPE escape_sequence] Keywords
Rules at a GlanceMatching string patterns is easy with LIKE, but there are a couple of simple rules to remember:
The ANSI standard currently supports two wildcard operators that are supported by all of the platforms covered in this book:
The first query in the following example retrieves any city record with "ville" in its name. The second query returns authors with a first name not like Sheryl or Cheryl (or Aheryl, Bheryl, Dheryl, 2heryl, and so forth). SELECT * FROM authors WHERE city LIKE '%ville%'; SELECT * FROM authors WHERE au_fname NOT LIKE '_heryl'; Some of the platforms support additional wildcard symbols. These are described in the platform-specific sections that follow. Use of the ESCAPE clause allows you to look for wildcard characters in the strings stored in your database. Using this mechanism, you designate a character as your escape character, typically a character that does not otherwise appear in the pattern string. For example, you might designate the tilde (~) because you know it never appears in the pattern string. Any wildcard character preceded by the escape sequence is then not treated as a wildcard, but rather as the character itself. For example, to look through the comments column of the sales_detail table (on SQL Server) to see whether any customers have mentioned a newly introduced discount: SELECT ord_id, comment FROM sales_detail WHERE comment LIKE '%~%%' ESCAPE '~' In this case, the first and last % are interpreted as wildcards. But the second % character is interpreted as just that, a % character, because it is preceded by the designated escape sequence. Programming Tips and GotchasThe usefulness of LIKE is based on the wildcard operators that it supports. LIKE returns a Boolean TRUE value when the comparison finds one or more matching values. The default case sensitivity of the DBMS is very important to the behavior of LIKE. For example, Microsoft SQL Server is not case-sensitive by default (though it can be configured that way). Thus, Microsoft SQL Server will evaluate the strings DAD and dad to be equal. Oracle, on the other hand, is case-sensitive. Thus, on Oracle, a comparison of DAD and dad would show them to be unequal. Here's an example query to better illustrate this point: SELECT * FROM authors WHERE lname LIKE 'LARS%' This query on Microsoft SQL Server would find authors whose last names are stored as 'larson' or 'lars,' even though the search was for uppercase 'LARS%'. Oracle would not find 'Larson' or 'Lars', because Oracle performs case-sensitive comparisons. DB2DB2 supports the ANSI SQL2003 syntax for LIKE. It supports the percent (%) and underscore ( _ ) wildcards. It supports ESCAPE sequences. DB2 is case sensitive, so its implementation of LIKE is completely case sensitive. You should use a function like UPPER or TRANSLATE to ensure that differently cased values are always evaluated uniformly. In addition, DB2 will implicitly convert the code page of either the string_pattern or the escape_sequence to that of the expression, as long as none of them are defined as FOR BIT DATA. MySQLMySQL supports the ANSI syntax for LIKE. It supports the percent (%) and underscore ( _ ) wildcards. It also supports the ESCAPE clause. MySQL also supports the special functions of REGEXP and RLIKE, plus NOT REGEXP and NOT RLIKE for the evaluation of regular expressions. MySQL, after Version 3.23.4, is not case sensitive by default. OracleOracle supports the ANSI syntax for LIKE. It supports the percent (%) and underscore ( _ ) wildcards. It supports ESCAPE sequences. Oracle's LIKE syntax is as follows: WHERE expression [NOT] {LIKE | LIKEC | LIKE2 | LIKE4} string_pattern [ESCAPE escape_sequence] The Oracle-specific syntax elements have the following meanings:
Since Oracle is case sensitive, you should enclose the expression, the string_pattern, or both, with the UPPER function. That way, you are always comparing apples to apples. PostgreSQLPostgreSQL supports the ANSI syntax for LIKE. It supports the percent (%) and underscore ( _ ) wildcards. It supports ESCAPE sequences. PostgreSQL is case sensitive by default. PostgreSQL provides the keyword ILIKE for case-insensitive pattern matching. You can also use the operator ~~ as an equivalent to LIKE, and ~~* for ILIKE, as well as !~~ and !~~* for NOT LIKE and NOT ILIKE, respectively. These are all extensions to the ANSI SQL2003 standard. For example, the following queries are functionally the same: SELECT * FROM authors WHERE city LIKE '%ville'; SELECT * FROM authors WHERE city ~~ '%ville'; Since these queries are in lowercase, you might run into a case-sensitivity problem. That is, the queries are looking for a lowercase '%ville', but the table might contain uppercase (and unequal) values of 'BROWNSVILLE', 'NASHVILLE', and 'HUNTSVILLE'. You can get around this as shown below: -- convert the values to upper case SELECT * FROM authors WHERE city LIKE UPPER('%ville'); -- perform the pattern match using case insensitivity SELECT * FROM authors WHERE city ~~* '%ville'; SELECT * FROM authors WHERE city ILIKE '%ville'; Although beyond the scope of this text, you should be aware that PostgreSQL also supports POSIX regular expressions. See the platform documentation for details. SQL ServerSQL Server supports the ANSI syntax for LIKE. It supports ESCAPE sequences. It supports the following additional wildcard operators:
Using SQL Server's additional wildcard operators, you have some added capabilities. For example, you can retrieve any author with a last name like Carson, Carsen, Karson, or Karsen: SELECT * FROM authors WHERE au_lname LIKE '[CK]ars[eo]n' Or you can retrieve any author with a last name that ends in "arson" or "arsen," but not Larsen or Larson: SELECT * FROM authors WHERE au_lname LIKE '[A-Z^L]ars[eo]n'
See Also
The MERGE statement is sort of like a CASE statement for DML operations. It combines UPDATE and INSERT statements into a single atomic statement with either/or functionality. Basically, MERGE examines the records of a source table and a target table. If the records exist in both the source and target tables, based upon predefined conditions, then records in the target table are updated with the values of the records in the source table. If records do not exist in the target table that do exist in the source table, then they are inserted into the target table. The MERGE statement was added in the SQL2003 release of the ANSI standard.
SQL2003 SyntaxMERGE INTO {object_name | subquery} [ [AS] alias ] USING table_reference [ [AS] alias] ON search_condition WHEN MATCHED THEN UPDATE SET column = { expression | DEFAULT } [, ...] WHEN NOT MATCHED THEN INSERT [( column [, ...] )] VALUES ( expression [, ...] ) Keywords
Rules at a GlanceThe rules for using MERGE are straightforward:
Programming Tips and GotchasThe MERGE statement has sometimes been nicknamed the "upsert" statement. That is because it allows, in a single operation, a set of records to be either inserted into a table or, if they already exist, updated with new values. The only tricky aspect of the MERGE statement is simply getting used to the idea of either/or processing of the INSERT and UPDATE statements. Assume that we have two tables. The EMP table contains all employees of the company who have successfully completed the mandatory 90-day probationary period at the start of their employment. Employees in the EMP table can also have several statuses, such as active, inactive, and terminated. Any new hire to the company is recorded in the NEW_HIRE table. After 90 days, they are moved into the EMP table like all other regular employees. However, since our company hires college interns every summer, it's very likely that some of our new hires will actually have a record in the EMP table from last year with a status of inactive. Using pseudocode, the business problem is summarized as: For each record in the NEW_HIRE table Find the corresponding record in the EMP table If the record exists in the EMP table Update existing data in the EMP table Else Insert this record into the EMP table End If End For We could write a rather lengthy stored procedure that will examine all the records of the NEW_HIRE table and then conditionally perform INSERT statements for the entirely new employees or UPDATE statements for the returning college interns. But the following ANSI standard MERGE statement makes this process much easier: MERGE INTO emp AS e USING (SELECT * FROM new_hire) AS n ON e.empno = n.empno WHEN MATCHED THEN UPDATE SET e.ename = n.ename, e.sal = n.sal, e.mgr = n.mgr, e.deptno = n.deptno WHEN NOT MATCHED THEN INSERT ( e.empno, e.ename, e.sal, e.mgr, e.deptno ) VALUES ( n.empno, n.ename, n.sal, n.mgr, n.deptno ); As you can see, the MERGE statement is very useful for data loading operations. DB2DB2 supports the ANSI standard for the MERGE statement. MySQLNot supported. OracleOracle supports the MERGE statement with only the tiniest variations, which are almost entirely evident by comparing the Oracle syntax diagram against the ANSI standard syntax diagram: MERGE INTO [schema.] {object_name | subquery} [alias ] USING [schema.]table_reference [alias] ON ( search_condition ) WHEN MATCHED THEN UPDATE SET column = { expression | DEFAULT } [, ...] WHEN NOT MATCHED THEN INSERT ( column [, ...] ) VALUES ( expression [, ...] The differences between the ANSI standard and Oracle's implementation include:
Refer to the examples in the earlier "Rules at a Glance" and "Programming Tips and Gotchas" for more information. PostgreSQLNot supported. SQL ServerNot supported. See Also
The OPEN statement is one of four commands used in cursor processing, along with DECLARE, FETCH, and CLOSE. Cursors allow you to process queries one row at a time, rather than in a complete set. The OPEN statement opens a pre-existing server cursor created with the DECLARE CURSOR statement. Cursors are especially important on relational databases because databases are set-based, while most client-centric programming languages are row-based. So cursors allow programmers and databases to perform operations a single row at a time, while the default behavior of a relational database is to operate on a whole set of records.
SQL2003 SyntaxOPEN cursor_name Keywords
Rules at a GlanceAt the highest level, a cursor must be:
By following these steps, you create a result set similar to that of a SELECT statement, except that you can operate against each individual row within the result set. For example, assume, on a DB2 database, that you've created a cursor called employee_cursor, containing three columns: DECLARE CURSOR employee_cursor FOR SELECT lname, fname, emp_id FROM employee WHERE hire_date >= 'FEB-14-2004'; Having created the cursor, you can now open the cursor and fetch data from it into some host variables: OPEN employee_cursor; FETCH NEXT FROM employee_cursor INTO :emp_last_name, :emp_first_name, :emp_id The FETCH statement allows you to position the current row within the result set retrieved by the the cursor. If you haven't accessed the cursor yet, you will be given the first record in the result set. You can then use any procedural code you want to operate against the values in that row. The following generic SQL example opens a cursor and fetches all the author firstname and author lastname values from the authors table: DECLARE employee_cursor CURSOR FOR SELECT au_lname, au_fname FROM pubs.dbo.authors WHERE lname LIKE 'K%' OPEN employee_cursor FETCH NEXT FROM employee_cursor BEGIN FETCH NEXT FROM employee_Cursor END CLOSE employee_cursor Programming Tips and GotchasThe most common error encountered with the OPEN statement is failing to close the cursor properly. Although the OPEN statement is detailed in isolation here, it should always be managed as a group with the DECLARE, FETCH, and CLOSE statements. Although you will not get an error message for failing to close a cursor, the cursor may continue to consume memory, locks, and other resources on the server as long as it is open. For example, if you forget to close cursors, you could be creating a headache somewhat similar to a memory leak - each cursor consumes memory until it is closed. Even if you no longer use a cursor, it is still taking up memory that the database server might otherwise be using elsewhere. So take a little extra time to make sure that every declared and opened cursor is eventually closed. Cursors are often used in stored procedures and in batches of procedural code. They are useful when you need to perform actions on individual rows rather than on entire sets of data at a time. But because cursors operate on individual rows and not on sets of data, they are often much slower than other means of accessing data. Just make sure that you perform strong analysis on your approach. Many challenges, such as a convoluted DELETE operation or a very complex UPDATE, can be solved by using clever WHERE and JOIN clauses instead of cursors. DB2DB2 supports the ANSI standard for OPEN with a couple of additions: OPEN cursor_name [USING {variable_name1 [,...] | DESCRIPTOR descriptor_name}] The DB2 specific syntax elements are:
For example, the following DB2 code declares several host (called hostvarxxx) and dynamic variables (defined with the PREPARE statement), and then uses them in a cursor called dynamic_cursor: EXEC SQL BEGIN DECLARE SECTION; static short hostvar_int; char hostvar_vchar64[65]; char statement_str[200]; EXEC SQL END DECLARE SECTION; EXEC SQL PREPARE statement_name FROM :statement_str; EXEC SQL DECLARE dynamic_cursor CURSOR FOR statement_name; EXEC SQL OPEN dynamic_cursor USING :hostvar_int, :hostvar_vchar64; MySQLNot supported. OracleOracle fully supports the ANSI standard. Plus, Oracle allows parameters to be passed directly into the cursor when it is opened. Do this using the following format: OPEN cursor_name parameter1 [,...] PostgreSQLPostgreSQL does not have an OPEN CURSOR statement. Instead, PostgreSQL implicitly opens a cursor when it is created by the DECLARE statement. SQL ServerIn addition to the standard OPEN statement, SQL Server allows global cursors using the following syntax: OPEN [GLOBAL] cursor_name where:
SQL Server allows you to declare several different kinds of cursors. If a cursor is INSENSITIVE or STATIC, then the OPEN statement creates a temporary table to hold the result set of the cursor. Similarly, if the declared cursor has the KEYSET option, then a temporary table is automatically created to hold the keyset. See Also
The ORDER BY clause specifies the sort order of the result set retrieved by a SELECT statement.
SQL2003 SyntaxORDER BY {sort_expression [COLLATE collation_name] [ASC | DESC]} [,...] Keywords
Rules at a GlanceThe ORDER BY clause should reference columns as they appear in the select item list of the SELECT statement, preferably using their alias, if one exists. For example: SELECT au_fname AS first_name, au_lname AS last_name FROM authors ORDER BY first_name, last_name The ORDER BY clause uses a major-to-minor sort ordering. This means that the result set is ordered by the first column referenced, equal values in the first column are then ordered by the second column, equal values in the second column are then ordered by the third column, and so forth. The individual aspects of a column's ordering - COLLATE and ASC/DESC - are independent of the other columns in the ORDER BY clause. Thus, you could order a result set in ascending order by one column, and then flip the next column and order it in descending order: SELECT au_fname AS first_name, au_lname AS last_name FROM authors ORDER BY au_lname ASC, au_fname DESC NULLs are always grouped together (i.e., considered equal) for the purposes of sorting. Depending on your platform, NULLs will be clumped together at the top or at the bottom of the result set. The following query on SQL Server: SELECT title, price FROM titles ORDER BY price, title provides this result set (edited for brevity): title price ---------------------------------------------------- ------- Net Etiquette NULL The Psychology of Computer Cooking NULL The Gourmet Microwave 2.9900 You Can Combat Computer Stress! 2.9900 Life Without Fear 7.0000 Onions, Leeks, and Garlic: Cooking Secrets of the Me 20.9500 Computer Phobic AND Non-Phobic Individuals: Behavior 21.5900 But Is It User Friendly? 22.9500 You can force NULLs to appear at the top or bottom of the result set using ASC or DESC. Of course, all the non-NULL rows of the result set are also ordered in ascending or descending order too. The ANSI standard for ORDER BY also supports the use of columns for the sort_expression that are not referenced in the select item list. For example, the following query is valid under SQL2003: SELECT title, price FROM titles ORDER BY title_id Looking at the example above, you can see that although the query does not select title_id, column is the primary sort_expression. The result set is returned in title_id order even though that column is not selected. Programming Tips and GotchasWhen using set operators (UNION, EXCEPT, INTERSECT), only the last query may have an ORDER BY clause. You should not use ORDER BY in subqueries of any type. A number of behaviors that were supported in SQL92 are deprecated in SQL2003. You should avoid these usages:
You may sort not only on columns, but also on expressions involving columns or even literals: SELECT SUBSTRING(title,1,55) AS title, (price * 1.15) as price FROM titles WHERE price BETWEEN 2 and 19 ORDER BY price, title When sorting on expressions from the select item list, you should use aliases to make the ORDER BY sort_expression column references easier! DB2DB2 supports the ANSI syntax, except for the COLLATE option. It also has the ORDER OF extension: ORDER BY {ORDER OF table_name | sort_expression [{ASC | DESC}] } where:
SELECT qty, price, order_nbr FROM (SELECT order_nbr, qty, price FROM sales_Q1_2005 UNION SELECT order_nbr, qty, price FROM sales_Q2_2005 ORDER BY price, qty, order_nbr) AS ytd_sales ORDER BY ORDER OF ytd_sales DB2 still supports SQL92 functions, such as ordering by ordinal position. By default, DB2 sorts NULL values as higher than all other values. Another example of order subqueries follows. First, we will create a view called best that returns the top five best selling books from the sales table, and then we will find the top five authors in order: CREATE VIEW best(title) AS SELECT title FROM sales ORDER BY sold FETCH FIRST 5 ROWS ONLY SELECT author FROM best JOIN authors ON best.title = author.title ORDER BY ORDER OF best DB2 does not support ORDER BY on columns of the following datatypes: LONG VARCHAR, CLOB, LONG VARGRAPHIC, DBCLOB, BLOB, and DATALINK. DB2 also does not support ordering by structure types. MySQLMySQL supports the ANSI standard, except for the COLLATE option. You should not attempt to ORDER BY columns of the BLOB datatype because only the first bytes, defined by the max_sort_length setting, are used in the sort. By default, MySQL sorts NULL values as lowest (first) for ASC order and highest (last) for DESC order. OracleOracle supports the ANSI syntax, except for the COLLATE option. It also supports the SIBLINGS and NULLS {FIRST | LAST} options. Oracle's ORDER BY syntax is: ORDER [SIBLINGS] BY {sort_expression [ASC | DESC] [NULLS {FIRST | LAST}]} [,...] where the Oracle-specific keywords are:
You can emulate the behavior of the COLLATE option for a single session by using the NLSSORT function with the NLS_SORT parameter. You can explicitly emulate the behavior of the COLLATE option for all sessions on the server by using the NLS_SORT initialization parameter or, implicitly, with the NLS_LANGUAGE initialization parameter. Oracle continues to support deprecated SQL92 features such as sorting by ordinal position. You should not perform an ORDER BY on any LOB column, nested table, or VARRAY. PostgreSQLPostgreSQL supports the ANSI SQL2003 standard, with the exception of the COLLATE option. It also supports the USING extension: ORDER BY {sort_expression1 [ASC | DESC | USING operator]} [,...] where:
PostgreSQL sorts NULLs as the highest values. Thus, NULLs will appear at the end when sorting in ascending order, and at the beginning when sorting in descending order. SQL ServerSQL Server supports the ANSI standard including the COLLATE option. For example, the following query retrieves author firstnames from the authors table in the SQL_Latin1 collation: SELECT au_fname FROM authors ORDER BY au_fname COLLATE SQL_Latin1_general_cp1_ci_as SQL Server continues to support the deprecated SQL92 features of the ORDER BY clause, including the ability to specify ORDER BY columns by their ordinal positions. By default, SQL Server sorts NULL values higher than all other values. You should not use TEXT, IMAGE, or NTEXT columns as sort_expressions on SQL Server. See Also
The RELEASE SAVEPOINT statement eliminates one or more previously created savepoints in the current transaction.
SQL2003 SyntaxRELEASE SAVEPOINT savepoint_name Keywords
Rules at a GlanceUse the RELEASE SAVEPOINT statement within a transaction to destroy a named savepoint. Any savepoints that were created after the named savepoint will also be destroyed. To illustrate the behavior of savepoints, the following example code inserts a few records, creates a savepoint named first_savepoint, and then releases it: INSERT authors (au_id, au_lname, au_fname, contract ) VALUES ('111-11-1111', 'Rabbit', 'Jessica', 1); SAVEPOINT first_savepoint; INSERT authors (au_id, au_lname, au_fname, contract ) VALUES ('277-27-2777', 'Fudd', 'E.P.', 1); INSERT authors (au_id, au_lname, au_fname, contract ) VALUES ('366-36-3636', 'Duck', 'P.J.', 1); RELEASE SAVEPOINT first_savepoint; COMMIT; In this example, the first_savepoint savepoint is destroyed and then all three records are inserted into the authors table. In the next example, we perform the same action but with more savepoints: INSERT authors (au_id, au_lname, au_fname, contract ) VALUES ('111-11-1111', 'Rabbit', 'Jessica', 1); SAVEPOINT first_savepoint; INSERT authors (au_id, au_lname, au_fname, contract ) VALUES ('277-27-2777', 'Fudd', 'E.P.', 1); SAVEPOINT second_savepoint; INSERT authors (au_id, au_lname, au_fname, contract ) VALUES ('366-36-3636', 'Duck', 'P.J.', 1); SAVEPOINT third_savepoint; RELEASE SAVEPOINT second_savepoint; COMMIT; In the earlier example, when we release the savepoint called second_savepoint, the database actually releases second_savepoint and third_savepoint, since third_savepoint was issued after second_savepoint. Once released, a savepoint name can be reused. Programming Tips and GotchasIssuing either a COMMIT or full ROLLBACK statement will destroy all open savepoints in a transaction. Issuing a ROLLBACK TO SAVEPOINT statement returns the transaction to its state at the specified savepoint. Thus, any savepoints declared afterwards are nullified. DB2DB2 supports the RELEASE SAVEPOINT statement fully with one optional keyword: RELEASE [TO] SAVEPOINT savepoint_name where:
MySQLNot supported. OracleNot supported. PostgreSQLNot supported. SQL ServerNot supported. See Also
The RETURN statement terminates processing within a SQL-invoked function (as opposed to a host-invoked function) or stored procedure and returns the routine's result value.
SQL2003 SyntaxRETURN return_parameter_value | NULL Keywords
Rules at a GlanceUse the RETURN statement within procedural code to terminate processing. For example, you might create a user-defined function that takes a complex and often-used CASE expression and, when passed a parameter, returns a single, easy-to-understand expression value. Programming Tips and GotchasAlthough the RETURN statement is categorized as a separate command within SQL, it is deeply intertwined with the CREATE FUNCTION and CREATE PROCEDURE statements. Consequently, the RETURN statement is almost always found embedded in one of these other commands. Check each statement's section in this book, or your vendor documentation, to get a more complete understanding of each platform's implementation of RETURN within the context of each statement. DB2DB2 uses the RETURN statement to exit a routine and, in a procedure, return any integer value. In a user-defined function, the RETURN statement returns the actual value that is calculated by the function. DB2 also allows query-based results to be returned by the statement, as in the following: RETURN [ {expression | NULL | WITH table_name ( column [,...] ) [AS (select_statement)] } ] select_statement where:
If RETURN is used in a procedure, the value of expression must be an integer, but not in a user-defined function. The NULL and WITH columns AS select_statement clauses can be used with user-defined functions and methods, but not stored procedures. Here's a simple example of RETURN: BEGIN ...<some SQL code>... GOTO ERROR_HANDLER ... <some SQL code>... SUCCESS: RETURN 0 ERROR_HANDLER: RETURN -200 END If you need to check the RETURN value of a stored procedure that is called by another stored procedure, use the GET DIAGNOSTICS statement to find it. Refer to the DB2 documentation for more information on GET DIAGNOSTICS. MySQLNot supported. All procedural code in MySQL is written in C or C++. You can use C or C++ programming techniques to return value(s) to the client. OracleOracle supports the ANSI standard syntax for RETURN, excluding the NULL keyword. (Oracle does support the return of a NULL value, just not using the ANSI syntax.) Oracle allows the RETURN clause only in user-defined functions and user-defined operators. The returned value cannot be a LONG, LONG RAW, or REF datatype in the CREATE OPERATOR statement. PL/SQL user-defined functions fully support Boolean datatypes internally, but you cannot invoke a Boolean UDF from a SQL statement. Therefore, just use INT (0 or 1) or VARCHAR2 ('TRUE' or 'FALSE') to hold Boolean values. This example creates a function. The function returns the value that is stored in the proj_rev variable to the calling session: CREATE FUNCTION project_revenue (project IN varchar2) RETURN NUMBER AS proj_rev NUMBER(10,2); BEGIN SELECT SUM(DECODE(action,'COMPLETED',amount,0) - SUM(DECODE(action,'STARTED',amount,0) + SUM(DECODE(action,'PAYMENT',amount,0) INTO proj_rev FROM construction_actions WHERE project_name = project; RETURN (proj_rev); END; PostgreSQLPostgreSQL supports the ANSI standard syntax for RETURN, excluding the NULL keyword: RETURNS return_parameter_value | NULL PostgreSQL allows you to define user-defined functions using either SQL code or C/C++. (PostgreSQL does not currently allow stored procedures, though you can duplicate their functionality with user-defined functions). For the purposes of this discussion, we are only interested in SQL functions. The output allowed by the return_parameter_value may be a base type, complex type, SETOF type, OPAQUE modifier, or the same as the type of an existing column. The SETOF type modifier is used to return a set of items via the RETURNS statement, rather than a single data value. The OPAQUE modifier indicates the RETURNS statement does not return a value. OPAQUE can only be used within triggers. SQL ServerSQL Server supports the RETURN statement with this syntax: RETURN [return_integer_value] The RETURN command is typically used in stored procedures or user-defined functions. The command causes an immediate and complete exit from the program and, optionally, returns an integer value upon exit. SQL Server procedures implicitly return a zero value if no RETURN clause is supplied in the procedure definition. Any commands that follow the RETURN statement are ignored. The RETURN statement in the following function returns an integer representing the calculated value: CREATE FUNCTION metric_volume -
Input dimensions in centimeters.
(@length decimal(4,1),
@width decimal(4,1),
@height decimal(4,1) )
RETURNS decimal(12,3) -- Cubic Centimeters.
AS
BEGIN
RETURN ( @length * @width * @height )
END
GO
REVOKE This example creates a function that returns a calculated value, the metric volume of a space, to the calling session. See Also
The REVOKE statement takes two main forms. The first form of the statement removes specific statement permissions from a user, group, or role. The second form of the statement removes access permissions to specific database objects or resources.
SQL2003 SyntaxThe SQL2003 syntax for REVOKE takes this general form: REVOKE { [special_options] | {privilege [,...] | role [,...]} } ON database_object_name FROM grantee_name [,...] [GRANTED BY {CURRENT_USER | CURRENT_ROLE} ] {CASCADE | RESTRICT} Keywords
{ [TABLE] object_name | DOMAIN object_name | COLLATION object_name | CHARACTER SET object_name | TRANSLATION object_name | TYPE object_name | [SPECIFIC] {ROUTINE | FUNCTION | PROCEDURE | METHOD} object_name }
Rules at a GlanceA specific privilege on a specific database object can be revoked for a single user using REVOKE privilege_name ON object_name FROM grantee_name RESTRICT. A specific privilege on a specific object may be revoked from all users via the PUBLIC global user list. When revoking privileges from multiple grantees, simply place a comma between each. You may also revoke privileges from one or more grantees and the PUBLIC role in a single REVOKE statement. (PUBLIC is described in detail under the GRANT Statement section. When a grant is issued to PUBLIC, that means everyone has permissions to the object without the need for a grant specifically to the object.) When revoking privileges on a table, the privileges may extend to the column level by showing a list of columns enclosed in parentheses after the table name. Programming Tips and GotchasMost of the platforms isolate privileges at the user and role level. (Remember, a role is a group of privileges.) Thus, an individual user who is assigned two roles may have an individual set of permissions three times. In this situation, you would have to revoke the user's privileges directly and remove the user from both roles to completely negate their permissions. An important aspect about REVOKE (and its sister command GRANT) is that certain elements of the command are geared toward object-level permissions, while other options of the command are more oriented toward roles or administrative privileges. Generally, these operations are never combined. The differences in object-level permissions and administrative privileges are fully explained under each database platform section below. For example, you might want to revoke certain object-level privileges for a given role (salespeople) or individual user (e_fudd and prince_edward): REVOKE SELECT ON TABLE authors FROM salespeople RESTRICT; REVOKE ALL PRIVILEGES ON TABLE sales FROM e_fudd, prince_edward CASCADE; A lot of table-level privileges can even be assigned down to the column level. Those privileges can be revoked: REVOKE INSERT(au_id, au_fname, au_lname) ON authors FROM e_fudd; The special option clauses (GRANT OPTION, HIERARCHY OPTION, and ADMIN OPTION) all exist to allow you to revoke the ability of users to pass privileges or roles on to other users. However, these clauses do not prevent the first group of users themselves from exercising those privileges. For example, we no longer want anyone in the role manager to grant their UPDATE privileges to other users: REVOKE GRANT OPTION FOR UPDATE ON sales FROM manager CASCADE; You can also remove role privileges from a user with the REVOKE command: REVOKE manager FROM e_fudd CASCADE; A common best practice is to keep REVOKE and GRANT statements as self-contained and logical as possible. Under this practice, you should avoid using the CASCADE and ALL PRIVILEGES clauses because they perform work for you that may not be immediately evident within the scope of the command. DB2DB2 offers a variety of additional options in its implementation of the REVOKE command: REVOKE { [database_privileges [,...] ON DATABASE ] | [CONTROL ON INDEX index_name] | [ {BIND | CONTROL | EXECUTE} ON PACKAGE package_name ] | [ {ALTERIN | CREATEIN | DROPIN} ON SCHEMA schema_name ] | [PASSTHRU ON SERVER server_name ] | [EXECUTE ON [SPECIFIC] {FUNCTION | PROCEDURE | METHOD} routine_name ] | [object_privilege [,...] ON [TABLE] object_name ] | [{ALTER | USAGE} ON SEQUENCE sequence_name ] | [USE OF TABLESPACE tablespace_name ] } } [FROM [ {USER |GROUP} ] { grantee_name [,...] | group_name [,...] | PUBLIC } ] [RESTRICT] where:
MySQLThe REVOKE statement rolls back permissions previously granted to one or more users. MySQL supports many of the standard keywords of the ANSI standard. Notable exceptions for support include TRIGGER, EXECUTE, and UNDER. MySQL also has a nice shortcut to allow global assignment or revocation of privileges. Please refer back to the ANSI standard description for those elements of the command not described below: REVOKE [ { ALL [PRIVILEGES] | {SELECT | INSERT | UPDATE} [ (column_name [,...]) ] | DELETE | REFERENCES [ (column_name [,...]) ] } | [ USAGE ] | [{ALTER | CREATE | DROP}] | [FILE] | [INDEX] | [PROCESS] | [RELOAD] | [SHUTDOWN] | [CREATE TEMPORARY TABLES] | [LOCK TABLES] | [REPLICATION CLIENT] | [REPLICATION SLAVE] | [SHOW DATABASES] | [SUPER] }[,...] ON {table_name | * | *.* | database_name.*} FROM user_name [,...] where:
REVOKE has size limitations. Usernames cannot be longer than 16 characters, while host, database, and database object names can be up to 60 characters in size. Usernames may also be linked to a specific host. Refer to the GRANT command for more details.
It is also important to note that MySQL allows multiple levels of permissions. Thus, a user might have access to a table from a table-level grant and have an additional set of permissions to the same table because they have been give global database or server-wide permissions. This means you need to be careful when revoking permissions because global level permissions might continue to provide permissions to a user when you thought you had revoked them! The first command shown below revokes all privileges on the sales table for users emily and dylan, while the second command revokes select privileges for the user kelly in the current database: REVOKE ALL PRIVILEGES ON sales FROM emily, dylan; REVOKE SELECT ON * FROM kelly; The first command below removes kelly's ability to grant privileges to other users on the sales table, while the second command removes privileges that the user sam has in the pubs database: REVOKE GRANT OPTION ON sales FROM kelly; REVOKE ALL ON pubs.* FROM sam; OracleThe REVOKE command not only immediately revokes object and system privileges, it also revokes a role from a user (or a role from another role). Refer to the GRANT statement for more information on the specific object and system privileges supported by the REVOKE command.
Oracle provides an interesting twist on privileges. While other database platforms typically allow a user to have more than one context for a set of privileges (based on the individual user and again for any groups that they are a member of), Oracle takes this one step further. A user may also have many grantors who have given them permission on a specific object. When this is the case, ALL grantors must revoke the privilege to effectively rid the user of the privilege. If even one grantor doesn't do this, then the user still has the privilege. Use the following syntax with REVOKE: REVOKE { [object_privilege] [,...]| [system_privilege] | [role] } [ON { [schema_name.][object] | [DIRECTORY directory_object_name] | [JAVA [ { SOURCE | RESOURCE } ] [schema_name.][object] } ] FROM {grantee_name [,...] | role_name [,...] | PUBLIC} [CASCADE [CONSTRAINTS] ] [FORCE]; where:
Oracle automatically cascades the revocation from the defined grantee_name to all users that received their privileges from the grantee. In addition, any objects created by the grantee that depend on the privilege (such as stored procedures, triggers, views, and packages that depend on a SELECT privilege against a certain table) will become invalid. Users who are granted the GRANT ANY ROLE system privilege may revoke any role. The REVOKE command can only revoke privileges specifically granted with the GRANT command, not privileges available through roles or the operating system. In a case like this, you must use the REVOKE command to drop the privilege from the role. All users assigned to the role will then lose the privilege. The following is an example of revoking a role from specific grantee and revoking a system privilege from a role: REVOKE read-only FROM sarah; REVOKE CREATE ANY SEQUENCE, CREATE ANY DIRECTORY FROM read_only; Here's an example that revokes a REFERENCES privilege and cascades the revoked privileges: REVOKE REFERENCES ON pubs_new_york.emp FROM dylan CASCADE CONSTRAINTS; The following example grants all privileges on a specific table, and then revokes a privilege: GRANT ALL PRIVILEGES ON emp TO dylan; REVOKE DELETE, UPDATE ON emp FROM dylan; PostgreSQLPostgreSQL offers base-level features of the REVOKE statement, primarily for revoking privileges on specific tables, views, and sequences. It does not offer the ANSI standard special options like HIERARCY OPTION FOR or ADMIN OPTION FOR. The syntax is as follows: REVOKE { [ GRANT OPTION FOR ] | privileges [,...] } ON {object_name} [,...] FROM {grantee_name [,...] | PUBLIC | GROUP group_name} [ {CASCADE | RESTRICT} ] where:
PostgreSQL's implementation of REVOKE is relatively straightforward. The only issue to be aware of is that PostgreSQL treats the term GROUP as a synonym for ROLE. For example, the following removes some privileges from the PUBLIC group and from the READ-ONLY group: REVOKE ALL PRIVILEGES ON employee FROM public; REVOKE SELECT ON jobs FROM read-only; PostgreSQL does not support privileges on individual columns within a table or view. In order to work around this limitation, you can create a view with only the columns in question and GRANT or REVOKE privileges to and from that view. When revoking the GRANT OPTION FOR clause, you should pay extra attention to dependencies. If you revoke a user's privilege with the RESTRICT keyword, the statement will fail if other users depend on the revokee. If you revoke a user's privilege with the CASCADE keyword, the statement will revoke privileges not only for the revokee, but for all users that depend on the revokee. SQL ServerSQL Server implements the REVOKE statement as a means to undo any permission settings for a given user. This is significant because SQL Server supports an extension statement called DENY which explicity disallows a user from a specific resource. REVOKE, in SQL Server, can be used to undo permissions granted to a user with GRANT. If you want to explicitly prevent a user from having a certain privilege, you must use the DENY statement. SQL Server does not support the HIERARCHY OPTION and ADMIN OPTION clauses of the ANSI standard. Although the ADMIN OPTION clause is not supported, a couple of administrative privileges (CREATE and BACKUP) are supported in SQL Server's version of REVOKE. Use the following syntax: REVOKE [GRANT OPTION FOR] { [object_privilege] [,...]| [system_privilege] } [ON [object] [(column [,...])] ]| {TO | FROM} {grantee_name [,...] | role [,...] | PUBLIC | GUEST } [CASCADE] [AS {group_name | role_name} ] where:
tHe two forms of the REVOKE command, REVOKE object_privilege and REVOKE system_privilege, are mutually exclusive. Do not attempt to do both operations in a single statement. The key syntactical difference between them is that you should not include the ON clause when revoking system privileges. For example, to drop a system privilege: REVOKE CREATE DATABASE, BACKUP DATABASE FROM dylan, katie If commands were granted to a user WITH GRANT OPTION enabled, the privilege should be revoked using both WITH GRANT OPTION and CASCADE together. For example: REVOKE GRANT OPTION FOR SELECT, INSERT, UPDATE, DELETE ON titles TO editors CASCADE GO REVOKE can only be used in the current database. Consequently, the CURRENT_USER and CURRENT_ROLE options in the ANSI variations are always implied. REVOKE also is used to disable any DENY settings.
You must use REVOKE to remove previously granted OR denied privileges. For example, the user kelly took an extended leave of absence for maternity leave. During that time, her permissions on the employee table where denied. Now that she's returned, we'll lift the denied privileges: DENY ALL ON employee TO Kelly GO REVOKE ALL ON employee TO Kelly GO In this example, the REVOKE command did not remove her privileges; rather, it neutralized the DENY statement. See Also
The ROLLBACK statement undoes a transaction to its beginning or to a previously declared SAVEPOINT. ROLLBACK also closes any open cursors.
SQL2003 SyntaxROLLBACK [WORK] [AND [NO] CHAIN] [TO SAVEPOINT savepoint_name] Keywords
In addition to undoing a single data-manipulation operation such as an INSERT, UPDATE, or DELETE statement (or a batch of them), the ROLLBACK statement undoes transactions up to the last issued START TRANSACTION, SET TRANSACTION, or SAVEPOINT statement. Rules at a GlanceROLLBACK is used to undo a transaction. It can be used to undo explicitly declared transactions that are started with a START TRAN statement or implicitly with a transaction-initiating statement. It can also be used to undo implicit transactions that are started without a START TRAN statement. ROLLBACK is mutually exclusive of the COMMIT statement. Most people associate commands like INSERT, UPDATE, and DELETE with the term "transaction." However, transactions encompass a wide variety of commands. This list of commands varies from platform to platform, but generally includes any command that alters data or database structures and is logged to the database logging mechanism. According to the ANSI standard, all SQL statements can be undone with ROLLBACK. Programming Tips and GotchasThe most important gotcha to consider is that some database platforms perform automatic and implicit transations, while others require explicit transactions. If you assume a platform uses one method of transactions over the other, you may get bitten. Thus, when moving between database platforms, you should follow a standard, preset way of addressing transactions. We recommend staying with explicit transactions using SET TRAN or START TRAN to begin a transaction, and COMMIT or ROLLBACK to end a transaction. DB2DB2 supports the basic ANSI form of the statement. It does not support the [AND [NO] CHAIN] clause. (Also note that DB2 does not support the START TRANSACTION statement, so all transactions in DB2 start implicitly.) DB2's ROLLBACK syntax is as follows: ROLLBACK [WORK] [TO SAVEPOINT savepoint_name] A number of DB2 SET statements are not covered by transaction control and, thus, cannot be rolled back. These include: SET CONNECTION, SET CURRENT DEFAULT TRANSFORM GROUP, SET CURRENT DEGREE, SET CURRENT EXPLAIN MODE, SET CURRENT EXPLAIN SNAPSHOT, SET CURRENT PACKAGESET, SET CURRENT QUERY OPTIMIZATION, SET CURRENT REFRESH AGE, SET EVENT MONITOR STATE, SET PASSTHRU, SET PATH, SET SCHEMA, and SET SERVER OPTION. DB2 issues an implicit rollback any time a unit of work terminates abnormally. When a regular (non-savepoint) rollback occurs on DB2, all locks held by the unit of work are released, open cursors are closed, LOB locators are freed, and in some cases, caching is impacted. When a rollback to savepoint occurs on DB2, locks and LOB locators are preserved, and dynamically prepared SQL is still valid (though it will have to be parsed again). Rollback to savepoint involving cursors is a bit more complicated. If a cursor depends on DDL in the savepoint, it will be marked invalid. If a cursor is referenced in the savepoint but does not contain DDL used by the savepoint, the cursor will remain open and the cursor pointer will be positioned before the next row. In all other cases, the cursor remains unaffected. MySQLMySQL supports only the simplest rollback mechanism and, even then, you must declare any table you might issue a ROLLBACK against to be transaction safe. (A transaction safe table is one declared with the InnoDB or BDB property. Refer to CREATE TABLE for more information.) ROLLBACK [TO SAVEPOINT savepoint_name] MySQL allows you to issue transaction control statements like COMMIT or ROLLBACK against non-transaction safe tables, but it will simply ignore them and autocommit as usual. In the case of ROLLBACK against a non-transaction safe table, changes will not be rolled back. MySQL, by default, runs in AUTOCOMMIT mode, causing all data modifications to automatically be written to disk. You can set AUTOCOMMIT off by issuing the command SET AUTOCOMMIT=0. You can also control the autocommit behavior on a statement-by-statement basis using the BEGIN or BEGIN WORK command: BEGIN; SELECT @A:=SUM(salary) FROM employee WHERE job_type=1; BEGIN WORK; UPDATE jobs SET summmary=@A WHERE job_type=1; COMMIT; MySQL automatically issues an implicit COMMIT upon the completion of any of these statements: ALTER TABLE, BEGIN, CREATE INDEX, DROP DATABASE, DROP TABLE, RENAME TABLE, and TRUNCATE. MySQL supports partial rollbacks using savepoints with Version 4.0.14. OracleOracle supports the ANSI standard form of the ROLLBACK statement with the addition of the FORCE clause: ROLLBACK [WORK] {[TO [SAVEPOINT] savepoint_name] | [FORCE 'text']}; ROLLBACK clears all data modifications made to the current open transaction (or to a specific, existing savepoint). It also releases all locks held by the transaction, erases all savepoints, undoes all the changes of the current transaction, and ends the current transaction. ROLLBACK ... TO SAVEPOINT rolls back just that portion of the transaction after the savepoint, erases all savepoints that followed, and releases all table- and row-level locks taken after the savepoint. Refer to the SAVEPOINT Statement section for more information. Oracle's implementation closely follows the SQL standard with the exception of the FORCE option. ROLLBACK FORCE rolls back an in-doubt, distributed transaction. You must have the FORCE TRANSACTION privilege to issue a ROLLBACK...FORCE statement. FORCE cannot be used with TO [SAVEPOINT]. ROLLBACK...FORCE affects the transaction named in 'text' and not the current transaction, where 'text' must be equal to the local or global transaction ID of the transaction you want to rollback. (These transactions and their ID names are detailed in the Oracle system view DBA_2PC_PENDING.) For example, you might want to roll back your current transaction to the salary_adjustment savepoint. These two commands are equivalent: ROLLBACK WORK TO SAVEPOINT salary_adjustment; ROLLBACK TO salary_adjustment; In the following example, you roll back an in-doubt distributed transaction: ROLLBACK FORCE '45.52.67' PostgreSQLPostgreSQL supports the basic form of ROLLBACK, but it does not support savepoints. {ROLLBACK | ABORT} [WORK | TRANSACTION]; ROLLBACK clears all data modifications made to the current open transaction. It will return an error if no transaction is currently open. PostgreSQL supports both the WORK option and the TRANSACTION option. It does not support rolling back to a savepoint. PostgreSQL supports ABORT as a synonym of ROLLBACK. SQL ServerSQL Server supports both the WORK and TRAN keywords. The only difference between them is that the ROLLBACK WORK statement doesn't allow rolling back of a named transaction or to a specific savepoint. ROLLBACK { [WORK] | [TRAN[SACTION] [transaction_name | savepoint_name] ] } If ROLLBACK is issued alone without the WORK or TRAN keywords, it rolls back all current open transactions. ROLLBACK normally frees locks, but it does not free locks when rolling back to a savepoint. SQL Server allows you to name a specific transaction_name in addition to a specific savepoint_name. You may reference them explicitly, or you may use variables within Transact-SQL. ROLLBACK TRANSACTION, when issued in a trigger, undoes all data modifications, including those performed by the trigger, up to the point of the ROLLBACK statement. Nested triggers are not executed if they follow a ROLLBACK within a trigger; however, any statements within the trigger that follow the rollback are not impacted by the rollback. ROLLBACK behaves similarly to COMMIT with regard to nesting, resetting the @@TRANCOUNT system variable to zero. (Refer to the COMMIT Statement section for more information on transaction control within a SQL Server nested trigger.) Following is a Transact-SQL batch using COMMIT and ROLLBACK in Microsoft SQL Server. In the example, the code inserts a record into the sales table. If the insertion fails, the transaction is rolled back; if the insertion succeeds, the transaction is committed: BEGIN TRAN -- initializes a transaction -- the transaction itself INSERT INTO sales VALUES('7896','JR3435','Oct 28 1997',25,'Net 60','BU7832') -- some error-handling in the event of a failure IF @@ERROR <> 0 BEGIN -- raises an error in the event log and skips to the end RAISERROR 50000 'Insert of sales record failed' ROLLBACK WORK GOTO end_of_batch END -- the transaction is committed if no errors are detected COMMIT TRAN -- the GOTO label that enables the batch to skip to -- the end without committing end_of_batch: GO SAVEPOINT sales1 See Also
This command breaks a transaction into logical breakpoints. Multiple savepoints may be specified within a single transaction. The main benefit of the SAVEPOINT command is that transactions may be partially rolled back to a savepoint marker using the ROLLBACK command.
SQL2003 SyntaxSAVEPOINT savepoint_name Keywords
Some vendors allow duplicate savepoint names within a transaction, but this is not recommended by the ANSI standard. SQL2003 supports the statement RELEASE SAVEPOINT savepoint_name, enabling an existing savepoint to be eliminated. Refer to the RELEASE SAVEPOINT Statement section for more information about eliminating an existing savepoint. Rules at a GlanceSavepoints are established within the scope of the entire transaction in which they are defined. Savepoint names should be unique within their scope. Furthermore, make sure you use BEGIN and COMMIT statements prudently, because accidentally placing a BEGIN statement too early or a COMMIT statement too late can have a dramatic impact on the way transactions are written to the database. Always make sure to provide easy-to-understand names for your savepoints because you'll be referencing them later in your programs. Programming Tips and GotchasGenerally, reusing a savepoint name won't produce an error or warning. A duplicate savepoint name will render the previous savepoint with the same name useless. So be careful when naming savepoints! When a transaction is initiated, resources (namely, locks) are expended to ensure transactional consistency. Make sure that your transaction runs to completion as quickly as possible so that the locks are released for others to use. DB2DB2 supports a more elaborate version of SAVEPOINT than is specified in the ANSI standard. SAVEPOINT savepoint_name [UNIQUE] ON ROLLBACK RETAIN CURSORS [ON ROLLBACK RETAIN LOCKS] where:
An important note about DB2 is that savepoints cannot be nested. An error will occur any time you issue a savepoint where an active savepoint already exists. DB2 implicitly releases any savepoints within the transaction if it is committed. In the following example, we set the sales_processing savepoint without explicitly retaining locks: SAVEPOINT sales_processing ON ROLLBACK RETAIN CURSORS; However, since retaining locks is the default, the next example is essentially redundant: SAVEPOINT sales_processing ON ROLLBACK RETAIN CURSORS ON ROLLBACK RETAIN LOCKS; MySQLNot supported. OracleOracle fully supports the ANSI implementation. SAVEPOINT savepoint_name The following example performs several data modifications, and then rolls back to a savepoint: INSERT INTO sales VALUES('7896','JR3435','Oct 28 1997',25,'Net 60','BU7832'); SAVEPOINT after_insert; UPDATE sales SET terms = 'Net 90' WHERE sales_id = '7896'; SAVEPOINT after_update; DELETE sales; ROLLBACK TO after_insert; PostgreSQLNot supported. SQL ServerSQL Server does not support the SAVEPOINT command. Instead, it uses the SAVE command: SAVE TRAN[SACTION] savepoint_name In addition, rather than declaring the literal name of the savepoint, you can optionally reference a variable containing the name of the savepoint. If you use a variable, it must be of the CHAR, VARCHAR, NCHAR, or NVARCHAR datatype. SQL Server allows you to have many different named savepoints in a single transaction. However, be careful. Since SQL Server supports multiple savepoints in a single transaction, it might appear that SQL Server fully supports nested savepoints. In fact, it does not. Any time you issue a commit or savepoint in SQL Server, it only commits or rolls back to the last open savepoint. When the ROLLBACK TRAN savepoint_name command is executed, SQL Server rolls the transaction back to the specified savepoint, then continues processing with the next valid Transact-SQL command following the ROLLBACK statement. Finally, the transaction must ultimately be concluded with a COMMIT or a final ROLLBACK statement. See Also
The SELECT statement retrieves rows, columns, and derived values from one or many tables of a database.
SQL2003 SyntaxThe full syntax of the SELECT statement is powerful and complex, but can be broken down into these main clauses: SELECT [{ALL | DISTINCT}] select_item [AS alias] [,...] FROM [ONLY | OUTER] {table_name [[AS] alias] | view_name [[AS] alias]} [,...] [ [join_type] JOIN join_condition ] [WHERE search_condition] [ {AND | OR | NOT} search_condition [...] ] [GROUP BY group_by_expression{group_by_columns | ROLLUP group_by_columns | CUBE group_by_columns | GROUPING SETS ( grouping_set_list ) | ( ) | grouping_set , grouping_set_list} [HAVING search_condition] ] [ORDER BY {order_expression [ASC | DESC]} [,...] ] KeywordsEach of the keywords shown below, except the select_item clause, is discussed in greater detail in the "Rules at a Glance" section that follows:
The schema or owner name should be prefixed to a column when extracted from a context outside of the current user. If the table is owned by another user, then that user must be included in the column reference. For example, assume the user jake needs to access data in the schema katie: SELECT emp_id FROM katie.employee; You can use the asterisk ( * ) shorthand to retrieve all columns in every table or view shown in the FROM clause. It's a good idea to use this shortcut on single-table queries only. ALL, the default behavior, returns all records that meet the selection criteria. DISTINCT tells the database to filter out any duplicate records, thus retrieving only one instance out of many identical records.
Join_conditions are most commonly depicted in the form: JOIN table_name2 ON table_name1.column1 <comparison operator> table_name2.column2 JOIN table_name3 ON table_name1.columnA <comparison operator> table_name3.columnA [...] When the comparison_operator is the equal sign (=), a join is said to be an equijoin. However, the comparison operation could be any acceptable comparison operation like <, >, <=, >=, or even <>. Use the AND operator to issue a JOIN with multiple conditions. You can also use the OR operator to specify alternate join conditions. If an explicit join_type is omitted, then an INNER JOIN is assumed. Note that there are many types of joins, each with their own rules and behaviors, described in the "Rules at a Glance" (and in more detail in the JOIN Subclause section). Note that an alternate approach to the join condition, via the USING clause, exists:
SELECT emp_id FROM employee LEFT JOIN sales USING (emp_id, region_id); SELECT emp_id FROM employee AS e LEFT JOIN sales AS s ON e.emp_id = s.emp_id AND e.region_id = s.region_id;
WHERE [schema.[table_name.]]column operator value WHERE clauses usually compare the values contained in a column of the table. The values of the column are compared using an operator of some type (refer to Chapter 2 for more detail). For example, a column might equal (=) a given value, be greater than (>) a given value, or be BETWEEN a range of values. WHERE clauses may contain many search conditions concatenated together using the AND or OR Boolean operators. Parentheses can be used to impact the order of precedence of the search conditions. WHERE clauses can contain subqueries (refer to the SUBQUERY Substatement section).
[GROUP BY group_by_expression where group_by_expression is: { (grouping_column [,...]) | ROLLUP (grouping_column [,...]) | CUBE (grouping_column [,...]) | GROUPING SETS ( grouping_set_list ) | ( ) | grouping_set , grouping_set_list} Refer below to the subsection The GROUP BY clause in the "Rules at a Glance" section for examples and more information on ROLLUP, CUBE, and GROUPING SETS.
Rules at a GlanceEach clause of the SELECT statement has a specific use. Thus, it is possible to speak individually of the FROM clause, the WHERE clause, the GROUP BY clause, and so forth. You can get more details and examples of SELECT statements by looking up the entries for each clause of the statement. However, not every query needs every clause. At a minimum, a query needs a SELECT item list and a FROM clause. Because the SELECT clause is so important and offers so many options, we've divided "Rules at a Glance" into the following detailed subsections:
Aliases and WHERE clause joinsColumns may need to be prefixed with the database, schema, and table name, particularly when there may be the same column name in one or more tables of the query. For example, (on an Oracle database) both the jobs table and scott's employee table contain the job_id column. Note that the following examples join the employee and jobs table using the WHERE clause: SELECT scott.employee.emp_id, scott.employee.fname, scott.employee.lname, jobs.job_desc FROM scott.employee, jobs WHERE scott.employee.job_id = jobs.job_id ORDER BY scott.employee.fname, scott.employee.lname You can also use aliases to write a query more simply and easily: SELECT e.emp_id, e.fname, e.lname, j.job_desc FROM scott.employee AS e, jobs AS j WHERE e.job_id = j.job_id ORDER BY e.fname, e.lname These two queries also illustrate some important rules about WHERE clause joins:
In general, you should favor the JOIN clause (explained in a moment) over the WHERE clause for describing join expressions. This not only keeps your code cleaner, making it easy to differentiate join conditions from search conditions, it also allows you to avoid the possibility of counter-intuitive behavior that results from using the WHERE clause for outer joins in some implementations. The JOIN clauseTo perform the same query as in the previous example using an ANSI-style join, list the first table and the keyword JOIN, followed by the table to be joined. Once the second table is typed in, type the keyword ON and the join condition that would have been used in the old style query. The next example shows the query now in ANSI style. SELECT e.emp_id, e.fname, e.lname, j.job_desc FROM scott.employee AS e JOIN jobs AS j ON e.job_id = j.job_id ORDER BY e.fname, e.lname; Alternately, you could use the USING clause. Instead of describing the conditions of the join, simply provide one or more column_names (separated by commas) that appears in both of the joined tables. The database then evaluates the join based on the column (or columns) of column_name that appear in both tables. (The column names must be identical in both tables.) In the following example, the two queries produce identical results, one using the ON clause and one using the USING clause: SELECT emp_id FROM employee LEFT JOIN sales USING (emp_id, region_id); SELECT emp_id FROM employee AS e LEFT JOIN sales AS s ON e.emp_id = s.emp_id AND e.region_id = s.region_id; You can specifiy several different types of joins in ANSI style:
The WHERE clauseA poorly written WHERE clause can ruin an otherwise beautiful SELECT statement, so the nuances of the WHERE clause must be mastered thoroughly. This is an example of a typical query and a multipart WHERE clause: SELECT a.au_lname, a.au_fname, t2.title, t2.pubdate FROM authors a JOIN titleauthor t1 ON a.au_id = t1.au_id JOIN titles t2 ON t1.title_id = t2.title_id WHERE (t2.type = 'business' OR t2.type = 'popular_comp') AND t2.advance > 5500 ORDER BY t2.title In examining this query, the parentheses impact the order of processing for the search conditions. Use parentheses to move search conditions up or down in precedence just like you would in an algebra equation.
The WHERE clause offers many more specific capabilities than the preceding example illustrates. The list below references some of the more common capabilities of the WHERE clause. Refer to the WHERE Clause section for full details and examples.
WHERE emp_id = '54123'
WHERE job_id = '12' AND job_status = 'active'
WHERE job_id = '13' OR job_status = 'active'
WHERE phone LIKE '415%'
SELECT au_lname FROM authors WHERE EXISTS (SELECT last_name FROM employees)
WHERE ytd_sales BETWEEN 4000 AND 9000.
WHERE state IN (SELECT state_abbr FROM territories).
SELECT au_lname FROM authors WHERE au_lname = SOME(SELECT last_name FROM employees)
WHERE city = ALL (SELECT city FROM employees WHERE emp_id = 54123) The GROUP BY clauseThe GROUP BY clause (and the HAVING clause) is needed only in queries that utilize aggregate functions. The GROUP BY clause is used to report an aggregated value for one or more rows returned by a SELECT statement based on one or more non-aggregated columns called grouping columns. For example, here is a query that counts up how many people we hired per year from the year 1999 to 2004: SELECT hire_year, COUNT(emp_id) AS 'nbr_emps' FROM employee WHERE status = 'ACTIVE' AND hire_year BETWEEN 1999 AND 2004 GROUP BY hire_year; The results are: hire_year nbr_emps --------- -------- 1999 27 2000 17 2001 13 2002 19 2003 20 2004 32 Queries using aggregate functions provide many types of summary information. The most common aggregate functions include:
Some queries that use aggregates return a sole value. Single value aggregates are known as a scalar aggregates. Scalar aggregates do not need a GROUP BY clause. For example: --Query SELECT AVG(price) FROM titles --Results 14.77 Queries that return both regular column values and aggregate function values are commonly called vector aggregates. Vector aggregates use the GROUP BY clause and return one or many rows. There are a few rules to follow when using GROUP BY:
For example, let's suppose you need to know the total purchase amount of several purchases, with an Order_Details table that looks like this: OrderID ProductID UnitPrice Quantity ----------- ----------- ------------------- -------- 10248 11 14.0000 12 10248 42 9.8000 10 10248 72 34.8000 5 10249 14 18.6000 9 10249 51 42.4000 40 10250 41 7.7000 10 10250 51 42.4000 35 10250 65 16.8000 15 ... The following example will give you the results: SELECT OrderID, SUM(UnitPrice * Quantity) AS 'Order Amt' FROM order_details WHERE orderid IN (10248, 10249, 10250) GROUP BY orderid The results are: OrderID Order Amt ----------- ---------------- 10248 440.0000 10249 1863.4000 10250 1813.0000 We could further refine the aggregations by using more than one grouping columns. Considering the following query retrieving the average price of our products, grouped first by name and then by size: SELECT name, size, AVG(unit_price) AS 'avg' FROM product GROUP BY name, size The results are: Name Size avg ------------ ------ ----------------------- Flux Capacitor small 900 P32 Space Modulator small 1400 Transmorgrifier medium 1400 Acme Rocket large 600 Land Speeder large 6500 In addition, the GROUP BY clause supports a number of very important subclauses:
Consider the following clauses and the result sets they return, shown in Table 3-3.
Each type of GROUP BY clause returns a different set of aggregated values and, in the case of ROLLUP and CUBE, totals and subtotals. The concepts of ROLLUP, CUBE, and GROUPING SETS are much more intuitive when explained by example. In the following example, we query for data summarizing the number of sales_orders by order_year and by order_quarter: SELECT order_year AS year, order_quarter AS quarter, COUNT (*) AS orders FROM order_details WHERE order_year IN (2003, 2004) GROUP BY ROLLUP (order_year, order_quarter) ORDER BY order_year, order_quarter; The results are: year quarter orders ---- ------- ------ NULL NULL 648 -- the grand total 2003 NULL 380 -- the total for year 2003 2003 1 87 2003 2 77 2003 3 91 2003 4 125 2004 NULL 268 -- the total for year 2004 2004 1 139 2004 2 119 2004 3 10 Adding grouping columns to the query provides more details (and more subtotaling) in the result set. We'll modify the previous example by adding a region to the query (but since the number of rows increases, we'll only look at the first and second quarter): SELECT order_year AS year, order_quarter AS quarter, region, COUNT (*) AS orders FROM order_details WHERE order_year IN (2003, 2004) AND order_quarter IN (1,2) AND region IN ('USA', 'CANADA') GROUP BY ROLLUP (order_year, order_quarter) ORDER BY order_year, order_quarter; The results are: year quarter region orders ---- ------- ------ ------ NULL NULL NULL 183 -- the grand total 2003 NULL NULL 68 -- the subtotal for year 2003 2003 1 NULL 36 -- the subtotal for all regions in q1 of 2003 2003 1 CANADA 3 2003 1 USA 33 2003 2 NULL 32 -- the subtotal for all regions in q2 of 2004 2003 2 CANADA 3 2003 2 USA 29 2004 NULL NULL 115 -- the subtotal for year 2004 2004 1 NULL 57 -- the subtotal for all regions in q1 of 2004 2004 1 CANADA 11 2004 1 USA 46 2004 2 NULL 58 -- the subtotal for all regions in q2 of 2004 2004 2 CANADA 4 2004 2 USA 54 The GROUP BY CUBE clause is useful for performing multidimensional analysis on aggregated data. Like GROUP BY ROLLUP, it returns subtotals. But unlike GROUP BY ROLLUP, it returns subtotals combining all of the grouping columns named in the query. (As you will see, it also has the potential to increase the number of rows returned in the result set.) In the following example, we query for data summarizing the number of sales_orders by order_year and by order_quarter: SELECT order_year AS year, order_quarter AS quarter, COUNT (*) AS orders FROM order_details WHERE order_year IN (2003, 2004) GROUP BY CUBE (order_year, order_quarter) ORDER BY order_year, order_quarter; The results are: year quarter orders ---- ------- ------ NULL NULL 648 -- the grand total NULL 1 226 -- the subtotal for q1 of both years NULL 2 196 -- the subtotal for q2 of both years NULL 3 101 -- the subtotal for q3 of both years NULL 4 125 -- the subtotal for q4 of both years 2003 NULL 380 -- the total for year 2003 2003 1 87 2003 2 77 2003 3 91 2003 4 125 2004 NULL 268 -- the total for year 2004 2004 1 139 2004 2 119 2004 3 10 The GROUP BY GROUPING SETS clause lets you aggregate on more than one group in a single query. For each group set, the query returns subtotals with the grouping column marked as NULL. While the CUBE and ROLLUP clauses place predefined subtotals into the result set, the GROUPING SETS clause allows you to control what subtotals to add to the query. The GROUPING SETS clause does not return a grand total. Using a similar example query to the ones shown with ROLLUP and CUBE, this time we chose to subtotal by year and quarter and separately by year: SELECT order_year AS year, order_quarter AS quarter, COUNT (*) AS orders FROM order_details WHERE order_year IN (2003, 2004) GROUP BY GROUPING SETS ( (order_year, order_quarter), (order_year) ) ORDER BY order_year, order_quarter; The results are: year quarter orders ---- ------- ------ 2003 NULL 380 -- the total for year 2003 2003 1 87 2003 2 77 2003 3 91 2003 4 125 2004 NULL 268 -- the total for year 2004 2004 1 139 2004 2 119 2004 3 10 Another way to think of GROUPING SETS is to consider them to be like a UNION ALL of more than one GROUP BY query that references different parts of the same data. You can tell the database to add subtotals to a GROUPING SET by simply adding in the ROLLUP or CUBE clause according to how you would like subtotaling to occur. GROUPING SETS can also be concatenated to concisely generate large combinations of groupings. Concatenated GROUPING SETS yield the cross-product of groupings from each of the sets within a GROUPING SET list. Concatenated GROUPING SETS are compatible with CUBE and ROLLUP. Concatenated GROUPING SETS, since they perform a cross-product of all GROUPING SETS, will generate a very large number of final groupings from even a small number of concatenated groupings.
In the example in Table 3-4, the concatenated GROUPING SETS yield a large number of final groupings. You can imaging how large the result set would be if the concatenated GROUPING SETS contained a large number of groupings! However, the information returned can be very valuable and hard to reproduce. The HAVING clauseThe HAVING clause adds search conditions on the result of the GROUP BY clause. HAVING works very much like the WHERE clause, but applies to the GROUP BY clause. The HAVING clause supports all the same search conditions as the WHERE clause shown earlier. For example, using the same query as before, we now want to find only those jobs that have more than three people: --Query SELECT j.job_desc "Job Description", COUNT(e.job_id) "Nbr in Job" FROM employee e JOIN jobs j ON e.job_id = j.job_id GROUP BY j.job_desc HAVING COUNT(e.job_id) > 3 --Results Job Description Nbr in Job -------------------------------------------------- ----------- Acquisitions Manager 4 Managing Editor 4 Marketing Manager 4 Operations Manager 4 Productions Manager 4 Public Relations Manager 4 Publisher 7 Note that the ANSI standard does not require that an explicit GROUP BY clause appear with a HAVING clause. For example, the following query against the employee table is valid because it has an implied GROUP BY clause: SELECT COUNT(dept_nbr) FROM employee HAVING COUNT(dept_nbr) > 30; Although the earlier code example is valid, this application for the HAVING clause is rather rare. The ORDER BY clauseA result set can be sorted through the ORDER BY clause, in accordance with the database's sort order. Each column of the result set may be sorted in either ascending (ASC) or descending (DESC ) order. (Ascending order is the default.) If no ORDER BY clause is specified, most implementations return the data according to the physical order of data within the table or according to the order of an index utilized by the query. However, when no ORDER BY clause is specified, there is no guarantee on the order of the result set. Following is an example of a SELECT statement with an ORDER BY clause on SQL Server: SELECT e.emp_id "Emp ID", e.fname "First", e.lname "Last", j.job_desc "Job Desc" FROM employee e, jobs j WHERE e.job_id = j.job_id AND j.job_desc = 'Acquisitions Manager' ORDER BY e.fname DESC, e.lname ASC The results are: Emp ID First Last Job Desc --------- --------------- --------------- -------------------- MIR38834F Margaret Rancé Acquisitions Manager MAS70474F Margaret Smith Acquisitions Manager KJJ92907F Karla Jablonski Acquisitions Manager GHT50241M Gary Thomas Acquisitions Manager After the result set is pared down to meet the search conditions, the result set is sorted by the authors' first names in descending order. Where the authors' first names are equal, the results are sorted in ascending order by last name.
Programming Tips and GotchasOnce an alias has been assigned to a table or view in the FROM clause, use it exclusively for all other references to that table or view within the query (in the WHERE clause, for example). Do not mix references to the full table name and the alias within a single query. You should avoid mixed references for a couple of reasons. First, it is simply inconsistent and makes code maintenance more difficult. Second, some database platforms return errors on SELECT statements containing mixed references. (Refer to the SUBQUERY Substatement section for special instructions on aliasing within a subquery.) MySQL, PostgreSQL, and SQL Server support certain types of queries that do not need a FROM clause. Use these types of queries with caution since the ANSI standard requires a FROM clause. Queries without a FROM clause must be manually migrated to either the ANSI standard form or to a form that also works on the target database. Certain platforms do not support the ANSI style JOIN clause. Refer to the entry for each clause to fully investigate the varying degrees of support among the different database vendors for all the options of the SELECT command. DB2DB2 supports all of the typical clauses of the ANSI standard SELECT statement, such as WHERE, FROM, GROUP BY, HAVING, and ORDER BY. DB2 supports all of the subclauses of the GROUP BY clause, including concatenated GROUPING SETS. Plus, DB2 supplements the most common clauses with its own SELECT INTO clause and FETCH FIRST clause: SELECT { [ALL | DISTINCT] } select_item [AS alias] [,...] [INTO host_variable [,...] ] FROM [ONLY | OUTER] {table_name | view_name | TABLE ( {function_name | subquery} ) [AS alias] [,...] } [ [join type] JOIN table2 ] [WHERE search_condition] [ {AND | OR | NOT} search_condition [,...] ] [GROUP BY group_by_expression [HAVING search_condition] ] [ORDER BY order_expression [ASC | DESC] ] [FETCH FIRST {n} {ROW | ROWS} ONLY] where:
SELECT column1 FROM TABLE (curr_conv(CURRENCY, CONVERSION_RATE) ) AS data;
DB2 supports four possible join types: INNER, LEFT OUTER, RIGHT OUTER, and FULL OUTER:
SELECT j.job_id, e.lname FROM jobs j LEFT OUTER JOIN employee e ON j.job_id = e.job_id ORDER BY j.job_id
In the example above, only the first 10 rows of the employee table will be used, but the parent query might possibly return any number of rows. Also remember that the ordering in the subquery has no impact on the ordering of the final result set. MySQLMySQL's implementation of SELECT includes partial JOIN support, the INTO clause, the LIMIT clause, and the PROCEDURE clause. MySQL does not support subqueries prior to Version 4.0. SELECT [DISTINCT | DISTINCTROW | ALL] [STRAIGHT_JOIN][ {SQL_SMALL_RESULT | SQL_BIG_RESULT} ][SQL_BUFFER_RESULT] [HIGH_PRIORITY] select_item AS alias [,...] [INTO {OUTFILE | DUMPFILE} 'file_name' options] [FROM table_name AS alias [,...] [ { USE INDEX (index1 [,...]) | IGNORE INDEX (index1 [,...]) } ] [join type][JOIN table2] [ON join_condition] [WHERE search_condition] [GROUP BY {unsigned_integer | column_name | formula} [ASC | DESC] [,...] ] [HAVING search_condition] [ORDER BY {unsigned_integer | column_name | formula} [ASC | DESC] [,...] ] [LIMIT [[offset_position,] number_of_rows]] [PROCEDURE procedure_name (param [,...] ) ] [{FOR UPDATE | LOCK IN SHARE MODE}] ; where:
Keep a couple of rules in mind when using the INTO clause. First, the output file cannot already exist since overwrite functionality is not supported. Second, any file created by the query will be readable by everyone that can connect to the server. (When using SELECT...INTO OUTFILE, you can then turn around and use the MySQL command LOAD DATA INFILE to quickly load the data.) You can use the following options to better control the content of the output file when using SELECT...INTO OUTFILE:
The following example illustrates the use of these optional commands via a MySQL query that returns a result set in a comma-delimited output file: SELECT job_id, emp_id, lname+fname INTO OUTFILE "/tmp/employees.text" FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"' LINES TERMINATED BY "\n" FROM employee; MySQL also allows SELECT statements without a FROM clause when performing simple arithmetic. For example, the following queries are valid on MySQL: SELECT 2 + 2; SELECT 565 - 200; SELECT (365 * 2) * 52; MySQL is very fluid in the way it supports joins. You can use several different syntaxes to perform a join. For example, you can explicitly declare a join in a query using the JOIN clause, but then show the join condition in the WHERE clause. The other platforms force you to pick one method or the other and do not allow you to mix them in a single query. However, we think it's bad practice to mix methods, so our examples use ANSI JOIN syntax. MySQL supports the following types of JOIN syntax: FROM table1, table2 FROM table1 { [CROSS] JOIN table2 | STRAIGHT_JOIN table2 | INNER JOIN table2 [ {ON join_condition | USING (column_list)} ] | LEFT [OUTER] JOIN table2 {ON join_condition | USING (column_list)} | NATURAL [LEFT [OUTER]] JOIN table2 | RIGHT [OUTER] JOIN table2 [ {ON join_condition | USING (column_list)} ]| NATURAL [RIGHT [OUTER]] JOIN table2 } where:
MySQL offers an interesting alternative to the ANSI SQL standard for querying tables - the HANDLER statement. The HANDLER statement works a lot like SELECT, except that HANDLER provides very rapid data reads that circumvent the SQL query engine in MySQL. However, since the HANDLER statement is not a SQL statement, refer to the MySQL documentation for more information. OracleOracle allows a very large number of extensions to the ANSI SELECT statement. For example, since both nested tables and partitioned tables are allowed in Oracle (see CREATE TABLE), the SELECT statement allows queries to those types of structures. [WITH query_name AS (subquery) [,...] ] SELECT { {[ALL | DISTINCT]} | [UNIQUE]} [optimizer_hints] select_item [AS alias] [,...] [INTO {variable [,...] | record}] FROM {[ONLY] {[schema.][table_name| view_name | materialized_view_name]}[@database_link] [AS [OF] {SCN | TIMESTAMP} expression] | subquery [WITH {READ ONLY | CHECK OPTION [CONSTRAINT constraint_name]}] | [[VERSIONS BETWEEN {SCN | TIMESTAMP} {exp | MINVALUE AND {exp | MAXVALUE}] AS OF {SCN | TIMESTAMP} expression] | TABLE (nested_table_column) [(+)] {[PARTITION (partition_name) | SUBPARTITION (subpartition_name)]} [SAMPLE [BLOCK] [sample_percentage] [SEED (seed_value)]} [AS alias] [,...] [ [join_type] JOIN join_condition [PARTITION BY expression [,...] ] ] [WHERE search_condition] [ {AND | OR} search_condition [,...] ] [[START WITH value] CONNECT BY [PRIOR] condition] ] [GROUP BY group_by_expression [HAVING search_condition] ] [MODEL model_clause] [ORDER [SIBLINGS] BY order_expression {[ASC | DESC]} {[NULLS FIRST | NULLS LAST]} ] [FOR UPDATE [OF [schema.][table.]column] [,...] {[NOWAIT | WAIT (integer)]} ] Unless otherwise noted, the clauses shown above follow the ANSI standard. Similarly, elements of the clauses are identical to the ANSI standard unless otherwise noted. For example, Oracle's GROUP BY clause is nearly identical to the ANSI standard, including its component elements like ROLLUP, CUBE, GROUPING SETS, concatenated GROUPING SETS, and the HAVING clause.
The optional subclause VERSIONS BETWEEN is used to retrieve multiple versions of the data specified, using either an upper and lower boundary of an SCN (a number) or TIMESTAMP (a timestamp value), or by using the MINVALUE and MAXVALUE keywords. Without this clause, only one past version of the data is returned. (Oracle also provides several version query pseudocolumns for additional versioning information.) The AS OF clause, discussed earlier in this list, determines the SCN or moment in time from which the database issues the query when used with the VERSIONS clause. You cannot use flashback queries with the VERSIONS clause against temporary tables, external tables, tables in a cluster, or views.
Hierarchical queries use the LEVEL pseudocolumn to identify the root node (1), the child nodes (2), the grandchild nodes (3), and so forth. Other pseudocolumns available in hierarchical queries are CONNECT_BY_ISCYCLE and CONNECT_BY_ISLEAF. Hierarchical queries are mutually exclusive of the ORDER BY and GROUP BY clauses. Do not use those clauses in a query containing START WITH or CONNECT BY. You can order records from siblings of the same parent table by using the ORDER SIBLINGS BY clause.
Unlike some other database platforms, Oracle does not allow a SELECT statement without a FROM clause. The following query, for example, is invalid: SELECT 2 + 2; In order to work around this, Oracle has provided a special-purpose table called dual. Any time you want to write a query that does not retrieve data from a user-created table, such as to perform a calculation, use FROM dual. All of the following queries are valid: SELECT 2 + 2 FROM dual; SELECT (((52-4) * 5) * 8) FROM dual; Oracle's implementation of SELECT is quite straightforward if you want to retrieve data from a table. For example, Oracle allows the use of named queries. A named query is, in a sense, an alias to an entire query that can save you time when writing a complex multi-subquery SELECT statement. For example: WITH pub_costs AS (SELECT pub_id, SUM(job_lvl) dept_total FROM employees e GROUP BY pub_id), avg_costs AS (SELECT SUM(dept_total)/COUNT(*) avg FROM employee) SELECT * FROM pub_costs WHERE dept_total > (SELECT avg FROM avg_cost) ORDER BY department_name; In this example, we create two named subqueries - pub_costs and avg_costs - which are later referenced in the main query. The named queries are effectively the same as subqueries; however, subqueries must be written out in their entirety each time they're used while named queries need not. Oracle allows you to select rows from a single partition of a partitioned table (using the PARTITION clause) or to retrieve only a statistical sampling of the rows, as a percentage of rows or blocks, of a results set using SAMPLE. For example: SELECT * FROM sales PARTITION (sales_2004_q3) sales WHERE sales.qty > 1000; SELECT * FROM sales SAMPLE (12); Flashback queries are a feature of Oracle that enable retrieval of point-in-time result sets. For example, you could find out what everyone's salary was yesterday before a big change was applied to the database: SELECT job_lvl, lname, fname FROM employee AS OF TIMESTAMP (SYSTIMESTAMP - INTERVAL '1' DAY); Another interesting Oracle extension of the standard query format is the hierarchic query. Hierarchic queries return the results of queries against hierarchically designed tables in the order you define. For example, the following query returns the names of the employees, their position in the hierarchy (represented by the position in the org_char column), the employee ID, the manager ID, and their job ID: SELECT LPAD(' ',2*(LEVEL-1)) || lname AS org_chart, emp_id, mgr_id, job_id FROM employee START WITH job_id = 'Chief Executive Officer' CONNECT BY PRIOR emp_id = mgr_id; ORG_CHART EMPLOYEE_ID MANAGER_ID JOB_ID -------------- ----------- ---------- ------------------------ Cramer 101 100 Chief Executive Officer Devon 108 101 Business Operations Mgr Thomas 109 108 Acquisitions Manager Koskitalo 110 108 Productions Manager Tonini 111 108 Operations Manager Whalen 200 101 Admin Assistant Chang 203 101 Chief Financial Officer Gietz 206 203 Comptroller Buchanan 102 101 VP Sales Callahan 103 102 Marketing Manager In the previous query, the CONNECT BY clause defines the hierarchical relationship of the emp_id value as the parent row equal to the mgr_id value in the child row, while the START WITH clause specifies where in the hierarchy the result set should begin. Oracle supports the following types of JOIN syntax (refer to the JOIN Subclause section for more details): FROM table1 { CROSS JOIN table2 | INNER JOIN table2 [ {ON join_condition | USING (column_list)} ] | LEFT [OUTER] JOIN table2 [ {ON join_condition | USING (column_list)} ] | NATURAL [LEFT [OUTER]] JOIN table2 | RIGHT [OUTER] JOIN table2 [ {ON join_condition | USING (column_list)} ]| NATURAL [RIGHT [OUTER]] JOIN table2 FULL [OUTER] JOIN table2 }
SELECT j.job_id, e.lname FROM jobs j LEFT OUTER JOIN employee e ON j.job_id = e.job_id ORDER BY d.job_id
SELECT column1 FROM foo LEFT JOIN poo USING (column1, column2); SELECT column1 FROM foo LEFT JOIN poo ON foo.column1 = poo.column1 AND foo.column2 = poo.column2; Note that older Oracle syntax for joins centered around the WHERE clause and outer joins were described using a (+) marker. ANSI join syntax only became available with Oracle 9i Release 1. Therefore, you will often see existing code using the old syntax. For example, the following query is semantically equivalent to the earlier example of a left outer join. SELECT j.job_id, e.lname FROM jobs j, employee e WHERE j.job_id = e.job_id (+) ORDER BY d.job_id This older syntax is sometimes problematic and more difficult to read. You are strongly advised to use the ANSI standard syntax. Partitioned outer joinsPartitioned outer joins are useful for retrieving sparse data that might otherwise not be easily seen in a result set. (The ANSI standard describes partitioned outer joins, but Oracle is the first to support them.) For example, our product table keeps track of all products we produce, while the manufacturing table shows when we produce them. Since we're not continuously making every product at all times, the joined data between the two tables may be sparse at times: SELECT manufacturing.time_id AS time, product_name AS name, quantity AS qty FROM product PARTITION BY (product_name) RIGHT OUTER JOIN times ON (manufacturing.time_id = product.time_id) WHERE manufacturing.time_id BETWEEN TO_DATE('01/10/05', 'DD/MM/YY') AND TO_DATE('06/10/05', 'DD/MM/YY') ORDER BY 2, 1; returns the following: time name qty --------- ---------- ---------- 01-OCT-05 flux capacitor 10 02-OCT-05 flux capacitor 03-OCT-05 flux capacitor 04-OCT-05 flux capacitor 05-OCT-05 flux capacitor 06-OCT-05 flux capacitor 10 06-OCT-05 flux capacitor 8 01-OCT-05 transmorgrifier 10 01-OCT-05 transmorgrifier 15 02-OCT-05 transmorgrifier 03-OCT-05 transmorgrifier 04-OCT-05 transmorgrifier 10 04-OCT-05 transmorgrifier 11 05-OCT-05 transmorgrifier 06-OCT-05 transmorgrifier The example query and result set show that partitioned outer joins are useful for retrieving result sets that might otherwise be hard to query due to sparse data. Flashback queriesOracle 10g also supports flashback queries - queries that keep track of previous values of the result requested by the SELECT statement. In the following set of example code, we'll issue a regular query on a table, change the values in the table with an UPDATE statement, and then query the flashback version of the data. First, the regular query: SELECT salary FROM employees WHERE last_name = 'McCreary'; The results are: SALARY ---------- 3800 Now, we'll change the value in the employees table and query the table to confirm the current value: UPDATE employees SET salary = 4000 WHERE last_name = 'McCreary '; SELECT salary FROM employees WHERE last_name = 'McCreary '; The results are: SALARY ---------- 4000 Finally, we'll perform a flashback query to see what the salary value was in the past: SELECT salary FROM employees AS OF TIMESTAMP (SYSTIMESTAMP - INTERVAL '1' DAY) WHERE last_name = 'McCreary'; The results are: SALARY ---------- 3800 If we wanted to be more elaborate, we could find out all of the values of salary for a given time period, say, the last two days: SELECT salary FROM employees VERSIONS BETWEEN TIMESTAMP SYSTIMESTAMP - INTERVAL '1' MINUTE AND SYSTIMESTAMP - INTERVAL '2' DAY WHERE last_name = 'McCreary'; The results are: SALARY ---------- 4000 3800 The MODEL clauseOracle Database 10g features a powerful new clause, called MODEL, which enables spreadsheet-like result sets from a SELECT statement. The MODEL clause, in particular, is designed to alleviate the need for developers to extract data from the database and put it into a spreadsheet, like Microsoft Excel, for further manipulation. It creates a multidimensional array in which cells can be referenced by dimension values. For instance, you might dimension an array on product and time, specifying column values that you wish to access via combinations of those two dimensions. You can then write rules that are similar in concept to spreadsheet formulas, that are executed in order to change values in your model, or that create new values, and perhaps even new rows, in your model. Syntactically, the MODEL clause appears after the GROUP BY and HAVING clauses and before the ORDER BY clause. The earlier syntax diagram for Oracle's SELECT statement shows the position of the clause, and the syntax details are presented here: MODEL model_clause model_clause ::= [options] [return] [reference_models] main_model options ::= [{IGNORE|KEEP} NAV] [UNIQUE {DIMENSION|SINGLE REFERENCE}] return ::= RETURN {UPDATED|ALL} ROWS reference_models ::= reference_model [reference_model...] reference_model ::= REFERENCE model_name ON (subquery) model_columns [options] model_columns ::= [partition [alias]] DIMENSION BY (column[, column...]) MEASURES (column[, column...]) partition ::= PARTITION BY (column[, column...]) column ::= {column_name|expression} [AS alias] main_model ::= [MAIN model_name] model_columns [options] rules rules ::= [RULES [UPSERT|UPDATE] [{AUTOMATIC|SEQUENTIAL} ORDER]] [ITERATE (count) [UNTIL (termination_condition)]] (rule[, rule...]) rule ::= [UPSERT|UPDATE] cell_reference [ordering] = expression cell_reference ::= measure[multi_column_for_loop|dimension_indexes] multi_column_for_loop ::= FOR (dimension[, dimension...]) IN (dimension_values) dimension_values ::= ({subquery|dimension_value[, dimension_value...]}) dimension_value ::= (literal[, literal...]) dimension_indexes ::= index[, index...] index ::= {condition|expression|single_column_for_loop} single_column_for_loop ::= FOR dimension {in_for_options|like_for_options} in_for_options ::= IN ({subquery|literal[, literal...]}) like_for_options ::= [LIKE pattern] FROM start_literal TO end_literal {INCREMENT|DECREMENT} diff_literal ordering ::= ORDER BY (order_column[, order_column...]) order_column ::= {expression|alias} [ASC|DESC] [NULLS FIRST|NULLS_LAST] The parameters of this MODEL clause are as follows:
Following is a list of functions that have been designed specifically for use in the MODEL clause:
The following example demonstrates that the MODEL clause gives a normal SELECT statement the ability to construct a multidimensional array as a result set and calculate inter-row and inter-array values interdependently. The newly calculated values are returned as part of the SELECT statement's result set: ELECT SUBSTR(region,1,20) country, SUBSTR(product,1,15) product, year, sales FROM sales_view WHERE region IN ('USA','UK') MODEL RETURN UPDATED ROWS PARTITION BY (region) DIMENSION BY (product, year) MEASURES (sale sales) RULES ( sales['Bounce',2006] = sales['Bounce',2005] + sales['Bounce',2004], sales['Y Box', 2006] = sales['Y Box', 2005], sales['2_Products',2006] = sales['Bounce',2006] + sales['Y Box',2006] ) ORDER BY region, product, year; In this example, a query against the SALES_VIEW materialized view returns the sum of sales over the course of a few years for the regions USA and UK. The MODEL clause then falls between the WHERE clause and the ORDER BY clause. Since SALES_VIEW currently holds data for the years 2004 and 2005, we provide it rules to calculate figures for the year 2006. The subclause RETURN UPDATED ROWS, limits the result set to the rows that were created or updated by the query. Next, the example defines the logical divisions of the data using data elements from the materialized view and using the PARTITION BY, DIMENSION BY, and MEASURES subclauses. The RULES subclause then references individual measures of the model by referring to combinations of different dimension values much like a spreadsheet macro references worksheet cells with specific lookups and references to ranges of values. PostgreSQLPostgreSQL supports a straightforward implementation of the SELECT statement. It supports JOIN and subquery applications. PostgreSQL also allows the creation of new temporary or permanent tables using the SELECT...INTO syntax. SELECT [ALL | DISTINCT [ON (select_item [,...]) ] ] [AS alias [(alias_list)] ] [,...] [INTO [[TEMP]ORARY] [TABLE] new_table_name] [FROM [ONLY] table1[.*] [,...] ] [ [join type]JOIN table2 {[ON join_condition] | [USING (column_list)]} [WHERE search_condition] [GROUP BY group_by_expression] [HAVING having_condition] [ORDER BY order_by_expression [{ASC | DESC | USING operator][,...] }] [FOR UPDATE [OF column [,...]] [LIMIT {count | ALL} ][OFFSET [number_of_records]] ] where:
SELECT 8 * 40; PostgreSQL will also include an implicit FROM on SELECT statements that include schema-identified columns. For example, the following query is acceptable (though not recommended): SELECT sales.stor_id WHERE sales.stor_id = '6380';
SELECT stor_id, ord_date, qty AS quantity FROM sales ORDER BY stor_id, ord_date DESC, qty ASC; SELECT stor_id, ord_date, qty FROM sales ORDER BY 1, 2 DESC, quantity; In single table SELECT statements, you may also order by columns of the table that do not appear in the select_item list. For example: SELECT * FROM sales ORDER BY stor_id, qty; ASC and DESC are ANSI standard. If not specified, ASC is the default. PostgreSQL sorts NULL values as higher than any other value, causing NULL values to appear at the end of ASC sorts and at the beginning of DESC sorts.
PostgreSQL supports a handy variation of the DISTINCT clause - DISTINCT ON (select_item [,...] ). This variation allows you to pick and choose the exact columns that are considered for elimination of duplications. PostgreSQL chooses the result set in a manner much like it does for ORDER BY. You should include an ORDER BY clause so that there's no unpredictability as to which record is returned. For example: SELECT DISTINCT ON (stor_id), ord_date, qty FROM sales ORDER BY stor_id, ord_date DESC; The above query retrieves the most recent sales report for each store based on the most recent order date. However, there would be no way to predict what single record would have been returned without the ORDER BY clause. PostgreSQL supports only these types of JOIN syntax (refer to the JOIN Subclause section for more details): FROM table1 [,...] { CROSS JOIN table2 | [INNER] JOIN table2 [ {ON join_condition | USING (column_list)} ] | LEFT [OUTER] JOIN table2 [ {ON join_condition | USING (column_list)} ] | NATURAL [LEFT [OUTER]] JOIN table2 | RIGHT [OUTER] JOIN table2 [ {ON join_condition | USING (column_list)} ]| NATURAL [RIGHT [OUTER]] JOIN table2 FULL [OUTER] JOIN table2 } where:
SELECT j.job_id, e.lname FROM jobs j LEFT OUTER JOIN employee e ON j.job_id = e.job_id ORDER BY d.job_id
SQL ServerSQL Server supports most of the basic elements of the ANSI SELECT statement, including all of the various join types. SQL Server offers several variations on the SELECT statement, including optimizer hints, the INTO clause, the TOP clause, GROUP BY variations, COMPUTE, and WITH OPTIONS. SELECT {[ALL | DISTINCT] | [TOP number [PERCENT]
[WITH TIES]]} select_item [AS alias]
[INTO new_table_name ]
[FROM {[rowset_function | table1 [,...]} [AS alias]]
[ [join type]JOIN table2 {[ON join_condition] ]
[WHERE search_condition ]
[GROUP BY {grouping_column [,...]| ALL}] [ WITH { CUBE | ROLLUP } ]
[HAVING search_condition ]
[ORDER BY order_by_expression [ ASC | DESC ] ]
[COMPUTE {aggregation (expression)} [,...]
[BY expression [,...] ] ]
[FOR {BROWSE | XML { RAW | AUTO | EXPLICIT}
[, XMLDATA][, ELEMENTS][, BINARY base64] ]
[OPTION ( <hint> [,...]) ] where:
Examples are shown below. (Please see the SQL Server documentation for the full description of the available FROM {[rowset_function | table1 [,...] ]} options). Among the many possibilities, SQL Server currently supports the following rowset_functions:
GROUP BY ALL tells SQL Server to provide group_by categories even when the matching aggregation is NULL. Normally, SQL Server does not return a category whose aggregation is NULL. It must be used only in conjunction with a WHERE clause. WITH {CUBE | ROLLUP} tells SQL Server to perform additional higher-level aggregates of the summary categories. In the simplest terms, ROLLUP produces subtotals for the categories, while CUBE produces cross-tabulated totals for the categories.
USE pubs SELECT royalty, SUM(advance) 'total advance', GROUPING(royalty) 'grp' FROM titles GROUP BY royalty WITH ROLLUP The result set shows two NULL values under royalty. The first NULL represents the group of null values from this column in the table. The second NULL is in the summary row added by the ROLLUP operation. The summary row shows the total advance amounts for all royalty groups and is indicated by 1 in the grp column. Here is the result set: royalty total advance grp --------- --------------------- --- NULL NULL 0 10 57000.0000 0 12 2275.0000 0 14 4000.0000 0 16 7000.0000 0 24 25125.0000 0 NULL 95400.0000 1
COMPUTE, in any form, does not work with the DISTINCT keyword or with TEXT, NTEXT, or IMAGE datatypes.
Here's an example of SQL Server's SELECT...INTO capability. This example creates a table called non_mgr_employees using SELECT...INTO. The table contains the emp_id, first name, and last name of each non-manager from the employee table, joined with their job description taken from the jobs table: --QUERY SELECT e.emp_id, e.fname, e.lname, SUBSTRING(j.job_desc,1,30) AS job_desc INTO non_mgr_employee FROM employee e JOIN jobs AS j ON e.job_id = j.job_id WHERE j.job_desc NOT LIKE '%MANAG%' ORDER BY 2,3,1 The newly created and loaded table non_mgr_employee now can be queried like any other table.
Many of SQL Server's extensions to the ANSI SELECT statement involve the GROUP BY clause. For example, the GROUP BY ALL clause causes the aggregation to include NULL valued results to be included when they would normally be excluded. The following two queries are essentially the same except for the ALL keyword, yet they produce very different result sets: -- standard GROUP BY SELECT type, AVG(price) AS price FROM titles WHERE royalty <= 10 GROUP BY type ORDER BY type -- Results type price ------------ ------- business 17.3100 mod_cook 19.9900 popular_comp 20.0000 psychology 13.5040 trad_cook 17.9700 Compared to: -- Using GROUP BY ALL SELECT type, AVG(price) AS price FROM titles WHERE royalty = 10 GROUP BY ALL type ORDER BY type -- Results type price ------------ ------- business 17.3100 mod_cook 19.9900 popular_comp 20.0000 psychology 13.5040 trad_cook 17.9700 UNDECIDED NULL COMPUTE has a number of permutations that can impact the result set retrieved by the query. The following example shows the sum of book prices broken out by type of book and sorted by type and then price: --Query SELECT type, price FROM titles WHERE type IN ('business','psychology') AND price > 10 ORDER BY type, price COMPUTE SUM(price) BY type --Results type price ------------ --------------------- business 11.9500 business 19.9900 business 19.9900 sum ===================== 51.9300 type price ------------ --------------------- psychology 10.9500 psychology 19.9900 psychology 21.5900 sum ===================== 52.5300 The COMPUTE clause behaves differently if you do not include BY. The following query retrieves the grand total of prices and advances for books over $16.00: --Query SELECT type, price, advance FROM titles WHERE price > $16 COMPUTE SUM(price), SUM(advance) --Result type price advance ------------ --------------------- --------------------- business 19.9900 5000.0000 business 19.9900 5000.0000 mod_cook 19.9900 .0000 popular_comp 22.9500 7000.0000 popular_comp 20.0000 8000.0000 psychology 21.5900 7000.0000 psychology 19.9900 2000.0000 trad_cook 20.9500 7000.0000 sum ===================== 165.4500 sum ===================== 41000.0000 You could even use COMPUTE BY and COMPUTE in the same query to produce subtotals and grand totals. (For the sake of brevity, we'll show an example query, but not the result set.) In this example, we find the sum of prices and advances by type for business and psychology books over $16.00: SELECT type, price, advance FROM titles WHERE price > $16 AND type IN ('business','psychology') ORDER BY type, price COMPUTE SUM(price), SUM(advance) BY type COMPUTE SUM(price), SUM(advance) Don't forget that you must include the ORDER BY clause with a COMPUTE BY clause! (You do not need an ORDER BY clause with a simple COMPUTE clause without the BY keyword.) There are many permutations you can perform in a single query - multiple COMPUTE and COMPUTE BY clauses, GROUP BY with a COMPUTE clause, and even with an ORDER BY statement. It's actually fun to tinker around with the different ways you can build queries using COMPUTE and COMPUTE BY. It's not theme park fun, but what'dya want? This is a programming book! SQL Server also includes the FOR XML clause that converts the standard result set output into an XML document. This is very useful for web database applications. You can execute queries with FOR XML directly against the database or within a stored procedure. For example, we can retrieve one of our earlier example queries as an XML document: SELECT type, price, advance FROM titles WHERE price > $16 AND type IN ('business','psychology') ORDER BY type, price FOR XML AUTO The results aren't particularly pretty, but they're very usable: XML_F52E2B61-18A1-11d1-B105-00805F49916B -------------------------------------------------------------- <titles type="business " price="19.9900" advance="5000.0000"/><titles type="business " price="19.9900" advance="5000.0000"/> <titles type="psychology" price="19.9900" advance="2000.0000"/><titles type="psychology" price="21.5900" advance="7000.000 If you wanted the XML schema and/or XML elements fully tagged in the output, you could simply append the XMLDATA and ELEMENTS keywords to the FOR XML clause. The query would look like this: SELECT type, price, advance FROM titles WHERE price > $16 AND type IN ('business','psychology') ORDER BY type, price FOR XML AUTO, XMLDATA, ELEMENTS SQL Server also implements a number of other enhancements to support XML. For example, the OPENXML rowset function can be used to insert an XML document into a SQL Server table. SQL Server also includes system stored procedures that can help you prepare and manipulate XML documents. SEE ALSO
The SET statement assigns a value to a runtime variable. The variables may be platform-specific system variables or user-defined variables.
SQL2003 SyntaxSET variable = value Keywords
Rules at a GlanceVariable values are set for the duration of the session. The value assigned to the variable must match the datatype of the variable. For example, you cannot assign a string value to a variable that is declared as with a numeric datatype. The actual command to create a variable varies from platform to platform. For example, DB2, Oracle, and SQL Server use the DECLARE statement to declare a variable name and datatype, but other platforms may use other means of creating a variable. The value assigned to a variable does not have to be a literal value. It may be a dynamic value that is derived from a subquery. For example, we assign the maximum employee ID to the emp_id_var variable in the following example: DECLARE emp_id_var CHAR(5) SET emp_id_var = (SELECT MAX(emp_id) FROM employees WHERE type = 'F') In this example, an employee type of 'F' indicates that the employee is a full-time, salaried employee. Programming Tips and GotchasThe SET statement is easily transportable between the database platforms. Only Oracle uses a consistently different scheme for assigning a value to a variable. In the following example, we declare a variable on SQL Server called emp_id_var and assign a value to it: DECLARE emp_id_var CHAR(5) SET emp_id_var = '67888' Now, we will perform the same action on an Oracle server: DECLARE emp_id_var CHAR(5); emp_id_var := '67888'; DB2DB2 supports the basic form of SET, assigning values to local variables, output parameters, or special registers. It allows a single SET statement to set multiple variable values at one time. It also assigns values to the columns of a base table in a trigger. The command cannot set both types of variables in a single statement. SET variable = { value | NULL | DEFAULT } [,...] The DB2 syntax elements are as follows:
To set a single variable value: SET new_var.order_qty = 125; When setting multiple values with a single statement, the number of variables on the left side of the equal sign must equal the number of values on the right side of the equal sign. To set multiple variables in a single statement: SET new_var.order_qty = 125, new_var.discount = 4; When using the SET variable = SELECT... variation, the values returned in the result set of the SELECT statement must exactly match the variables item for item in ordinal position and datatype. If the SELECT statement does not return any rows, then null values are assigned to the variables. You can also use the SELECT INTO statement to assign multiple variable values with a single statement. MySQLSET, as a keyword in MySQL, has several uses. First, SET is a MySQL datatype that allows multiple values each separated by a comma. In addition, SET may assign a value to a user variable. That latter use is described here, and the syntax for it is: SET @variable = value [,...] When setting multiple values in a single statement, set each value separately with a comma between: SET new_var.order_qty = 125, new_var.discount = 4; In addition, MySQL allows the use of SELECT to assign values to variables in the same fashion as that described under the ANSI description of SET. However, there are some weaknesses when using the SELECT method for assigning values to variables. The primary problem is that values are not assigned immediately within the SELECT statement. Thus, for the following example: SELECT (@new_var := row_id) AS a, (@new_var + 3) AS b FROM table_name; the @new_var variable will not possess the newly selected value of row_id + 3. It will retain the value from the beginning of the statement. For this reason, it is a best practice to assign only one value to a variable at a time. OracleThe SET clause is not supported as the method of assigning values to variables. Instead, user-defined variables are simply assigned a value using the assignment indicator :=. The basic syntax is shown here: variable := value PostgreSQLThe PostgreSQL command SET is used to set a runtime variable. SET variable { TO | = } { value | DEFAULT } The runtime variable may be set to a string literal designated by value. Using the DEFAULT keyword sets the runtime variable to its default value. PostgreSQL v7.2 supports these runtime variables:
You can further qualify the SQL and Postgresql styles using the keywords as European, US, and NonEuropean, which give dates in the forms dd/mm/yyyy, mm/dd/yyyy, and mm/dd/yyyy, respectively. For example: SET DATESTYLE = SQL, European.
SELECT setseed(value);.
Here is an example for setting the date and time format to the Oracle and European style: SET DATESTYLE TO sql, European; SQL ServerSQL Server supports SET for variable assignments, provided the variables have previously been created with the DECLARE statement, and to define values for cursor variables in SQL Server. (SQL Server also uses the SET statement for a variety of other purposes, such as enabling or disabling session flags like SET NOCOUNT ON.) The platform-specific syntax is: SET { { @variable = value} | { @cursor_variable = { @cursor_variable | cursor_name | { CURSOR [ FORWARD_ONLY | SCROLL ] [ STATIC | KEYSET | DYNAMIC | FAST_FORWARD ] [ READ_ONLY | SCROLL_LOCKS | OPTIMISTIC ] [ TYPE_WARNING ] FOR select_statement [ FOR { READ ONLY | UPDATE [ OF column_name [,...] ] } ] } } } } The command does not support the DEFAULT keyword, but otherwise supports all the syntax of the ANSI command. The value for server_name must reference a connection named in a previous CONNECT statement, either as a literal or as a variable.
The SET CONNECTION statement allows users to switch between several open connections on one or more database servers.
SQL2003 SyntaxSET CONNECTION {DEFAULT | connection_name} Keywords
DescriptionThis command does not end a connection. Instead, it switches from the current connection to the connection named (making it the current connection), or from the current connection to the default connection. When switching between connections, the old connection becomes dormant (without committing any changes), while the new connection becomes active. Rules at a GlanceSET CONNECTION does not create a connection. It merely switches your connection context. Use the CONNECT command to create a new connection; use DISCONNECT to terminate a connection. Programming Tips and GotchasThe SET CONNECTION command is not frequently used, since many users connect programmatically via ODBC, JDBC, or some other connectivity method. However, on those platforms that support SET CONNECTION, the command can be very useful for rapidly changing connection properties without terminating any existing connections. DB2DB2 supports the following basic form of SET CONNECTION: SET CONNECTION server_name The DB2 command does not support the DEFAULT keyword, but is otherwise compatible with the ANSI command. The value for server_name must reference a connection created by a previous CONNECT statement. Following is a fully embedded SQL program in DB2 that shows CONNECT and SET CONNECTION: EXEC SQL CONNECT TO SQLNUTSHELL; /* Opens connection to SQLNUTSHELL. The connection will now execute statements referencing objects at SQLNUTSHELL */ EXEC SQL CONNECT TO DB2DEV; /* Opens connection to SQLNUTSHELL. The connection will now execute statements referencing objects at DB2DEV */ EXEC SQL SET CONNECTION SQLNUTSHELL; /* Now move back to the SQLNUTSHELL connection and execute statements referencing objects at SQLNUTSHELL. The connection to DB2DEV is still available, only dormant. */ MySQLNot supported. OracleNot supported. PostgreSQLNot supported. SQL ServerSQL Server supports SET CONNECTION, but only in embedded SQL, not within its ad hoc querying tool, SQL Query Analyzer. Although SQL Server supports the full SQL2003 syntax within SQL embedded in other programs, such as a C++ program, it is not used very often. Most prefer the SQL Server specific command USE instead. The platform-specific syntax is: SET CONNECTION connection_name The command does not support the DEFAULT keyword, but is otherwise the same as the ANSI command. The value for connection_name must reference a connection named in a previous CONNECT statement, either as a literal or as a variable. Here is a full TSQL program in SQL Server that shows CONNECT, DISCONNECT, and SET CONNECTION: EXEC SQL CONNECT TO chicago.pubs AS chicago1 USER sa; EXEC SQL CONNECT TO new_york.pubs AS new_york1 USER read-only; -- opens connections to the servers named "chicago" and "new_york" EXEC SQL SET CONNECTION chicago1; EXEC SQL SELECT name FROM employee INTO :name; -- sets the chicago1 connection as active and performs work within that session EXEC SQL SET CONNECTION new_york1; EXEC SQL SELECT name FROM employee INTO :name; -- sets the new_york1 connection as active and performs work within that session EXEC SQL DISCONNECT ALL; -- Terminates all sessions. You could alternately use two -- DISCONNECT commands, one for each named connection. See Also
The SET CONSTRAINT statement defines, for the current transaction, whether a deferrable constraint checks after each DML statement or when the transaction is finally committed. If the session is not currently in an open transaction, the setting applies to the next transaction.
SQL2003 SyntaxSET CONSTRAINT {constraint_name [,...] | ALL} {DEFERRED | IMMEDIATE} Keywords
Rules at a GlanceSET CONSTRAIN defines a value for the constraint mode of all deferrable constraints of the current transaction. If the session is not currently in a transaction, then the SET CONSTRAINT statement applies to the next transaction issued during the session. The following example sets all deferrable constraints to be checked immediately following the issuance of each DML statement: SET CONSTRAINT ALL IMMEDIATE; The next example sets two constraints to defer their data modification until the transaction is committed: SET CONSTRAINT scott.hr_job_title, scott.emp_bonus DEFERRED: Programming Tips and GotchasConstraints, when they are defined, may be specified as DEFERRABLE or NOT DEFERRABLE. The SET CONSTRAINT statement will fail if it is issued against a specific constraint that was defined as NOT DEFERRABLE. DB2Not supported. MySQLNot supported. OracleOracle supports the ANSI standard exactly as described, except that SET CONSTRAINT is allowed, as well as SET CONSTRAINTS. PostgreSQLPostgreSQL supports the ANSI standard syntax exactly as it is described. Currently, PostgreSQL only allows SET CONSTRAINT against foreign key constraints, not check and unique constraints, which are always considered IMMEDIATE. SQL Server
The SET PATH statement changes the value of the CURRENT PATH setting to one or more schemas.
SQL2003 SyntaxSET PATH schema_name [,...] Keywords
Rules at a GlanceSET PATH defines one or more schemas used to qualify an unqualified routine name (that is, functions, procedures, and methods). The following example sets the current path (i.e., schema name) for unqualified objects to scott: SET PATH scott; Then, whenever a routine is referenced during the current session, it will assume the scott schema if no schema is identified. Programming Tips and GotchasWhen referencing multiple schema names, all the schemas must belong to the current database. (The schemas cannot be on a remote database.) SET PATH does not apply the schema to unqualified objects like tables or views. It only applies to routines. DB2DB2 supports several variations on the ANSI standard: SET {[CURRENT] PATH | CURRENT_PATH} = {schema_name | SYSTEM PATH | USER | CURRENT PATH | CURRENT_PATH} [,...] where:
The following example sets the schema scott and the DB2 system schemas as the current path: SET PATH = scott, SYSTEM PATH MySQLNot supported. OracleNot supported. PostgreSQLNot supported. SQL ServerNot supported. See Also
The SET ROLE statement enables and disables specific roles for the current session.
SQL2003 SyntaxSET ROLE {NONE | role_name} Keywords
Rules at a GlanceWhen a user session is opened using the CONNECT statement, issuing the SET ROLE statement grants that session the privileges associated with a role. The SET ROLE command can be issued only outside of a transaction. The value for role_name must reference a valid user role existing on the server. You may specify the role name either as a literal or through a variable. Programming Tips and GotchasSessions are created using the CONNECT statement, while roles are created using the CREATE ROLE statement. Most database platforms offer some method of setting or changing the role used during a user session. SET ROLE is the ANSI standard approach to setting the role used during a user session, but it is not widely supported by the different database platforms. Check your platform to find an analogous command supported by your specific vendor. DB2Not supported. DB2 does not yet support roles. MySQLSET ROLE is not supported. An analogous method to control connection settings in MySQL is controlled in the [client] section of the .my.cnf configuration file in the home directory. For example: [client] host=server_name user=user_name password=client_password To rapidly change between user roles, you can reassign host, user, and password connection properties by assigning new values to MYSQL_HOST, USER (for Windows only), and MYSQL_PWD (though MYSQL_PWD is insecure in that other users can view this file), respectively. OracleWhen a user initiates a connection, Oracle explicitly assigns roles to the user. The role(s) under which the session is operating can be changed with the SET ROLE command, assuming the user has permissions to the role. Oracle uses the MAX_ENABLED_ROLES initialization parameter (in the INIT.ORA file) to control the maximum number of roles that can be enabled concurrently. The Oracle syntax is: SET ROLE { role_name [IDENTIFIED BY password] [,...] | [ALL [EXCEPT role_name [,...]] | NONE } ; Options for the SET ROLE command include:
Roles with passwords may be accessed only through the statement SET responsibility role_name IDENTIFIED BY password. For example, to enable the specific roles read_only and updater, identified by the passwords editor and red_marker, respectively, for the current session: SET ROLE read_only IDENTIFIED BY editor, updater IDENTIFIED BY red_marker; To enable all roles except the read_write role: SET ROLE ALL EXCEPT read_write; PostgreSQLSET ROLE is not supported. However, the ANSI SQL command SET SESSION AUTHORIZATION is supported and can achieve somewhat similar results. SQL ServerNot supported. See Also
The SET SCHEMA statement changes the value of the CURRENT SCHEMA setting to a user-specified schemas.
SQL2003 SyntaxSET SCHEMA schema_name [,...] Keywords
Rules at a GlanceSET SCHEMA defines a user-defined schema to use to qualify an unqualified object, such as a table or view. The following example sets the current schema for unqualified objects to scott: SET SCHEMA scott; Then, whenever an object is referenced during the current session, it will assume the scott schema, if no schema is identified. Programming Tips and GotchasThe SET SCHEMA statement cannot assign a schema from a remote database as the CURRENT SCHEMA. SET SCHEMA does not apply the schema to unqualified routines like functions, procedures, and methods. It only applies to database objects like tables and views. DB2DB2 supports several variations on the ANSI standard: SET [CURRENT] SCHEMA = {schema_name | USER } [,...] where:
The DB2 implementation is otherwise the same as the ANSI standard. MySQLNot supported. OracleNot supported. PostgreSQLNot supported. SQL ServerNot supported. See Also
The SET SESSION AUTHORIZATION statement sets the user identifier for the current session.
SQL2003 SyntaxSET SESSION AUTHORIZATION username Keywords
Rules at a GlanceThis command allows you to switch between users and to run under their permissions. Programming Tips and GotchasSome platforms allow you to use special shortcut keywords, like SESSION USER and CURRENT USER. SESSION USER and CURRENT USER are usually the same thing - the username of the currently active session provided by the client. However, SESSION USER and CURRENT USER can diverge in a session when SETUID functions and other similar mechanisms are invoked. Superuser permissions are required to invoke this command, but you will still be able to switch back to the initial user session even if the current user session does not normally have permission to run SET SESSION AUTHORIZATION. You might also wish to check the value of the SESSION_USER and CURRENT_USER functions with this SQL statement: SELECT SESSION_USER, CURRENT_USER; Normally, you should issue SET SESSION AUTHORIZATION before any transactions to set the session and current user values for all transactions that follow. It must be issued as the only command in its transaction batch. DB2Not supported. Similar functionality is achieved with the CONNECT statement or by logging off the server and reconnecting. MySQLNot supported. You must disconnect your session from MySQL, and then reconnect to utilize another set of user privileges. OracleNot supported. Similar functionality is achieved using the CONNECT statement or by logging off the server and reconnecting. PostgreSQLPostgreSQL supports the ANSI standard for this command. The only difference, and it is a minor one, is that the ANSI standard does not allow this command during a transaction, while PostgreSQL does not care one way or the other. SQL ServerNot supported. Similar functionality is achieved with the CONNECT statement or by logging off the server and reconnecting. See Also
The SET TIME ZONE statement changes the current session's time zone if it needs to be different from the default time zone.
SQL2003 SyntaxSET TIME ZONE {LOCAL | INTERVAL {+ | -}'00:00' [HOUR TO MINUTE]} Keywords
Rules at a GlanceThis is a relatively simple command that sets the user session time zone to that of the server (LOCAL), or sets the time zone in relation to Coordinated Universal Time (UTC) (formerly Greenwich Mean Time or GMT). Thus, an INTERVAL of 2 would advance the time zone two hours greater than UTC, while an INTERVAL of -6 would reduce the time zone by six hours from UTC to the United States central time zone. Programming Tips and GotchasLike most SET commands, SET TIME ZONE can be executed only outside of an explicit transaction. In other words, you do not need to encapsulate the command within a START or BEGIN TRAN and a COMMIT TRAN statement. DB2Not supported. MySQLNot supported. OracleIn Oracle9i and higher, you can use the following ALTER SESSION command to set the session time zone: ALTER SESSION SET TIME_ZONE = {'[+ | -] hh:mm' | LOCAL | DBTIMEZONE | 'region'} Use LOCAL to revert back to your session's original default time zone. Use DBTIMEZONE to set your session time zone to be the database time zone. Use 'region' to specify a time zone region name such as EST or PST. Use an offset such as '-5:00' to specify your time zone in terms of an hour and minute displacement from UTC. A displacement of '-5:00' means that your time is five hours behind UTC time (e.g., 5:00 A.M. your time is 10:00 A.M. UTC time). To retrieve a list of valid time zone region names, issue the following query: SELECT tzname FROM v$timezone_names; Both of the following commands set the time zone to Eastern Standard Time. The first command does this by specifying the appropriate displacement from UTC, while the second command specifies the time zone region name: ALTER SESSION SET TIME_ZONE = '-5:00'; ALTER SESSION SET TIME_ZONE = 'EST'; Oracle's time zone support is complex. Steven Feuerstein's Oracle PL/SQL Programming (O'Reilly), contains a good explanation of it in the chapter on datetime datatypes. PostgreSQLPostgreSQL allows a session's time value to be set to the server default by interchangeably using the LOCAL or DEFAULT clauses: SET TIME ZONE {'timezone' | LOCAL | DEFAULT }; There are some variations from the ANSI standard:
For example, 'PST8PDT' is a valid time zone for California on Linux systems, while 'Europe/Rome' is a valid time zone for Italy on Linux and other systems. If you specify an invalid time zone, the command sets the time zone to UTC. If increasing the time zone over UTC, the plus sign is optional. If an invalid time zone is specified, UTC is used as the time zone. The following example sets the PostgreSQL time zone to Pacific Standard Time: SET TIME ZONE 'PST8PDT'; Next, the time for the current session is returned to the server's default time zone: SET TIME ZONE LOCAL; SQL Server
The SET TRANSACTION statement controls many characteristics of data modification, primarily the read/write characteristics and isolation level of a transaction.
SQL2003 SyntaxSET [LOCAL] TRANSACTION [READ ONLY | READ WRITE] [ISOLATION LEVEL {READ COMMITTED | READ UNCOMMITTED | REPEATABLE READ | SERIALIZABLE} [DIAGNOSTIC SIZE int] Keywords
Rules at a GlanceWhen issued, SET TRANSACTION sets the properties of the next upcoming transaction. Because of this, SET TRANSACTION is an interim statement that should be issued after one transaction completes and before the next transaction starts. (To begin a transaction and set its characteristics at the same time, use START TRANSACTION.) More than one option may be applied with this command but only one access mode, isolation level, and diagnostic size may be specified at a time. The isolation level of a transaction specifies the degree of isolation a transaction has from other concurrently running sessions. The isolation level controls:
If you are unfamiliar with isolation levels, be sure to read the SQL2003 Syntax section under SET TRANSACTION Statement, since the explanation for isolation levels is offered only in that section. Programming Tips and GotchasThe ISOLATION LEVEL clause controls a number of behaviors and anomalies in a transaction concerning concurrent transactions, particularly the following:
Table 3-5 shows the impact of various isolation level settings on the anomalies just listed.
DB2Not supported. DB2 supports all isolation levels, but not through the SET TRANSACTION statement. Isolation levels are controlled instead through the program preparation process and in server-level settings issued by the database administrator. Although DB2 supports the ANSI isolation levels, it uses different names for them. DB2 also allows users to set the isolation for specific statements, including SELECT, DELETE, INSERT, and UPDATE. DB2 uses the following names for the various isolation levels:
DB2 also supports two statements, CHANGE ISOLATION LEVEL and SET CURRENT ISOLATION, that change the way that DB2 isolates data from other processes while a database is being accessed. MySQLMySQL allows you to set the transaction isolation level for the next individual transaction, the whole session, or globally across the server, as follows: SET [GLOBAL | SESSION] TRANSACTION ISOLATION LEVEL [READ UNCOMMITTED |READ COMMITTED | REPEATABLE READ | SERIALIZABLE] By default, MySQL sets the isolation level for the transaction that immediately follows the statement.
When omitted, MySQL defaults to the REPEATABLE READ isolation level. The SUPER privilege is required to set a GLOBAL transaction isolation level. You can also set the default isolation level via the MYSQL command line executable using the -transaction-isolation='' switch. Following is an example that sets the all subsequent threads (both user and system threads) to a serializable transaction isolation level: SET GLOBAL TRANSACTION ISOLATION LEVEL SERIALIZABLE; OracleOracle allows you to set a transaction as read-only or read-write, set the transaction isolation level, or specify a specific rollback segment for your transactions. SET TRANSACTION { [ READ ONLY | READ WRITE ] | [ ISOLATION LEVEL { READ COMMITTED | SERIALIZABLE} ] | [ USE ROLLBACK SEGMENT segment_name ] | NAME 'transaction_name'}; where:
The USE ROLLBACK SEGMENT variant can be useful for performance tuning by allowing you to direct long-running transactions to rollback segments large enough to hold them, while small transactions could be directed to small rollback segments that might be small enough to be retained in cache. The SET TRANSACTION statement should be the first statement in any SQL batch, though Oracle treats it virtually the same as the START TRANSACTION statement. So one could be substituted for the other. In the following example, the query reports from a bi-weekly process on the Chicago server while avoiding impact from any other users who might be updating or inserting records: SET TRANSACTION READ ONLY NAME 'chicago'; SELECT prod_id, ord_qty FROM sales WHERE stor_id = 5; In another case, late-night batch processing might create a huge transaction that would overflow all but the rollback segment created to support that one transaction: SET TRANSACTION USE ROLLBACK SEGMENT huge_tran_01; PostgreSQLSET TRANSACTION in PostgreSQL impacts the new transaction you are beginning only. Consequently, you may have to issue this statement before each new transaction. SET TRANSACTION ISOLATION LEVEL {READ COMMITTED | SERIALIZABLE}; where:
By default, PostgreSQL supports the READ COMMITTED transaction isolation level. You can set the default transaction isolation level for all transactions in the session by using either of the following commands: SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL { READ COMMITTED | SERIALIZABLE } SET default_transaction_isolation = { 'read committed' | 'serializable' } Of course, you can then override the isolation level of any subsequent transaction using the SET TRANSACTION statement. For example, you can set the next transaction to the serializable transaction isolation level: SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; Alternately, you could set all the transactions in an entire session to serializable: SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL SERIALIZABLE; SQL ServerSET TRANSACTION in SQL Server sets the isolation level for an entire session. All queries that follow a SET TRANSACTION statement run under the isolation level set by the statement until it is otherwise changed. SET TRANSACTION ISOLATION LEVEL { READ COMMITTED | READ UNCOMMITTED | REPEATABLE READ | SERIALIZABLE} where:
For example, the following command lowers the transaction isolation level for all SELECT statements during the session from READ COMMITTED to REPEATABLE READ: SET TRANSACTION ISOLATION LEVEL REPEATABLE READ GO See Also
The START TRANSACTION statement allows you to perform all the functions of SET TRANSACTION while also initiating a new transaction.
SQL2003 SyntaxSTART TRANSACTION [READ ONLY | READ WRITE] [ISOLATION LEVEL {READ COMMITTED | READ UNCOMMITTED | REPEATABLE READ | SERIALIZABLE} [DIAGNOSTIC SIZE int] According to the ANSI standard, the only difference between SET and START is that SET is considered outside of the current transaction, while START is considered the beginning of a new transaction. Thus, SET TRANSACTION settings apply to the next transaction, while START TRANSACTION settings apply to the current transaction While only MySQL supports the START TRANSACTION statement, three of the vendors (MySQL, PostgreSQL, and SQL Server) support a similar command, BEGIN [TRAN[SACTION]] and its synonym BEGIN [WORK]. BEGIN TRANSACTION declares an explicit transaction, but it does not set isolation levels. Rules at a GlanceThe only significant rule of the START TRANSACTION statement is that you must use this statement to control the access mode, isolation level, or diagnostic size of the current transaction. Once a new transaction starts, you must either issue new values for the setting(s) or rely on the default. Most database platforms allow you to implicitly control transactions, using what is commonly called autocommit mode. In autocommit mode, the database treats each statement as a transaction in and of itself, complete with an implicit BEGIN TRAN and COMMIT TRAN statement. The alternative to autocommit mode is to manually control each transaction explicitly. Under explicit transaction control, you declare each new transaction with the START TRANSACTION statement. A new transaction may also start implicitly any time a transaction-initiating statement is issued, such as INSERT, UPDATE, DELETE, or SELECT. The transaction is not committed or rolled back until either COMMIT or ROLLBACK statements are explicitly issued. Some platforms, namely DB2 and Oracle, do not support the explicit declaration of a new transaction using START TRANSACTION, but they do support explicitly committing, savepointing, and rolling back a transaction. Other platforms, such as MySQL, PostgreSQL, and SQL Server, allow you to both explicitly declare a transaction with START TRANSACTION and to explicitly commit, savepoint, and roll back the transaction. Programming Tips and GotchasMany of the platforms discussed in this book run in autocommit mode by default. Therefore, it is a good rule of thumb to only use explicitly declared transactions if you intend to do so for all transactions in a session. In other words, do not mix implicitly declared transactions and explicitly declared transactions in a single session. Each transaction that is explicitly declared can only be made permanent with the COMMIT statement. Similarly, any transaction that fails or needs to be discarded must be explicitly undone with the ROLLBACK statement.
It is a good idea to issue an explicit rollback or commit after one or a few statements because long-running transactions can lock up resources, thus preventing other users from accessing those resources. And don't forget to commit (or roll back)! Long-running or very large transaction batches can fill up the rollback segments or transaction logs of a database, even if those files are small. DB2Not supported. Transactions in DB2 are started implicitly. MySQLMySQL normally runs in autocommit mode, which means changes are automatically saved to disk when completed. If a change fails for any reason, it is automatically rolled back. MySQL supports START TRANSACTION, but it is merely a synonym for BEGIN. You can suspend autocommit for one or several statements using the BEGIN syntax: BEGIN [WORK] where:
Issuing the following command can disable autocommit mode for all sessions and threads: SET AUTOCOMMIT=0 Once you have disabled autocommit, the COMMIT statement is required to store any and every data modification to disk, and the ROLLBACK statement is required to undo changes made during a transaction. Disabling autocommit is only effective with "transaction-safe tables" such as InnoDB or BDB tables. Disabling autocommit on non-transaction safe tables has no effect - autocommit will still be enabled.
Transactions are stored in the binary log in a single write operation when the COMMIT statement is issued. Here's an example: BEGIN; SELECT @A := SUM(salary) FROM employee WHERE type=1; UPDATE payhistory SET summmary=@A WHERE type=1; COMMIT; Rollbacks issued against non-transactional tables fail with the error ER_WARNING_NOT_COMPLETE_ROLLBACK, but transaction safe tables will be restored as expected. OracleNot supported. Transactions in Oracle are started implicitly. Refer to SET TRANSACTION on the Oracle platform for more information about how Oracle controls individual transactions. PostgreSQLThe PostgreSQL syntax is: BEGIN [ WORK | TRANSACTION ] where:
PostgreSQL normally runs in autocommit mode where each data modification statement or query is its own transaction. PostgreSQL applies an implicit COMMIT for a transaction that completes without an error. PostgreSQL also automatically issues a ROLLBACK statement at the end of a statement that fails. The BEGIN statement allows explicit COMMIT or ROLLBACK of a transaction, which may then consist of multiple statements. Manually coded transactions are much faster in PostgreSQL than are autocommitted transactions. The SET TRANSACTION ISOLATION LEVEL should be set to SERIALIZABLE just after the BEGIN statement to bolster the transaction isolation. PostgreSQL allows many data-modification statements (INSERT, UPDATE, DELETE) within a BEGIN . . . COMMIT block. However, when the COMMIT command is issued, either all or none of the transaction takes place, depending on the success or failure of the command.
Following is an example of BEGIN TRANSACTION in PostgreSQL: BEGIN TRANSACTION; INSERT INTO jobs(job_id, job_desc, min_lvl, max_lvl) VALUES(15, 'Chief Operating Officer', 185, 135) COMMIT; SQL ServerMicrosoft SQL Server supports the BEGIN TRANSACTION statement rather than the ANSI START TRANSACTION statement. It also supports a couple of extensions that facilitate transaction backup and recovery. The Microsoft SQL Server syntax is: BEGIN TRAN[SACTION] [transaction_descriptor [WITH MARK [ 'log_descriptor' ] ] ] where:
When nesting transactions, only the outermost BEGIN . . . COMMIT or BEGIN . . . ROLLBACK pair should reference the transaction name (if it has one). In general, we recommend avoiding nested transactions. Here is a SQL Server set of INSERT statements, all performed as a single transaction: BEGIN TRANSACTION INSERT INTO sales VALUES('7896','JR3435','Oct 28 2003',25, 'Net 60','BU7832') INSERT INTO sales VALUES('7901','JR3435','Oct 28 2003',17, 'Net 30','BU7832') INSERT INTO sales VALUES('7907','JR3435','Oct 28 2003',6, 'Net 15','BU7832') COMMIT GO If, for any reason, any one of these INSERT statements had to wait for completion, they would all have to wait since they are treated as a single transaction. See Also
A subquery is a nested query. Subqueries may appear in various places within a SQL statement.
SQL supports the following types of subquery:
Scalar and vector subqueries can, on some platforms, appear as part of the expression in a SELECT list of items, WHERE clause, and HAVING clause. Nested table subqueries tend to appear in the FROM clause of SELECT statements. A correlated subquery is a subquery that is dependent upon a value in an outer query. Consequently, the inner query is executed once for every record retrieved in the outer query. Since subqueries can be nested many layers deep, a correlated subquery may reference any level in the main query higher than its own level. Different rules govern the behavior of a subquery depending on which clause it appears in. The level of support amongst the database platforms also varies. Some platforms support subqueries in all clauses mentioned earlier (SELECT, FROM, WHERE and HAVING), while others support subqueries in only one or two of the clauses. Subqueries are usually associated with the SELECT statement. Since subqueries may appear in the WHERE clause, they can be used in any SQL statement that supports a WHERE clause, including SELECT, INSERT...SELECT, DELETE, and UPDATE statements. SQL2003 SyntaxScalar, table, and nested table subqueries are represented in the generalized syntax below: SELECT column1, column2, ... (scalar subquery) FROM table1, ... (nested table subquery) AS subquery_table_name] WHERE foo = (scalar subquery) OR foo IN (table subquery) Correlated subqueries are more complex because the value of such subqueries is dependent on values retrieved in their main queries. For example: SELECT column1
FROM table1 AS t1
WHERE foo IN
(SELECT value1
FROM table2 AS t2
WHERE t2.pk_identifier = t1.fk_identifier) Note that the IN clause is for example purposes only. Any comparison operator may be used). Keywords
Rules at a GlanceSubqueries allow you to return one or more values and nest them inside a SELECT, INSERT, UPDATE, or DELETE statement, or inside another subquery. Subqueries can be used wherever expressions are allowed. Subqueries also can often be replaced with a JOIN statement. Depending on the DBMS, subqueries may perform less quickly than joins.
Subqueries may appear in a SELECT clause with an item list containing at least one item, a FROM clause for referencing one or more valid tables or views, and in WHERE and HAVING clauses. Scalar subqueries can return only a single value. Certain operators in a WHERE clause expect only one value, such as =, <, >, >=, <=, and <> (or !=). If a subquery returns more than one value against an operator that expects a single value, the entire query will fail. On the other hand, table subqueries may return multiple values, but are usable only with multivalue expressions like [NOT] IN, ANY, ALL, SOME, or [NOT] EXISTS. Nested table subqueries may appear only in the FROM clause and should be aliased by the AS clause. The result set returned by the nested table subquery, sometimes called a derived table, offers similar functionality to a view (see CREATE VIEW). Every column returned in the derived table need not be used in the query, though they can all be acted upon by the outer query. Correlated subqueries typically appear as a component of a WHERE or HAVING clause in the outer query (and, less commonly, in the SELECT item list) and are correlated through the WHERE clause of the inner query (that is, the subquery). (Correlated subqueries can also be used as nested table subqueries, though this is less common.) Be sure to include a WHERE clause in such a subquery that evaluates based on a correlating value from the outer query. (The example for a correlated query in the earlier ANSI syntax diagram illustrates this requirement.) It is important to specify a table alias, called a correlation name, using the AS clause or other alias shortcut for every table referenced in a correlated query, both in the outer and inner query. Correlation names avoid ambiguity and help the DBMS quickly resolve the tables involved in the query. All ANSI-compliant subqueries comply with the following short list of rules:
Programming Tips and GotchasFor most all vendor platforms, subqueries should not reference large object datatypes (e.g., CLOB or BLOB on Oracle and IMAGE or TEXT on SQL Server), or array datatypes (such as TABLE or CURSOR on SQL Server). The platforms all support subqueries, but not every vendor supports every type of subquery. Table 3-6 tells you whether your platform supports each type of subquery.
Subqueries are not relegated to SELECT statements only. They may also be used in INSERT, UPDATE, and DELETE statements that include a WHERE clause. Subqueries are often used for the following purposes:
ExamplesThis section shows subquery examples that are equally valid on DB2, MySQL, Oracle, PostgreSQL, and SQL Server. A simple scalar subquery is shown in the SELECT item list of the following query: SELECT job, (SELECT AVG(salary) FROM employee) AS "Avg Sal" FROM employee Nested table subqueries are functionally equivalent to querying a view. In the following, we query the education level and salary in a nested table subquery, and then perform aggregations on the values in the derived table in the outer query. SELECT AVG(edlevel), AVG(salary) FROM (SELECT edlevel, salary FROM employee) AS emprand GROUP BY edlevel
The following query shows a standard table subquery in the WHERE clause expression. In this case, we want all project numbers for employees in the department 'A00': SELECT projno FROM emp_act WHERE empno IN (SELECT empno FROM employee WHERE workdept = 'A00') The above subquery is executed only once for the outer query. In the next example, we want to know the names of employees and their level of seniority. We get this result set through a correlated subquery: SELECT firstname, lastname, (SELECT COUNT(*) FROM employee senior WHERE employee.hiredate > senior.hiredate) as senioritype FROM employee Unlike the previous subquery, this subquery is executed one time for every row retrieved by the outer query. In a query like this, the total processing time could be very long, since the inner query may potentially execute many times for a single result set. Correlated subqueries depend on values retrieved by the outer query before being able to complete the processing of the inner query. They are tricky to master, but offer unique programmatic capabilities. The following example returns information about orders where the quantity sold in each order is less than the average quantity in other sales for that title: SELECT s1.ord_num, s1.title_id, s1.qty FROM sales AS s1 WHERE s1.qty < (SELECT AVG(s2.qty) FROM sales AS s2 WHERE s2.title_id = s1.title_id) For this example, you can accomplish the same functionality using a self join. However, there are situations in which a correlated subquery may be the only easy way to do what you need. The next example shows how a correlated subquery might be used to update values in a table: UPDATE course SET ends = (SELECT min(c.begins) FROM course AS c WHERE c.begins BETWEEN course.begins AND course.ends) WHERE EXISTS (SELECT * FROM course AS c WHERE c.begins BETWEEN course.begins AND course.ends) Similarly, you can use a subquery to determine which rows to delete. This example uses a correlated subquery to delete rows from one table based on related rows in another table: DELETE FROM course WHERE EXISTS (SELECT * FROM course AS c WHERE course.id > c.id AND (course.begins BETWEEN c.begins AND c.ends OR course.ends BETWEEN c.begins AND c.ends)) DB2DB2 supports the ANSI standard subquery types. It allows scalar subqueries in the SELECT item list, nested table subqueries in the FROM clause, and scalar and vector subqueries in the expressions of WHERE and HAVING clauses. DB2 allows correlated subqueries in the SELECT item list and in WHERE and HAVING clauses. MySQLMySQL supports nested table subqueries as select items and in the WHERE clause. OracleOracle supports ANSI standard subqueries, though it uses a different nomenclature. In Oracle, a nested table subquery that appears in the FROM clause is called an inline view. That makes plenty of sense because nested table subqueries are basically views built on the fly. Oracle calls a subquery that appears in the WHERE clause and HAVING clause of a query a nested subquery. Oracle allows correlated subqueries in the SELECT item list and in the WHERE and HAVING clauses. PostgreSQLPostgreSQL supports ANSI standard subqueries in the FROM, WHERE, and HAVING clauses. However, subqueries appearing in a HAVING clause cannot include ORDER BY, FOR UPDATE, and LIMIT clauses. PostgreSQL does not currently support subqueries in the SELECT item list. SQL ServerSQL Server supports ANSI standard subqueries. Scalar subqueries can be used almost anywhere a standard expression is allowed. Subqueries in SQL Server cannot include the COMPUTE or FOR BROWSE clauses. They can include the ORDER BY clause if the TOP clause is also used. See Also
The TRUNCATE TABLE statement, a non-ANSI statement, irrevocably removes all rows from a table without logging the individual row deletes. It quickly erases all the records in a table without altering the table structure, taking little or no space in the redo logs or transaction logs. However, since a truncate operation is not logged, the TRUNCATE TABLE statement cannot be rolled back once it is issued.
Defacto Standard SyntaxOfficially, TRUNCATE TABLE is not an ANSI standard command. However, it is a commonly supported statement standard that follows this format: TRUNCATE TABLE table_name Keywords
Rules at a GlanceThe TRUNCATE TABLE statement has the same effect on a table as a DELETE statement with no WHERE clause; both erase all rows in a given table. However, there are two important differences. TRUNCATE TABLE is faster and it is non-logged, meaning it cannot be rolled back if issued in error. Plus, TRUNCATE TABLE does not activate triggers, while the DELETE statement does. This command should be issued manually. We strongly encourage you not to place it into automated scripts or production systems that contain irreplaceable data. It cannot be paired with transaction control statements such as BEGIN TRAN or COMMIT. Programming Tips and GotchasBecause the TRUNCATE TABLE statement is not logged, it is generally used only in development databases. Use it in production databases with caution!
The TRUNCATE TABLE command will fail if another user has a lock on the table at the time the statement is issued. TRUNCATE TABLE does not activate triggers, but it will work when they are present. However, it won't work when foreign key constraints are in place on a given table. DB2As of Version 8, DB2 does not support the TRUNCATE TABLE statement. MySQLMySQL supports a basic format for the TRUNCATE TABLE statement starting in Version 3.23: TRUNCATE TABLE name MySQL achieves the results of a TRUNCATE TABLE command by dropping and recreating the affected table. Since MySQL stores each table in a file called table_name.frm, the table_name.frm file must exist in the directory containing the database files for the command to work properly. For example, to remove all data from the publishers table: TRUNCATE TABLE publishers OracleOracle allows a table or an indexed cluster (but not a hash cluster) to be truncated. Oracle's syntax is: TRUNCATE { CLUSTER [owner.]cluster | TABLE [owner.]table [{PRESERVE | PURGE} MATERIALIZED VIEW LOG]} [{DROP | REUSE} STORAGE] TRUNCATE TABLE was first introduced by Oracle, and other platforms soon added support for the statement. Oracle has added more features to this statement than are commonly implemented by other vendors. Oracle's syntax elements are as follows:
For example: TRUNCATE TABLE scott.authors PRESERVE SNAPSHOT LOG REUSE STORAGE This example command erases all records in the table scott.authors. It also maintains the existing snapshot log and allows the table to keep and reuse the storage space already allocated to it. PostgreSQLPostgreSQL varies little from the defacto industry standard, but does add the optional TABLE keyword: TRUNCATE [TABLE] name The TABLE component of the command is optional. For example, the following command erases all the records in the authors table on a PostgreSQL database: TRUNCATE authors SQL ServerSQL Server supports the defacto industry standard. See Also
The UNION set operator combines the result sets of two or more queries, showing all the rows returned by each of the queries as one, single result set. UNION is in a class of keyword called a set operator. Other set operators include INTERSECT and EXCEPT/MINUS. (EXCEPT and MINUS are functually equivalent. EXCEPT is the ANSI standard.) All set operators are used to simultaneously manipulate the result sets of two or more queries; hence the term "set operators."
SQL2003 SyntaxThere are technically no limits to the number of queries that you may combine with the UNION statement. The general syntax is: <SELECT statement1> UNION [ALL | DISTINCT] <SELECT statement2> UNION [ALL | DISTINCT] ... Keywords
Rules at a GlanceThere is only one significant rule to remember when using UNION: the order, number, and datatype of columns should be the same in all queries. The datatypes do not have to be identical, but they should be compatible. For example, CHAR and VARCHAR are compatible datatypes. By default, the result set will default to the largest of two (or more) compatible datatypes, and a query that unions three CHAR columns (CHAR(5), CHAR(10), and CHAR(12)) will display the results in the CHAR(12) format with extra space padded onto the smaller column results. Programming Tips and GotchasEven though the ANSI standard calls for INTERSECT to take precedence over other set operators in a single statement, many platforms evaluate all set operators with equal precedence. Explicitly control the precedence of set operators using parentheses. Otherwise, the DBMS is likely to evaluate them from leftmost to rightmost expression. Depending on the platform, DISTINCT can incur a significant performance cost, since it often adds a second pass through the results to winnow out duplicate records. ALL can be specified in any instance where no duplicate records are expected (or where duplicate records are OK) for faster results. According to the ANSI standard, only one ORDER BY clause is allowed in the entire query. Include it at the end of the last SELECT statement. In order to avoid column and table ambiguity, be sure to alias each column for each table with the same respective alias. However, for column naming purposes, only the alias in the first query is used to each column in the SELECT...UNION query. For example: SELECT au_lname AS "lastname", au_fname AS "firstname" FROM authors UNION SELECT emp_lname AS "lastname", emp_fname AS "firstname" FROM employees ORDER BY lastname, firstname Also, while your query may have compatible datatyped columns throughout the queries in the UNION, there may be some variation in behavior across the DBMS platforms, especially with regard to varying length of the columns. For example, if the au_lname column in the first query is markedly longer than the emp_lname column in the second query, each platform may apply different rules as to which length is used. In general, though, the platforms will choose the longer (and less restrictive) column size for use in the result set. Each DBMS may apply its own rules as to which column name is used if the columns across the tables have different names. In general, the column names of the first query are used. DB2DB2 supports the UNION and UNION ALL keywords of the ANSI standard, plus the VALUES clause: <SELECT statement1> UNION [ALL] <SELECT statement2> UNION [ALL] ... [VALUES (expression1, expression2, ...)] [, (expression1, expression2, ...)] [,...] where:
Although UNION DISTINCT is not supported, UNION is the functional equivalent. The CORRESPONDING clause is not supported. The datatypes LONG VARCHAR, LONG VARGRAPHIC, BLOB, CLOB, DBCLOB, DATALINK, and structured types are not usable in a UNION (but are usable in UNION ALL). If the result set has the same column name across tables, that name is used. If, however, the tables have different column names, DB2 generates a new column name. That column is then unusable in the ORDER BY clause or the FOR UPDATE clause. If multiple set operations are used in a single query, those enclosed in parentheses are evaluated first. Then, the queries are evaluated in left-to-right order. However, all INTERSECT operations are performed before any UNION and EXCEPT operations. For example: SELECT empno FROM employee WHERE workdept LIKE 'E%' UNION SELECT empno FROM emp_act WHERE projno IN ('IF1000', 'IF2000', 'AD3110') UNION VALUES ('AA0001'), ('AB0002'), ('AC0003') The example above retrieves all employee IDs from the employee table that are in any department that starts with "E," as well as any employee IDs from the employee accounts (emp_act) table working in the projects 'IF1000', 'IF2000', and 'AD3110', and always includes employee IDs 'AA0001', 'AB0002', and 'AC0003'. MySQLNot supported OracleOracle supports the UNION and UNION ALL set operators using the basic ANSI SQL syntax: <SELECT statement1> UNION [ALL] <SELECT statement2> UNION [ALL] ... Oracle does not support the CORRESPONDING clause. UNION DISTINCT is not supported, but UNION is the functional equivalent. Oracle does not support UNION or UNION ALL on queries under the following circumstances:
If the first query in the set operation contains any expressions in the select item list, then include the AS statement to associate an alias with the column. Also, only the last query in the set operation may contain an ORDER BY clause. For example, you could find out all unique store IDs without duplicates using this query: SELECT stor_id FROM stores UNION SELECT stor_id FROM sales; PostgreSQLPostgreSQL supports the UNION and UNION ALL set operators using the basic ANSI SQL syntax: <SELECT statement1> UNION [ALL] <SELECT statement2> UNION [ALL] ... PostgreSQL does not support UNION or UNION ALL on queries with a FOR UPDATE clause. PostgreSQL does not support the CORRESPONDING clause. UNION DISTINCT is not supported, but UNION is the functional equivalent. The first query in the set operation may not contain an ORDER BY clause or a LIMIT clause. Subsequent queries in the UNION or UNION ALL set operation may contain these clauses, but such queries must be enclosed in parentheses. Otherwise, the rightmost occurrence of ORDER BY or LIMIT will be assumed to apply to the entire set operation. For example, we could find all authors and all employees whose last name starts with "P": SELECT a.au_lname FROM authors AS a WHERE a.au_lname LIKE 'P%' UNION SELECT e.lname FROM employee AS e WHERE e.lname LIKE 'W%'; SQL ServerSQL Server supports the UNION and UNION ALL set operators using the basic ANSI SQL syntax: <SELECT statement1> UNION [ALL] <SELECT statement2> UNION [ALL] ... SQL Server does not support the CORRESPONDING clause. UNION DISTINCT is not supported, but UNION is the functional equivalent. You can use SELECT...INTO with UNION or UNION ALL, but INTO may only appear in the first query of the union. Special keywords, such as SELECT TOP and GROUP BY...WITH CUBE, are usable with all queries in a union. However, be sure to include them with all queries in the union. If you use SELECT TOP or GROUP BY...WITH CUBE in only one query, the operation will fail. Each query in a union must contain the same number of columns. The datatypes of the columns do not have to be identical, but they must implicitly convert. For example, mixing VARCHAR and CHAR columns is acceptable. When returning data, SQL Server uses the larger of the two columns when evaluating the size of the columns returned in the result set. Thus, if a SELECT...UNION statement has a CHAR(5) column and a CHAR(10) column, it will display the data of both columns as a CHAR(10) column. Numeric columns are converted to and displayed as the most precise datatype in the union. For example, the following query unions the results of two independent queries that use GROUP BY...WITH CUBE: SELECT ta.au_id, COUNT(ta.au_id) FROM pubs..titleauthor AS ta JOIN pubs..authors AS a ON a.au_id = ta.au_id WHERE ta.au_id >= '722-51-5454' GROUP BY ta.au_id WITH CUBE UNION SELECT ta.au_id, COUNT(ta.au_id) FROM pubs..titleauthor AS ta JOIN pubs..authors AS a ON a.au_id = ta.au_id WHERE ta.au_id < '722-51-5454' GROUP BY ta.au_id WITH CUBE See also
The UPDATE statement changes existing data in a table. Use great caution when issuing an UPDATE statement without a WHERE clause, since the statement then affects every row in the entire table.
SQL2003 SyntaxUPDATE [ONLY] {table_name | view_name} SET {{column_name = { ARRAY [array_val [,...] ] | DEFAULT | NULL | scalar_expression}, column_name = { ARRAY | DEFAULT | NULL | scalar_expression} [,...] } | ROW = row_expression } [ WHERE search_condition | WHERE CURRENT OF cursor_name ] Keywords
Rules at a GlanceThe UPDATE statement can be used to modify the value of one or more columns of a table or view at a time. Typically, you will update the values in one or more rows at a time. The new values must be scalar (except in row expressions and arrays). That is, the new value must have a single, constant value at the time the transaction is executed, though it can be literal or derived from a function call or subquery. A basic UPDATE statement without a WHERE clause looks like this: UPDATE authors SET contract = 0 Without a WHERE clause, all authors in the authors table have their contract status set to zero (meaning they don't have a contract any more). Similarly, values can be adjusted mathematically with an UPDATE statement: UPDATE titles SET price = price * 1.1 This UPDATE statement would increase all book prices by 10%. You can set multiple values in one statement as here, where we set all author last name and first names to uppercase letters: UPDATE authors SET au_lname = UPPER(au_lname), au_fname = UPPER(au_fname); Adding a WHERE clause to an UPDATE statement allows records in the table to be modified selectively: UPDATE titles SET type = 'pers_comp', price = (price * 1.15) WHERE type = 'popular_com'; The above query makes two changes to any record of the type 'popular_com'. The command increases their price by 15% and alters their type to 'pers_comp'. You can also invoke functions or subqueries to derive the values used in an UPDATE statement. In some cases, you may wish to update the specific row that is being processed by a declared and open cursor. The following example shows both concepts: UPDATE titles SET ytd_sales = (SELECT SUM(qty) FROM sales WHERE title_id = 'TC7777') WHERE CURRENT OF title_cursor; This query assumes that you have declared and opened a cursor named title_cursor and that it is processing titles with an ID of TC7777. Sometimes you need to update values in a given table based on the values stored in another table. For example, if you need to update the publication date for all the titles written by a certain author, you might find the author and list of titles first through subqueries: UPDATE titles SET pubdate = 'Jan 01 2002' WHERE title_id IN (SELECT title_id FROM titleauthor WHERE au_id IN (SELECT au_id FROM authors WHERE au_lname = 'White')) Programming Tips and GotchasThe UPDATE statement alters values in existing records of a table or view. If you update a view, the view should include all necessary (NOT NULL) columns, or else the statement may fail. Columns with a DEFAULT value can usually be safely omitted. The SET ROW clause is not widely supported among DBMS platforms. Avoid this clause. The WHERE CURRENT OF clause is more commonly implemented on DBMS platforms, but must be used in conjunction with a cursor. Make sure you understand the use and functionality of cursors before using this clause. In an interactive user session, it is good practice to issue a SELECT statement using the same WHERE clause before issuing the actual UPDATE statement. This precaution enables you to check all rows in the result set before actually performing the UPDATE, helping ensure that you don't alter anything you don't mean to. DB2DB2 supports most of the standard structures found in the ANSI standard. It includes transaction control features using a transaction isolation level clause (the WITH clause), as well as typed table and view modification, but does not include the SET ROW clause. UPDATE [ONLY] ( {table_name | view_name} ) AS alias SET { {column_name1 = { DEFAULT | NULL | expression }, column_name2 = { DEFAULT | NULL | expression } [,...] } | { ( column_name1[..attribute_name], column_name2[..attribute_name][,...] ) = ( {DEFAULT | NULL | expression}, {DEFAULT | NULL | expression}[,...] ) | SELECT statement } [WHERE {search_conditions | CURRENT OF cursor_name}] [WITH { RR | RS | CS | UR }] where:
The UPDATE statement must have only one value assignment per column. A column value can be set only once in an UPDATE statement. If any values of the update violate a constraint on the table or otherwise cause an error, then none of the columns are updated. When updating the value of an identity column to the current identity value, just use DEFAULT for that column. (DB2 fully supports updates on structure types, but they are neither commonly used nor quick and easy to explain. Refer to the DB2 documentation for more details on updating structured types.) DB2 allows parenthetical SET clauses where multiple target columns and values are assessed at once (see example later). DB2 not only allows updates against standard tables and views, but also against typed (object-oriented) tables and views. (Refer to CREATE TABLE and CREATE TYPE for more details on these.) There are some special rules for updating against typed tables and views:
Updating the URL value of DATALINK columns is the same as deleting and then re-inserting them. Updating a DATALINK column with NULL is the same as deleting it. Updating the comment value of a DATALINK without relinking requires passing an empty string for the URL value. Here are two examples that show how DB2 updates multiple values at once using the standard SET clause: UPDATE employee SET job=NULL, salary=0, bonus=0, comm=0 WHERE workdept = 'E21' AND job <> 'MANAGER' This statement could also be written using a parenthetical SET clause, as follows: UPDATE employee SET (job, salary, bonus, comm) = (NULL, 0, 0, 0) WHERE workdept = 'E21' AND job <> 'MANAGER' Here is an example of a DB2 update against a base table on a type called circles. The circles attributes are owner, radius, X-axis center, and Y-axis center. Since the column C is defined as a type of circle, the UPDATE statement is changing the attributes of a single column: UPDATE CIRCLES SET (OWNER, C..RADIUS_LENGTH, C..X_CENTER, C..Y_CENTER) = ('tony', 5, 18, 22) WHERE ID = 15 MySQLMySQL supports the ANSI standard with a few variations, including the LOW PRIORITY clause, the IGNORE clause, and the LIMIT clause: UPDATE [LOW PRIORITY] [IGNORE] table_name SET column_name = {scalar_expression} [,...] WHERE search_conditions [ORDER BY column_name1 [{ASC | DESC}] [,...] ] [LIMIT integer] where:
MySQL supports DEFAULT value assignment in the INSERT statement, but not currently in the UPDATE statement. The following UPDATE would increase all prices in the titles table by 1: UPDATE titles SET price = price + 1; The following example limits the UPDATE to the first 10 records encountered in the titles table according to title_id. Also, the value of price is updated twice. Those two updates of price are assessed from left to right. First, the price is doubled, then it is increased by 1: UPDATE titles SET price = price * 2, price = price + 1 ORDER BY title_id LIMIT 10; OracleThe Oracle implementation of UPDATE allows updates against views, materialized views, subqueries, and tables in an allowable schema: UPDATE [ONLY] { [schema.]{view_name | materialized_view_name | table_name} [@database_link] [alias] {[PARTITION (partition_name)] | [SUBPARTITION (subpartition_name)]} | subquery [WITH {[READ ONLY] | [CHECK OPTION [CONSTRAINT constraint_name] ] | [TABLE (collection_expression_name) [ ( + ) ] ] } SET {column_name1 [,...] = {expression [,...] | subquery} | VALUE [(alias)] = { value | (subquery)}, {column_name1 [,...] = {expression [,...] | subquery} | VALUE [(alias)] = { value | (subquery)}, [,...] WHERE search_conditions | CURRENT OF cursor_name} RETURNING expression [,...] INTO variable [,...]; Syntax elements for Oracle's version of UPDATE are:
Oracle has some important rules about issuing UPDATE statements:
The following code snippets show some examples of the extensions that Oracle offers in the UPDATE statement. To begin with, assume that the sales table has grown into a large partitioned table. You can update a specific partition using the PARTITON clause, for example: UPDATE sales PARTITION (sales_yr_2004) s SET s.payterms = 'Net 60' WHERE qty > 100; You can also use the SET VALUE clause to supply a new value for each column in the row identified by the WHERE clause: UPDATE big_sales bs SET VALUE(bs) = (SELECT VALUE(s) FROM sales s WHERE bs.title_id = s.title_id) AND bs.stor_id = s.stor_id) WHERE bs.title_id = 'TC7777'; You can return values for review after an update by using the RETURNING clause. The following example updates one row, placing the updated values into PL/SQL variables: UPDATE employee SET job_id = 13, job_level = 140 WHERE last_name = 'Josephs' RETURNING last_name, job_id INTO :var1, :var2; PostgreSQLPostgreSQL supports the ANSI standard UPDATE syntax with a couple of small variations. PostgreSQL supports the addition of a FROM clause. PostgreSQL's other variations include no support for the ARRAY keyword, but support for an array functionality. PostgreSQL's UPDATE syntax is: UPDATE [ONLY] {table_name | view_name} SET column_name1 = {DEFAULT | NULL | scalar_expression }, column_name2 = {DEFAULT | NULL | scalar_expression } [,...] [FROM {table1 [AS alias], table2 [AS alias] [,...]}] [ WHERE search_condition | WHERE CURRENT OF cursor_name ] Most syntax elements are the same as in the ANSI standard. The only non-standard element is:
In the following example, we want to update job_lvl for employees who have a job_id of 12 and a min_lvl of 25. We could do this with a large, complex subquery, or we could use the FROM clause to enable us to build a join: UPDATE employee SET job_lvl = 80 FROM employee AS e, jobs AS j WHERE e.job_id = j.job_id AND j.job_id = 12 AND j.min_lvl = 25; All other aspects of the ANSI standard are fully supported. SQL ServerSQL Server supports most of the basic components of the ANSI UPDATE statement, but it does not support the ONLY and ARRAY keywords, nor does it support array update functionality. SQL Server has extended the capabilities of UPDATE by adding table hints using the WITH clause, query hints using the OPTION clause, as well as more robust variable handling, as follows: UPDATE {table_name | view_name | rowset} [WITH (hint1, hint2 [,...])] SET {column_name = {DEFAULT | NULL | scalar_expression } | variable_name = scalar_expression | variable_name = column_name = scalar_expression } [,...] [FROM {table1 | view1 | nested_table1 | rowset1} [,...]] [AS alias] [JOIN {table2 [,...]}] WHERE {conditions | CURRENT OF [GLOBAL] cursor_name} [OPTION (hint1, hint2 [,...])] SQL Server's UPDATE syntax elements are as follows:
The primary extension to the ANSI standard that Microsoft SQL Server offers in an UPDATE statement is a FROM clause. This FROM clause allows the use of the JOIN statement to make it especially easy to update rows in the target table by correlating rows declared in the FROM clause to the rows updated by the UPDATE table_name component of the statement. The following example shows an update using the ANSI style and a rather cumbersome subquery, followed by an update using SQL Server's FROM clause extension, to update the result of a table join. Both statements accomplish the same work, but in very different ways: -- ANSI style UPDATE titles SET pubdate = GETDATE( ) WHERE title_id IN (SELECT title_id FROM titleauthor WHERE au_id IN (SELECT au_id FROM authors WHERE au_lname = 'White')) -- Microsoft Transact-SQL style UPDATE titles SET pubdate = GETDATE( ) FROM authors AS a JOIN titleauthor AS t2 ON a.au_id = t2.au_id WHERE t2.title_id = titles.title_id AND a.au_lname = 'White' Performing this update using the Transact-SQL style is simply a matter of joining two tables - authors and titleauthor - to the titles table. To perform the same operation using ANSI-compliant code, the au_id in author must first be found and passed up to the titleauthors table, and then the title_id must be identified and passed up to the main update statement. The following example updates the state column for the first 10 authors from the authors table: UPDATE authors SET state = 'ZZ' FROM (SELECT TOP 10 * FROM authors ORDER BY au_lname) AS t1 WHERE authors.au_id = t1.au_id The important thing to note about this example is that it is normally difficult to update the first n records in an UPDATE statement unless there is some explicit row sequence you can identify in the WHERE clause. However, the nested table subquery in the FROM clause uses a TOP keyword to return on the first 10 records, thereby saving a lot of added programming that would otherwise be required. See Also
The WHERE clause sets the search criteria for an operation such as SELECT, UPDATE, or DELETE. Any records in the target table(s) that do not meet the search criteria are excluded from the operation. The search conditions may include many variations such as calculations, Boolean operators, and SQL predicates (for example, LIKE or BETWEEN).
SQL2003 Syntax{ WHERE search_criteria | WHERE CURRENT OF cursor_name } Keywords
Rules at a GlanceWHERE clauses are found in SELECT statements, DELETE statements, INSERT...SELECT statements, UPDATE statements, and any statement that might have a query or subquery such as DECLARE, CREATE TABLE, CREATE VIEW, and so forth. The search conditions, all of which are described in their own entries elsewhere in the book, can include:
SELECT pub_name FROM publishers WHERE city = SOME (SELECT city FROM authors);
SELECT a.au_id FROM authors AS a JOIN titleauthor AS ta ON a.au_id = ta.au_id WHERE ta.title_id IN (SELECT title_id FROM sales WHERE qty >= 75) OR (a.au_id IN (SELECT au_id FROM titleauthor WHERE royaltyper >= 60) AND a.au_id IN (SELECT au_id FROM titleauthor WHERE au_ord = 2)); See Programming Tips and Gotchas next, for more information.
SELECT au_lname, au_fname FROM authors WHERE contract = 0;
SELECT au_fname, au_lname FROM authors WHERE au_id NOT IN (SELECT au_id FROM titleauthor);
SELECT title_id, SUBSTRING(title, 1, 25) AS title FROM titles WHERE ytd_sales IS NULL;
Pattern matches (LIKE and NOT LIKE) For example, to see authors whose last names start with a "C": SELECT au_id FROM authors WHERE au_lname LIKE 'C%';
SELECT au_lname, au_fname FROM authors WHERE au_lname BETWEEN 'smith' AND 'white'; Programming Tips and GotchasThe WHERE clause may require special handling when dealing with certain datatypes, such as LOBs or certain character sets like UNICODE. Parentheses are used to control evaluation hierarchy within a WHERE clause. Encapsulating a clause within parentheses tells the DBMS to evaluate that clause before others. Parentheses can be nested to create a hierarchy of evaluations. The innermost parenthetical clause will be evaluated first. You should watch parentheses very carefully for two reasons:
For example, consider again the following query, which returns six rows in the pubs database on the SQL Server platform: SELECT DISTINCT a.au_id FROM authors AS a JOIN titleauthor AS ta ON a.au_id = ta.au_id WHERE ta.title_id IN (SELECT title_id FROM sales WHERE qty >= 75) OR (a.au_id IN (SELECT au_id FROM titleauthor WHERE royaltyper >= 60) AND a.au_id IN (SELECT au_id FROM titleauthor WHERE au_ord = 2)) The output from this query is as follows: au_id ----------- 213-46-8915 724-80-9391 899-46-2035 998-72-3567 Changing just one set of parenthesis produces different results: SELECT DISTINCT a.au_id FROM authors AS a JOIN titleauthor AS ta ON a.au_id = ta.au_id WHERE (ta.title_id IN (SELECT title_id FROM sales WHERE qty >= 75) OR a.au_id IN (SELECT au_id FROM titleauthor WHERE royaltyper >= 60)) AND a.au_id IN (SELECT au_id FROM titleauthor WHERE au_ord = 2) This time, the output will look like this: au_id ----------- 213-46-8915 724-80-9391 899-46-2035 DB2Supported as described above. MySQLSupported as described above. OracleSupported as described above. PostgreSQLSupported as described above. SQL ServerSupported as described above. See Also |
![]() |
Table of Contents |
![]() |