转: Comparison of different SQL implementations (2) [私人]
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:
Note that " If the DBMS supports the non-core time zone features (feature ID F411), then
it must also provide the functions |
| 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:
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, 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:
|
| 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.
Documentation (see the unique-constraint section of the page). |
| 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
|
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):
or
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:
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:
'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
Another option is to add the OIDs cannot be assigned by the user, so the semantics of OIDs resemble the standard's |
| 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:
With MSSQL's IDENTITY attribute, the user cannot manually
insert/change the value, unless the user has first run I.e., MSSQL's IDENTITY type is closest to the standard's
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
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: 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 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:
This will create an auto-incrementing column resembling the
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: |
Note: IBM has a page comparing IDENTITY columns and sequences.
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— |
| DB2 | DB2 doesn't have a TRUNCATE TABLE command.
Instead, you may abuse the
The 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 Needed privileges—Quoting from the documentation:
|
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:psqlwhich 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 The 'utility' doesn't seem to have anything resembling useful
command history or command completion. Fortunately, queries may be
sent to the DB2 also has a 'utility' called |
| MSSQL |
The command line interface is started by runningsqlcmd
In MSSQL 2000, the command line interface was started by running 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
|
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 |
| 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:
|
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:
or (often more relevant):
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: Documentation: The |
| 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: |
| 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:
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: |
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:
—or like this (more verbose):
To get information about constraints, involved columns and (possibly)
referenced columns, a query like this may be used: If you don't care about potential namespace conflicts, you may leave out the lines commented with " 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: |
| DB2 |
Doesn't provide the standard INFORMATION_SCHEMA.
To obtain (very) basic information about a table: To get information about constraints, including involved/referred columns,
a query like the following may be used, although the Documentation:
|
| MSSQL | Follows the standard, except that
Often, the Documentation: |
| MySQL |
Follows the standard, except that
In command-line context it's easier to use this non-SQL command: Documentation: |
| Oracle |
Doesn't provide the standard INFORMATION_SCHEMA. Offers data dictionary views instead.
To get (very) basic information: 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): To get information
on indexes on a table, a query like this may be
used (adjust tablename in one of the last lines): 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 Documentation: |
| MSSQL | First, you have to add statistics to the table:
CREATE STATISTICS stats_name
(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:
Having to explicitly mention tables and columns can be tedious, and in many cases, the 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.
—Or: 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 -terminalIn the above example, the query has been saved to a file called "query.sql". In some situations, you may want to use the If you prefer to get the explanation through SQL:
|
| 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 |
| MySQL | EXPLAIN <query>
|
| Oracle | After having set up a plan table, runningDELETE FROM plan_table WHERE statement_id='explanationX';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: A bit of documentation reading can probably not be avoided: |
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 |
| DB2 | Dummy-table: SYSIBM.SYSDUMMY1.
In addition, the |
| 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
|
| 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 Documentation: |
| MSSQL | MSSQL's implementation of the IMPLEMENTATION_SCHEMA doesn't seem to include the SQL_IMPLEMENTATION_INFO view. In stead, you may useSELECT 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: |
| 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
- Mimer Information Technology AB (makers of the
Mimer SQL DBMS) has an
interesting
feature comparison chart, displaying what SQL:1999 features are implemented
in different commercial products. May be biased because it's created by a DBMS vendor.
Mimer also has lists of reserved words. - Wikipedia has a Comparison of relational database management systems page. And a Wikibook called SQL dialects reference is in the works.
- MySQL AB has a feature comparison machine; possibly somewhat biased in favor of MySQL.
- Chris Fehily's SQL: Visual QuickStart Guide teaches SQL by first describing the standards-based (SQL:2003) approach, and then how to adjust to the real World, using MS Access, MSSQL, Oracle, MySQL, PostgreSQL, and DB2. (Full disclosure note: I was technical editor on second edition of the book.)
- Peter Gulutzan (who works for MySQL AB) has written several
articles related to the subject. He has also written two related books:
- SQL-99 Complete, Really (co-authored with Trudy Pelzer) is said to be good.
- SQL Performance Tuning (also co-authored with Trudy Pelzer), mentions quite a few cross-product SQL issues (primarily related to performance, of course).
- Some DBMS evaluations performed at the Astrogrid Virtual Observatory (focus on spatial functionality):
- Various comparisons of MySQL, PostgreSQL and DB2.
- Comparison of availability and names of mathematical functions in major DBMS products.
- Autumn '03: Comparison of DB2, MySQL, and Postgres, comparing ease of use, scalability and performance of two types of spatial joins.
- Autumn '02: Comparison of PostgreSQL, MySQL, Oracle, SQL Server and DB2.
- Uday Parmar: Open Source Database Feature Comparison Matrix.
Note: Created by employees of a database vendor. - SQLite's survey of NULL-handling: NULL Handling in SQLite Versus Other Database Engines.
- Bowman/Emerson/Darnovsky's The Practical SQL Handbook—Using SQL Variants is OK for this subject, although it is rather out-dated and (worse) doesn't include any guidance on working with open source DBMSs.
- Kevin E. Kline's SQL in a Nutshell from O'Reilly is a good reference. O'Reilly has also published Jonathan Gennick's SQL Pocket Guide which looks good (but I haven't read it).
- The Analysis and Solutions Company:
- Building Truly Portable Database Applications in PHP includes advice on DBMS differences (some of the presentation is PHP-specific, as the title indicates).
- Database Portability: Date and Timestamp Columns.
- IBM has an online book about writing portable SQL with DB2: SQL Reference for Cross-Platform Development.
- Jutta Horstmann OSDBmigration.
- See also my DBMS links.
评论Feed: /feed.asp?q=comment&id=1786

