转: Comparison of different SQL implementations (2) [私人]

[ 2008-07-12 12:39:14 | 作者: progame ]
文字大小: | |
原文地址

LOCALTIMESTAMP

It's often important to get the value of current date and time. Below are the functions used to do that in the different implementations.

Standard The current timestamp (without time zone) is retrieved with the LOCALTIMESTAMP function which may be used as:

SELECT LOCALTIMESTAMP ...
or
SELECT LOCALTIMESTAMP(precision) ...

Note that "SELECT LOCALTIMESTAMP() ..." is illegal: If you don't care about the precision, then you must not use any parenthesis.

If the DBMS supports the non-core time zone features (feature ID F411), then it must also provide the functions CURRENT_TIMESTAMP and CURRENT_TIMESTAMP(precision) which return a value of type TIMESTAMP WITH TIME ZONE. If it doesn't support time zones, then the DBMS must not provide a CURRENT_TIMESTAMP function.

PostgreSQL Follows the standard.
DB2 Doesn't have the LOCALTIMESTAMP function.

Instead, it provides a special, magic value ('special register' in IBM language), CURRENT_TIMESTAMP (alias to 'CURRENT TIMESTAMP') which may be used as though it were a function without arguments. However, since DB2 doesn't provide TIMESTAMP WITH TIME ZONE support, the availability of CURRENT_TIMESTAMP could be said to be against the standard—at least confusing.

MSSQL Doesn't have the LOCALTIMESTAMP function.

Instead, it has CURRENT_TIMESTAMP which—however—doesn't return a value of TIMESTAMP WITH TIME ZONE, but rather a value of MSSQL's DATETIME type (which doesn't contain time zone information).

MySQL Follows the standard.
Oracle Follows the standard.

Concatenation

Standard Core feature ID E021-07:
Concatenating two strings is done with the || operator:

string1 || string2

If at least one operand is NULL, then the result is NULL.

It's unclear to me if the DBMS is allowed to try to automatically cast the operands to concatenation-compatible types.

PostgreSQL Follows the standard.

Automatically casts the concatenated values into types compatible with concatenation. If an operand is NULL then the result is NULL.

DB2 Follows the standard, partly.

Does not automatically cast concatenated values into compatible types.

MSSQL Breaks the standard by using the '+' operator instead of '||'.

Does not automatically cast operands to compatible types. If an operand is NULL, then the result is NULL.

MySQL Badly breaks the standard by redefining || to mean OR.

Offers instead a function, CONCAT(string, string), which accepts two or more arguments.

Automatically casts values into types which can be concatenated. If an operand is NULL, then the result is NULL.

Oracle Follows the standard, partly.

Automatically casts values into types which can be concatenated.

As Oracle interprets NULL as the empty string, it doesn't return NULL if an operand is NULL.

Constraint handling

The UNIQUE constraint

Standard As the constraint name indicates, a (set of) column(s) with a UNIQUE constraint may only contain unique (combinations of) values.

A column—or a set of columns—which is subject to a UNIQUE constraint must also be subject to a not NULL constraint, unless the DBMS implements an optional "NULLs allowed" feature (Feature ID 591). The optional feature adds some additional characteristics to the UNIQUE constraint:

  1. Columns involved in a UNIQUE constraint may also have NOT NULL constraints, but they do not have to.
  2. If columns with UNIQUE constraints do not also have NOT NULL constraints, then the columns may contain any number of NULL-'values'. (Logical consequence of the fact that NULL<>NULL.)
    In SQL:2003 parlance, the constraint is satisfied, if

    there are no two rows in [the relation] such that the value of each column in one row is non-null and is not distinct from the value of the corresponding column in the other row

PostgreSQL Follows the standard, including the optional NULLs allowed feature.
DB2 Follows the non-optional parts of the UNIQUE-constraint. Doesn't implement the optional NULLs allowed feature.
MSSQL Follows the standard—with a twist:

MSSQL offers the NULLs allowed feature, but allows at most one instance of a NULL-'value', if NULLs are allowed; i.e. breaks characteristic 2 in the above description of the standard.

MySQL Follows the standard, including the optional NULLs allowed feature.
Oracle Follows the standard—with a twist:.

The optional NULLs allowed feature is implemented: If the UNIQUE-constraint is imposed on a single column, then the column may contain any number of NULLs (as expected from characteristic 2 in the above description of the standard). However, if the UNIQUE-constraint is specified for multiple columns, then Oracle sees the constraint as violated if any two rows

  • contain at least one NULL in a column affected by the constraint
  • identical, non-NULL values in the rest of the columns affected by the constraint

Mixture of type and operations

Automatic key generation

It's sometimes handy to have the DBMS handle generation of keys. The DBMSes offer various means for this. Note, however, that some database authorities warn against—at least some variants of—auto-generated keys; this is a classic database discourse.

Standard The standard specifies a column attribute of:
GENERATED ... AS IDENTITY (non-core feature ID T174+T175).

When creating a table, an IDENTITY clause may be declared for certain types of columns (INTEGER being one):

CREATE TABLE tablename (
  tablename_id INTEGER GENERATED ALWAYS AS IDENTITY
  ...
)

or

CREATE TABLE tablename (
  tablename_id INTEGER GENERATED BY DEFAULT AS IDENTITY
  ...
)

The column with the IDENTITY attribute will be given values in increasing order, possibly with 'holes' (...,3,4,7,...).

A base table may at most contain one column with the IDENTITY attribute. NOT NULL is implied for an IDENTITY column. Normally, a column declared with IDENTITY will also be declared PRIMARY KEY, but it's not implied.

The examples differ in their 'ALWAYS' vs. 'BY DEFAULT' clauses:

  • When ALWAYS is specified, the user cannot specify a value for the column which means that the DBMS can guarantee successful insertion of a unique value on each table insert.
  • When BY DEFAULT is specified, the user may manually specify what value to put in the identity field of a row. The flip side is that the DBMS cannot guarantee that this will work.

The standard specifies several extended options which may be declared for a generated IDENTITY column.

PostgreSQL PostgreSQL doesn't support the standard's IDENTITY attribute.

PostgreSQL's best offering for a column with auto-generated values is to declare a column of 'type' SERIAL:

CREATE TABLE tablename (
  tablename_id SERIAL,
  ...
)

'SERIAL' is a short-hand for creating a sequence and using that sequence to create unique integers for a column. If the table is dropped, PostgreSQL seems to remember to drop the sequence which was created as a side-effect of using the SERIAL type.

As a user may manually insert or update a value in a column created as SERIAL, this comes closest to the standard's GENERATED BY DEFAULT AS IDENTITY variant. It's probably possible to use a trigger to protect a SERIAL-column, such that it will get semantics matching the standard's GENERATED ALWAYS AS IDENTITY attribute.

Another option is to add the WITH OIDS clause when creating a table. Object identifiers (OIDs) will then be added to a special oid column which is hidden by default, i.e. isn't included in SELECT * FROM ... result sets). The oid column can be revealed by explicitly adding it to the SELECT-list, and it can be referred to in WHERE clauses.

OIDs cannot be assigned by the user, so the semantics of OIDs resemble the standard's GENERATED ALWAYS AS IDENTITY attribute.

Documentation: The SERIAL and OIDs types.

DB2 Follows standard, albeit with some restrictions on how identity columns may (not) be added to an existing table, etc.

Documentation: CREATE TABLE syntax and description of identity columns.

MSSQL MSSQL offers IDENTITY as a column property, but with a different syntax (not as intuitive and with less options) than the standard's specification. An example of creating a table with an IDENTITY column:

CREATE TABLE tablename (
  tablename_id INT IDENTITY PRIMARY KEY,
  ...
)

With MSSQL's IDENTITY attribute, the user cannot manually insert/change the value, unless the user has first run
SET IDENTITY_INSERT tablename ON

I.e., MSSQL's IDENTITY type is closest to the standard's GENERATED ... ALWAYS AS IDENTITY variant.

Documentation: The IDENTITY property and SET IDENTITY_INSERT.

MySQL MySQL doesn't support the standard's IDENTITY attribute.

As an alternative, an integer column may be assigned the non-standard AUTO_INCREMENT attribute:

CREATE TABLE tablename (
  tablename_id INTEGER AUTO_INCREMENT PRIMARY KEY,
  ...
)

Columns with the AUTO_INCREMENT attribute will—under certain conditions—automatically be assigned a value of <largest value in column>+<at least 1>. Look in MySQL's documentation for the (rather extensive) details.

A table can have at most one column with the AUTO_INCREMENT attribute; that column must be indexed (it doesn't have to be a primary key, as in the example SQL above) and cannot have a DEFAULT value attribute.

It's probably not too far fetched to think of MySQL's AUTO_INCREMENT feature as this equivalence:
  MySQL:
  CREATE TABLE tablename (
    columnname INTEGER AUTO_INCREMENT PRIMARY KEY
    ...
  )

  Standard SQL:
  CREATE TABLE tablename (
    columnname INTEGER DEFAULT some_func() PRIMARY KEY
    ...
  )
where some_func() is a function which finds 1 plus the currently largest value of columnname.

The nice thing about this approach is that the automatic value insertion should never fail, even though some of the column's values might have been manually set—i.e. the combined advantages of the standard's ALWAYS and BY DEFAULT variants.

The drawback is that it might result in more house-keeping: The system may need extra table locks when performing row updates/insertions to protect against ghost updates in concurrent transactions—thus slowing down the system in case of many concurrent updates/insertions.

Oracle Oracle doesn't support the standard's IDENTITY attribute.

If you want an auto-incrementing column in Oracle, then create a sequence and use that sequence in a trigger associated to the table. Example: For the table mytable, you want the mytable_id column to be of integer type, with an auto-incrementing values:

CREATE TABLE mytable (
  mytable_id INTEGER PRIMARY KEY,
  ... -- (other columns)
);
 
CREATE SEQUENCE mytable_seq;
 
CREATE TRIGGER mytable_seq_trigger
BEFORE INSERT ON mytable FOR EACH ROW
BEGIN
  IF (:new.mytable_id IS NULL) THEN
    SELECT mytable_seq.nextval INTO :new.mytable_id
    FROM DUAL;
  END IF;
END;
/

This will create an auto-incrementing column resembling the GENERATED BY DEFAULT variant from the standard. If an column resembling the GENERATED ALWAYS variant is needed, then the trigger should be extended to raise an exception if the user tries to insert a non-NULL value, and a trigger preventing UPDATEs of the relevant column should be added.

Note: If 'nice', incrementing values aren't important, you may use Oracle's SYS_GUID function as the default for a column; that way, universally unique identifiers will be assigned if you don't indicate a value for the column in new rows.

Documentation: CREATE TRIGGER, CREATE SEQUENCE, and SYS_GUID.

Bulk operations

TRUNCATE TABLE

Often, it's useful to be able to remove all rows from a large table in a quick way. And often, DELETE isn't as quick as you'd like it to be. So several DBMSes implement a TRUNCATE operation. Typically, truncating means that deletion isn't associated with triggers which may exist for the table.

Standard The SQL standard doesn't specify a TRUNCATE operation.
PostgreSQL TRUNCATE TABLE tablename

See the documentation for variations and restrictions. Most importantly, you need to be owner of the table to be truncated (or work as a superuser). Note also the nice—but potentially dangerous—CASCADE modifier which may be useful for emptying related tables.

DB2 DB2 doesn't have a TRUNCATE TABLE command.

Instead, you may abuse the IMPORT statement. Unfortunately, you need to know which operating system the command is executed from for this to work:

  • On unix-like systems:
    IMPORT FROM /dev/null OF DEL REPLACE INTO tablename
  • On Windows:
    IMPORT FROM NUL OF DEL REPLACE INTO tablename

IMPORT cannot be abused in all contexts. E.g., when working with dynamic SQL (from Java/.NET/PHP/...—not using the db2 command line processor), you need to wrap the IMPORT command in a call to ADMIN_CMD, e.g.:
CALL ADMIN_CMD('IMPORT FROM /dev/null OF DEL REPLACE INTO tablename')

The ALTER TABLE command may also be abused to quickly empty a table, but it requires more privileges, and may cause trouble with rollforward recovery.

Documentation:

MSSQL TRUNCATE TABLE tablename

You need to have at least ALTER-permission on the table to be truncated.

MySQL TRUNCATE TABLE tablename

Note that in some cases, MySQL's truncate command is really the equivalent of a DELETE command. And its behaviour depends somewhat on which storage engine the table is managed by.

Oracle TRUNCATE TABLE tablename

Note, that the TRUNCATE TABLE implicitly commits the current transaction.

Needed privileges—Quoting from the documentation:
...the table or cluster must be in your schema or you must have DROP ANY TABLE system privilege.

Command line procedures / metadata

The following are not necessarily SQL operations, but rather a description of how different operations are performed in the command line interface provided by each product.

The shape of the command line interfaces in the commercial products is depressing. Vendors, please do something about it: Not all database developers like to use slow GUIs for technical stuff. And sometimes, DBMS work is performed over slow Internet lines which makes a decent command line interface vital.

Fortunately, a tool like HenPlus exists. It can be a pain to install, but once working, it's nice to work with.

Starting the command line interface

Standard Not defined.
PostgreSQL Run:
psql
which should be in the PATH in any sensible installation.

PostgreSQL's command line interface is very user friendly. It has command history (press arrow-up for previous commands) and a fairly well-working command completion feature.

DB2 Run:
db2 -t
(The -t argument tells the command line processor to a semicolon as statement terminator instead of the default (newline). This allows for multi-line SQL statements.)

The db2 binary may not be in your PATH or may be missing vital environment variables (which is one of the stupid parts of DB2's installation procedure: It doesn't offer to set up a proper global DB2 environment for the users on the server) and you may have to include the db2profile file (situated in the sqllib directory in the home directory of the special DB2 instance user) into your shell.
    E.g. on my Linux system, I've added the following line to my .bash_profile in order to get a shell with proper DB2 environment when logging in:
. /home/db2inst1/sqllib/db2profile

The 'utility' doesn't seem to have anything resembling useful command history or command completion. Fortunately, queries may be sent to the db2 'utility' in a non-interactive way like this:
db2 "SELECT a_column FROM a_table"
This allows you to make use of your shell's command history handling.

DB2 also has a 'utility' called db2batch which some might find at bit nicer to work with.

MSSQL The command line interface is started by running
sqlcmd

sqlcmd is not nice to work with. It's bad at formatting result sets. It doesn't have command line completion. You have to say go after your commands. A positive thing about sqlsmd: It has command history, so you may press arrow-up for previous commands in the current sqlsmd session.

In MSSQL 2000, the command line interface was started by running osql.

An alternative to osql—apart from HenPlus, mentioned above—is SQSH which should work on any modern open source operating system, except it doesn't seem to support Kerberos, so you need to log into the database using a database-account (not a Windows-account).

MySQL Run:
mysql

If you need help on the optional command line options, see the man page.

On platforms like Linux and FreeBSD (which have decent readline-capabilities), MySQL's command line interface is simply great; not much else to say. MySQL's command line interface is said to be rather poor on Windows, though.

Oracle Run:
sqlplus

sqlplus lacks command completion, and has very limited built-in command history handling.

Getting a list of databases

Standard Not specified, as far as I know. (By the way: The SQL standard doesn't have the concept of a database as a container of schemas; instead, the standard specifies that schemas are contained in a catalog.)
PostgreSQL When working in the psql command line interface: \l or \l+

Alternative (when working from the terminal, not in psql): psql --list

DB2 Offers the LIST DATABASE DIRECTORY command, but only when working in the db2 command line processor (i.e. not when working from db2batch); this command's output is human readable, but sub-optimal as machine readable format.
MSSQL EXEC SP_HELPDB
MySQL SHOW DATABASES
Oracle In Oracle, there is a one-to-one relationship between databases and instances (unless you work with a clustered Oracle system). You can get a list of instances; the way to do it depends on the operating system which Oracle is running on:
  • On unix-like systems: Look in the /etc/oratab file.
  • On Windows: Start Windows' Services management console and look for services with names starting with OracleServiceXXXX. Each XXXX is the name (AKA SID) of an instance.

Getting a list of schemas

Standard SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA
PostgreSQL In the command line interface: \dn or \dn+ (for more details).

Using SQL: Follows the standard.

Documentation:

DB2 SELECT schemaname FROM syscat.schemata
MSSQL Follows the standard.
MySQL MySQL doesn't support schemas.
Oracle Oracle has a peculiar approach to schemas: A schema exists for each and every user. And there cannot be a schema without a corresponding user. Consequently, a way to get a list of schemas in Oracle is to query the ALL_USERS dictionary view:
SELECT username FROM all_users

Getting a list of tables

Standard Part 11 of the SQL standard specifies the INFORMATION_SCHEMA schema which must be part of all database catalogues. The schema may be used like this:

SELECT * FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_TYPE='BASE TABLE'

or (often more relevant):

SELECT * FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_TYPE='BASE TABLE'
  AND TABLE_SCHEMA='SCHEMA-NAME'

See a warning about potential case sensitivity problems below.

PostgreSQL Follows the standard, except for some gotchas mentioned below.

In command-line context, it's easier to use the following non-SQL command instead of querying the INFORMATION_SCHEMA:
\dt

Documentation: The tables INFORMATION_SCHEMA view, the psql tool.

DB2 Doesn't provide the standard INFORMATION_SCHEMA. Instead, DB2 offers the SYSCAT schema (catalog) which is somewhat compatible.

Offers what is probably a shorthand to some system catalog query:
LIST TABLES
or - if you want to see tables in another schema:
LIST TABLES FOR SCHEMA foo
These commands are only available in the db2 command line processor (i.e. not from—e.g.— db2batch).

MSSQL Follows that standard.
Sometimes, the SP_TABLES system stored procedure is easier to use.

Documentation:

MySQL Follows the standard, except that MySQL doesn't support schemas, so one might say that MySQL's INFORMATION_SCHEMA is really an 'INFORMATION_DATABASE' or 'INFORMATION_CATALOGUE'.

In command-line context, it's easier to use the following non-standard SQL:
SHOW TABLES

Documentation:

Oracle Doesn't provide the standard INFORMATION_SCHEMA. Provides a data dictionary system instead.

The quickest way to get a usable list of 'normal' tables:
SELECT * FROM tab
(Use of the tab dictionary view is officially deprecated, though.)

Warning about a general case sensitivity gotcha

Note that there may be case sensitivity issues involved when using meta-data views like those in the INFORMATION_SCHEMA. Generally, the standard states that the name of an identifier (such as table names) are implicitly converted to uppercase, unless double-quotes are used when referring to the identifier. The same goes for identifiers used in queries: A query like SELECT foo FROM tablename is implicitly converted to SELECT FOO FROM TABLENAME.

If you create your table as
  CREATE TABLE testtab (id INTEGER PRIMARY KEY)
then a query like
  SELECT * FROM testtab
should work fine, and
  SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME='TESTTAB'
should work, while the following query will probably fail:
  SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME='testtab'

Warning about INFORMATION_SCHEMA gotchas in PostgreSQL

Warning: PostgreSQL's case-conversion rules for unquoted identifiers (such as table names) are non-standard: PostgreSQL converts the identifiers to lower case, instead of converting to upper case. This means that you may try altering the case of identifier names used for queries in the INFORMATION_SCHEMA if you experience unexpected, empty metadata queries.

Note also that due to PostgreSQL's handling of constraint names, the INFORMATION_SCHEMA cannot safely be used to deduce referential constraints; for this, you have to use PostgreSQL's pg_catalog system-schema.

Getting a table description

Standard Part 11 of the SQL standard specifies the INFORMATION_SCHEMA schema which must be part of all database catalogues. The schema may be used like this:

SELECT column_name,data_type,column_default,is_nullable
FROM
  information_schema.tables AS t
  JOIN
  information_schema.columns AS c ON
    t.table_catalog=c.table_catalog AND
    t.table_schema=c.table_schema AND
    t.table_name=c.table_name
WHERE
  t.table_name='TABLE-NAME'

—or like this (more verbose):

SELECT
  column_name,
  data_type,
  character_maximum_length,
  numeric_precision,
  column_default,
  is_nullable
FROM
  information_schema.tables as t
  JOIN
  information_schema.columns AS c ON
    t.table_catalog=c.table_catalog AND
    t.table_schema=c.table_schema AND
    t.table_name=c.table_name
WHERE
    c.table_schema='TABLE-SCHEMA'
  AND
    c.table_name='TABLE-NAME'

To get information about constraints, involved columns and (possibly) referenced columns, a query like this may be used:
SELECT
  tc.CONSTRAINT_NAME,
  CONSTRAINT_TYPE,
  ccu.COLUMN_NAME,
  rccu.COLUMN_NAME,
  rccu.TABLE_CATALOG,
  rccu.TABLE_SCHEMA,
  rccu.TABLE_NAME,
  CHECK_CLAUSE
FROM
  INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc
  LEFT JOIN
  INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE ccu ON
    tc.CONSTRAINT_CATALOG=ccu.CONSTRAINT_CATALOG AND
    tc.CONSTRAINT_SCHEMA=ccu.CONSTRAINT_SCHEMA AND
    tc.CONSTRAINT_NAME=ccu.CONSTRAINT_NAME AND
    tc.TABLE_CATALOG=ccu.TABLE_CATALOG AND
    tc.TABLE_SCHEMA=ccu.TABLE_SCHEMA AND
    tc.TABLE_NAME=ccu.TABLE_NAME
  LEFT JOIN
  INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS rc ON
    rc.CONSTRAINT_CATALOG=ccu.CONSTRAINT_CATALOG AND
    rc.CONSTRAINT_SCHEMA=ccu.CONSTRAINT_SCHEMA AND
    rc.CONSTRAINT_NAME=ccu.CONSTRAINT_NAME
  LEFT JOIN
  INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE rccu ON
    rc.UNIQUE_CONSTRAINT_CATALOG=rccu.CONSTRAINT_CATALOG AND
    rc.UNIQUE_CONSTRAINT_SCHEMA=rccu.CONSTRAINT_SCHEMA AND
    rc.UNIQUE_CONSTRAINT_NAME=rccu.CONSTRAINT_NAME
  LEFT JOIN
  INFORMATION_SCHEMA.CHECK_CONSTRAINTS cc ON
    tc.CONSTRAINT_CATALOG=cc.CONSTRAINT_CATALOG AND
    tc.CONSTRAINT_SCHEMA=cc.CONSTRAINT_SCHEMA AND
    tc.CONSTRAINT_NAME=cc.CONSTRAINT_NAME
WHERE
  tc.TABLE_CATALOG='CATALOG-NAME' AND -- see remark
  tc.TABLE_SCHEMA='SCHEMA-NAME' AND   -- see remark
  tc.TABLE_NAME='TABLE-NAME'
ORDER BY tc.CONSTRAINT_NAME

If you don't care about potential namespace conflicts, you may leave out the lines commented with "-- see remark".

See also: Warning about potential case sensitivity problems above.

PostgreSQL Follows the standard, except for some gotchas mentioned above.

In command-line context it's easier to use this non-SQL command:
\d tablename

DB2 Doesn't provide the standard INFORMATION_SCHEMA.

To obtain (very) basic information about a table:
DESCRIBE TABLE tablename
DESCRIBE INDEXES FOR TABLE tablename SHOW DETAIL

To get information about constraints, including involved/referred columns, a query like the following may be used, although the db2 'utility' isn't good at adjusting column widths in output (i.e. the output is not easy to read):
SELECT
  tc.constname as const_name,
  type as const_type,
  kcu.colname as col_name,
  r.reftabschema as ref_tabschema,
  r.reftabname as ref_tabname,
  kcu_r.colname as ref_colname
FROM
  syscat.tabconst tc
  JOIN
  syscat.keycoluse kcu ON
    tc.constname=kcu.constname
  LEFT JOIN
  syscat.references r ON
    type='F' AND
    tc.constname=r.constname
  LEFT JOIN
  syscat.keycoluse kcu_r ON
    r.constname=kcu_r.constname
WHERE
  tc.tabschema=UCASE('schemaname') AND
  tc.tabname=UCASE('tablename')
ORDER BY const_name,col_name

Documentation:

MSSQL Follows the standard, except that
  • MSSQL uses non-standard names for some standard datatypes, i.e. varchar instead of the standard's CHARACTER_VARYING
  • MSSQL's INFORMATION_SCHEMA doesn't have all SQL:2003's columns (an example: MSSQL's INFORMATION_SCHEMA.COLUMNS view does not contain the IS_IDENTITY column)

Often, the SP_HELP tablename system stored procedure is easier to use.

Documentation:

MySQL Follows the standard, except that
  • MySQL doesn't support schemas, so one might say that MySQL's INFORMATION_SCHEMA is really an 'INFORMATION_DATABASE' or 'INFORMATION_CATALOGUE'.
  • MySQL's INFORMATION_SCHEMA doesn't have all SQL:2003's columns (an example: MySQL's INFORMATION_SCHEMA.COLUMNS view does not contain the IS_IDENTITY column).
  • As MySQL's namespaces don't match the SQL standard fully, the standard queries mentioned above will not work. The reason is that in MySQL, the value of TABLE_CATALOG is NULL for all tables and columns. To obtain the wanted information, you need to remove the table_catalog join-conditions. I.e., the first (and simplest) of the above queries must be re-written to:

    SELECT column_name,data_type,column_default,is_nullable
    FROM
      information_schema.tables AS t
      JOIN
      information_schema.columns AS c ON
        t.table_schema=c.table_schema AND
        t.table_name=c.table_name
    WHERE
      t.table_name='TABLE-NAME'

In command-line context it's easier to use this non-SQL command:
DESCRIBE tablename

Documentation:

Oracle Doesn't provide the standard INFORMATION_SCHEMA. Offers data dictionary views instead.

To get (very) basic information:
DESCRIBE tablename

To get information on constraints, including foreign (referred) table/column information, a query like this may be used (adjust tablename in one of the last lines):
COLUMN consname FORMAT a11;
COLUMN colname FORMAT a10;
COLUMN type FORMAT a11;
COLUMN cond FORMAT a20;
COLUMN ref_tabname FORMAT a11;
COLUMN ref_colname FORMAT a11;
SELECT
  uc.constraint_name consname,
  ucc.column_name colname,
  CASE
    WHEN uc.constraint_type='C' THEN 'CHECK'
    WHEN uc.constraint_type='P' THEN 'PRIMARY KEY'
    WHEN uc.constraint_type='R' THEN 'REFERENTIAL'
    WHEN uc.constraint_type='U' THEN 'UNIQUE'
    ELSE uc.constraint_type
  END as type,
  uc.search_condition cond,
  ucc_r.table_name ref_tabname,
  ucc_r.column_name ref_colname
FROM
  user_constraints uc
  JOIN
  user_cons_columns ucc ON
    uc.constraint_name=ucc.constraint_name AND
    uc.owner=ucc.owner
  LEFT JOIN
  user_constraints uc_r ON
    uc.r_constraint_name=uc_r.constraint_name AND
    uc.owner=uc_r.owner
  LEFT JOIN
  user_cons_columns ucc_r ON
    uc_r.constraint_name=ucc_r.constraint_name AND
    uc_r.owner=ucc_r.owner
WHERE
  uc.TABLE_NAME = UPPER('tablename')
ORDER BY consname,colname
;

To get information on indexes on a table, a query like this may be used (adjust tablename in one of the last lines):
COLUMN index_name  FORMAT a11;
COLUMN type        FORMAT a8;
COLUMN uniness     FORMAT a9;
COLUMN column_name FORMAT a20;
      SELECT index_name,
             index_type type,
             uniqueness uniness,
             column_name
        FROM user_indexes ui
NATURAL JOIN user_ind_columns uic
       WHERE dropped='NO'
         AND table_name=upper('tablename')
    ORDER BY index_name,column_name

Documentation:

Manually telling the DBMS to collect statistics

In most DBMSes, it's possible to enable automatic statistics gathering, but sometimes, it's nice to be able to manually tell the DBMS to gather statistics for a table (or a number of tables).

Standard Not standardized.
PostgreSQL ANALYZE tablename

If the tablename parameter is left out, then statistics are gathered for all tables in the current database.

DB2 RUNSTATS ON TABLE schema-name.table-name AND INDEXES ALL
(many variations/options available)

The RUNSTATS command needs to be invoked in a special way if you aren't using the db2 command line processor, namely through the ADMIN_CMD procedure.

Documentation: RUNSTATS and RUNSTATS wrapped in ADMIN_CMD.

MSSQL First, you have to add statistics to the table:
CREATE STATISTICS stats_name
ON table_name
(column_name_1, column_name_2, column_name_3, ...)

(The CREATE STATISTICS step is not needed for indexed columns. Thus, this step may be skipped if you are satisfied with keeping statistics on indexed columns only.)

The statistics may then be updated when needed:
UPDATE STATISTICS table_name

Having to explicitly mention tables and columns can be tedious, and in many cases, the sp_createstats and sp_updatestats stored procedures are easier to use.

Documentation: CREATE STATISTICS, UPDATE STATISTICS, sp_createstats, sp_updatestats

MySQL ANALYZE TABLE tablename
Oracle Oracle offers to estimate (quick) or compute (thorough) statistics for a database object. The quick way to do this is to use the deprecated ANALYZE command which can be used in various ways, e.g.

ANALYZE TABLE tablename ESTIMATE STATISTICS;
ANALYZE TABLE tablename ESTIMATE STATISTICS FOR ALL INDEXES;

(It's unclear to me if both are needed to gain the relevant statistics.)

—Or:
ANALYZE TABLE tablename COMPUTE STATISTICS;
ANALYZE TABLE tablename COMPUTE STATISTICS FOR ALL INDEXES;

If you want to stay away from deprecated features (although I doubt that Oracle will remove ANALYZE...STATISTICS... any time soon), you need to use the DBMS_STATS package.

Getting a query explanation

Standard Not standardized.
PostgreSQL EXPLAIN <query>
DB2 The easiest way to get a query explanation is to save the query in a file (without a terminating semicolon), and then run a special command-line utility:
db2expln -database databasename -stmtfile query.sql -terminal
In the above example, the query has been saved to a file called "query.sql".

In some situations, you may want to use the dynexpln utility instead of db2expln. And in yet other situations, the db2exfmt tool is a better choice. A visual explanation tool also exists.

If you prefer to get the explanation through SQL:

  1. Set up needed explain tables using EXPLAIN.DDL which should exist in sqllib/misc of your DB2 instance user's home directory.
  2. Optionally: Clean up old plan explanations: DELETE FROM EXPLAIN_INSTANCE
  3. Generate the explanation: EXPLAIN PLAN FOR <SQL-statement>
  4. Display plan:
    SELECT O.Operator_ID, S2.Target_ID, O.Operator_Type,
      S.Object_Name, CAST(O.Total_Cost AS INTEGER) Cost
    FROM EXPLAIN_OPERATOR O
      LEFT OUTER JOIN EXPLAIN_STREAM S2
        ON O.Operator_ID=S2.Source_ID
      LEFT OUTER JOIN EXPLAIN_STREAM S
        ON O.Operator_ID = S.Target_ID
        AND O.Explain_Time = S.Explain_Time
        AND S.Object_Name IS NOT NULL
    ORDER BY O.Explain_Time ASC, Operator_ID ASC

    (Adapted from recipe in SQL Tuning.)

MSSQL MSSQL can be put in a query explanation mode where queries are not actually executed, but a query explanation is returned instead:
SET SHOWPLAN_TEXT ON

The query explanation mode is turned off by running
SET SHOWPLAN_TEXT OFF

MySQL EXPLAIN <query>
Oracle After having set up a plan table, running
DELETE FROM plan_table WHERE statement_id='explanationX';
EXPLAIN PLAN
SET STATEMENT_ID = 'explanationX'
FOR <query>

will place an explanation into your PLAN_TABLE. Substitute explanationX with a suitable name for the explanation (and make sure you don't delete other users' explanation plans in the DELETE-line above).

The plan explanation may now be viewed by a query like this:
COLUMN operation   FORMAT a30;       -- for output formatting
COLUMN options     FORMAT a10;       -- for output formatting
COLUMN object_name FORMAT a12;       -- for output formatting
COLUMN otype       FORMAT a5;        -- for output formatting
COLUMN cardinality FORMAT 999999999; -- for output formatting
COLUMN cost        FORMAT 999999;    -- for output formatting
          SELECT LPAD(' ',2*(LEVEL-1))||operation operation,
                 options,
                 object_name,
                 object_type otype,
                 cardinality,
                 cost
            FROM plan_table
      START WITH id = 0 AND statement_id = 'explanationX'
CONNECT BY PRIOR id = parent_id
             AND statement_id = 'explanationX'

A bit of documentation reading can probably not be avoided:
Documentation

Turning on query timing

Standard Not standardized.
PostgreSQL \timing
DB2 Run the query in the "db2batch" command line processor; db2batch prints the elapsed time of each query.
MSSQL SET STATISTICS TIME ON
MySQL MySQL's command line interface prints query times by default.
Oracle SET TIMING ON

Other topics

Dummy table use

Some DBMSes let you perform a query like this:
 SELECT 1+1
answering
 2

With other DBMSes, you need to insert a dummy-table expression to obtain the same result:
 SELECT 1+1 FROM dummy-table

Standard On my TODO.
PostgreSQL No need for dummy-table.

In addition, the VALUES keyword may be used to produce a simple result set, without introducing a FROM clause, e.g.
VALUES(1+1)
(Note the missing SELECT and FROM keywords).

DB2 Dummy-table: SYSIBM.SYSDUMMY1.

In addition, the VALUES keyword may be used to produce a simple result set, without introducing a FROM clause, e.g.
VALUES(1+1)
(Note the missing SELECT and FROM keywords).

MSSQL No need for dummy-table.
MySQL No need for dummy-table, although MySQL allows you to refer to a DUAL dummy-table (for Oracle compatibility).
Oracle Dummy-table: DUAL.

Obtaining DBMS version

Standard SELECT CHARACTER_VALUE
  FROM INFORMATION_SCHEMA.SQL_IMPLEMENTATION_INFO
 WHERE IMPLEMENTATION_INFO_NAME='DBMS VERSION'
PostgreSQL Follows the standard. An alternative, non-standard function may be used:
SELECT VERSION()
DB2 SELECT service_level FROM SYSIBMADM.ENV_INST_INFO

—or run the special db2level program.

Documentation: SYSIBMADM.ENV_INST_INFO and db2level

MSSQL MSSQL's implementation of the IMPLEMENTATION_SCHEMA doesn't seem to include the SQL_IMPLEMENTATION_INFO view. In stead, you may use
SELECT SERVERPROPERTY('ProductVersion')
(just the version), or
SELECT @@VERSION
(verbose, harder to parse).

Documentation: SERVERPROPERTY, @@VERSION

MySQL MySQL's INFORMATION_SCHEMA doesn't include the SQL_IMPLEMENTATION_INFO view.

Work-around:
SELECT VERSION()

Oracle SELECT banner FROM v$version

Standard TCP/IP port

Standard Not specified
PostgreSQL 5432
DB2 50000
MSSQL 1433
MySQL 3306
Oracle 1521

Related work

评论Feed 评论Feed: /feed.asp?q=comment&id=1786

标签: sql different

这篇日志没有评论.

发表
表情图标
[smile] [confused] [cool] [cry]
[eek] [angry] [wink] [sweat]
[lol] [stun] [razz] [redface]
[rolleyes] [sad] [yes] [no]
[heart] [star] [music] [idea]
UBB代码
转换链接
表情图标
悄悄话
用户名:   密码:   (非注册用户不需要输入密码) 注册?
验证码(不区分大小写) * 请输入验证码