Comparison of Different SQL Implementations 1
Comparison of Different SQL Implementations 1
implementations
The goal of this page — which is a work in progress — is to gather information
relevant for people who are porting SQL from one product to another and/or are
interested in possibilities and limits of 'cross-product' SQL.
The following tables compare how different DBMS products handle various SQL
(and related) features. If possible, the tables also state how the implementations
should do things, according to the SQL standard.
I will only write about subjects that I've worked with personally, or subjects which
I anticipate to find use for in the near future. Subjects on which there are no
significant implementation variances are not covered. Beta-versions of software
are not examined. Furthermore, I will not mention all the cases of MySQL not
supporting standard SQL features.
I'm sorry about the colors. They are a result of wanting to mark each DBMS
differently and at the same time wanting to be relatively nice to printers.
Contents:
• Legend, definitions, and notes
• Features
o Views
o Join types/features
• The SELECT statement
o Ordering result sets
o Limiting result sets (RANK() / ROW_NUMBER() / LIMIT / TOP /
FETCH FIRST)
Simple limit
Top-n (quota-queries)
Limit—with offset, including note about the importance of
sorting on unique values
• The INSERT statement
o Inserting several rows at a time
• Data types
o BOOLEAN
o CHAR
o Date and time types
TIMESTAMP
• Functions and operators
o CHARACTER_LENGTH
o SUBSTRING
o REPLACE
o TRIM
o LOCALTIMESTAMP
o Concatenation
• Constraint handling
o The UNIQUE constraint
• Mixture of type and operations
o Automatic key generation
(IDENTITY/SERIAL/AUTO_INCREMENT)
• Command line operations
o Starting the command line interface
o Getting a list of tables
o Getting a table description
o Telling the DBMS to collect statistics
o Getting a query explanation
o Turning on query timing
• Other topics
o Dummy-table use
o Obtaining DBMS version
• Related work
• Acknowledgments
• TODOs
I don't have access to the official ISO standard text, but Whitemarsh
Information Systems Corporation provides a rather final draft as a
zip-archive, containing several files. Most important to this page is
the file 5WD-02-Foundation-2003-09.pdf.
SQL:2003 is very new, and the only book currently covering the
subject is in German which I was never any good at. Therefore, I
also use the following book as reference:
Jim Melton and Alan Simon: SQL:1999—Understanding Relational
Language Components (ISBN 1-55860-456-1).
PostgreSQL PostgreSQL 8.0.0 on Fedora Core Linux.
DOCUMENTATION
DB2 DB2 Universal Database Personal Edition v. 8.1FP7 (AKA v. 8.2)
on Fedora Core Linux. Note that there are differences between
various DB2 UDB flavors; this page is about DB2 for "LUW"
(Linux/Unix/Windows).
DOCUMENTATION (takes a while to render properly)
MS SQL MS SQL Server 2000 SP3a on Windows Server 2003. Microsoft's
Server SQL implementation is sometimes named Transact-SQL. In this
document, I'll generally write MSSQL as a short-hand for
Microsoft's SQL Server product.
DOCUMENTATION (takes a while to render properly)
MySQL MySQL Database Server 4.1.9 on Fedora Core Linux (i.e. MySQL
AB's "classic" DBMS product—not MaxDB).
DOCUMENTATION
(Note: The online MySQL manual documents MySQL beta-releases, i.e.
sometimes it doesn't reflect the situation in the current MySQL production
versions.)
Oracle Oracle Database 10g Release 1 Standard Edition on Fedora Core
release 1 (Linux). The tables should hold for version 9i, as well
(Oracle 10g contains remarkably few improvements/changes in
Oracle's SQL standard compliance).
DOCUMENTATION (requires OTN-membership)
The products are running with their default settings. This is important for MySQL
and MSSQL: Their interpretation of SQL may be changed rather drastically by
adjusting certain configuration options, potentially increasing the level of standard
compliance. However, such non-default configuration options are not of great
value for people writing SQL applications because the developer often cannot
rely on non-default configuration settings.
Features
Views
Standard Views are part of the standard, and they may be updated, as long as
it 'makes sense'.
Peter Gulutzan has written an article about the implementation of views in three major products.
All the DBMSes support basic INNER JOINs, but vary in their support for other
join types.
In the following feature chart, a means yes; an empty table cell means no.
Remarks:
Standard The SQL-standard states that relations are unordered, but result
sets may be ordered when returned to the user through a cursor:
DOCUMENTATION
DB2 Allows ORDER BY in contexts other than cursor definitions. NULLs are
considered higher than any non-NULL value.
DOCUMENTATION
MSSQL Allows ORDER BY in contexts other than cursor definitions. NULLs are
considered lower than any non-NULL value.
DOCUMENTATION
MySQL Allows ORDER BY in contexts other than cursor definitions.
DOCUMENTATION
Oracle Allows ORDER BY in contexts other contexts than cursor definitions.
DOCUMENTATION
Simple limit
Objective: Want to only get n rows in the result set. Usually only makes sense in
connection with an ORDER BY expression.
Note: This is not the same as a top-n query — see next section.
Note also: Some of the queries below may not be legal in all situations, such as
in views or sub-queries.
SELECT * FROM (
SELECT
ROW_NUMBER() OVER (ORDER BY key ASC) AS rownumber,
columns
FROM tablename
) AS foo
WHERE rownumber <= n
PostgreSQL Doesn't support ROW_NUMBER(). Supports cursors (in all contexts, not
only in embedded, dynamic SQL).
SELECT columns
FROM tablename
ORDER BY key ASC
LIMIT n
DOCUMENTATION
DB2 Supports both standards-based approaches. An alternative to using
ROW_NUMBER(), which may be quicker in some situations(?):
SELECT columns
FROM tablename
ORDER BY key ASC
FETCH FIRST n ROWS ONLY
DOCUMENTATION
MSSQL Doesn't support ROW_NUMBER(); supports cursors.
Alternative to using ROW_NUMBER():
DOCUMENTATION
MySQL Doesn't support the standard. Alternative solution:
SELECT columns
FROM tablename
ORDER BY key ASC
LIMIT n
DOCUMENTATION
Oracle Supports ROW_NUMBER. Seems to have non-compliant cursor facilities.
SELECT * FROM (
SELECT
ROW_NUMBER() OVER (ORDER BY key ASC) AS rownumber,
columns
FROM tablename
)
WHERE rownumber <= n
DOCUMENTATION
Top-n query
Objective: Like the simple limit-query above, but include rows with tie conditions.
Thus, the query may return more than n rows.
Now, we only want the three (n=3) youngest persons displayed, i.e. a result set
like this:
+----------+-------------+-----+
|PERSON_ID | PERSON_NAME | AGE |
+----------+-------------+-----+
| 7 | Hilda | 12 |
| 8 | Bill | 12 |
| 4 | Joe | 23 |
| 2 | Veronica | 23 |
+----------+-------------+-----+
Standard With standard SQL, there are two principal ways to obtain the
wanted data:
SELECT * FROM (
SELECT
RANK() OVER (ORDER BY age ASC) AS ranking,
person_id,
person_name,
age
FROM person
) AS foo
WHERE ranking <= 3
(Change ASC to DESC in the position marked like this in order to get a
top-3 oldest query instead.)
(Change < to > in the position marked like this in order to get a top-3
oldest query instead.)
SELECT *
FROM person
WHERE (
age <= (
SELECT age FROM person
ORDER BY age ASC
LIMIT 1 OFFSET 2 -- 2=n-1
)
) IS NOT FALSE
(Change <= to >= and ASC to DESC in the positions marked like this in order to
get a top-3 oldest query instead.)
DB2 Supports the fast standard SQL variant.
DOCUMENTATION
MSSQL Supports the slow standard SQL variant. In practice, a MSSQL-only
expression should be used, in order to obtain acceptable query
performance:
DOCUMENTATION
MySQL Supports the slow standard SQL solution. In practice, this MySQL-
specific solution should be used, in order to obtain acceptable query
performance:
SELECT *
FROM person
WHERE age <= COALESCE(
(
SELECT age
FROM person
ORDER BY age ASC
LIMIT 1 OFFSET 2 -- 2=n-1
),
(
SELECT MAX(age)
FROM person
)
)
(Change <= to >= and ASC to DESC and MAX to MIN in the positions marked like
this in order to get a top-3 oldest query instead.)
The second argument to the COALESCE call makes the query work in
cases where the cardinality of the table is lower than n.
Oracle Supports the fast standard SQL variant. However, as Oracle doesn't
like "AS ..." after subqueries (and doesn't require naming of
subqueries), the query has to be paraphrased slightly:
SELECT * FROM (
SELECT
RANK() OVER (ORDER BY age ASC) AS ranking,
person_id,
person_name,
age
FROM person
)
WHERE ranking <= 3
(Change ASC to DESC in the position marked like this in order to get a top-3
oldest query instead.)
DOCUMENTATION
Limit—with offset
Objective: Want to only get n rows in the result set, and we want the first skip
rows in the result set discarded. Usually only makes sense in connection with an
ORDER BY expression.
In the recipes below, basic ordering is ASCending, i.e. lowest-first queries. If you
want the opposite, then change ASC->DESC and DESC->ASC at the places
emphasized like this.
SELECT * FROM (
SELECT
ROW_NUMBER() OVER (ORDER BY key ASC) AS rownum,
columns
FROM tablename
) AS foo
WHERE rownum > skip AND rownum <= (n+skip)
Alternative to ROW_NUMBER():
SELECT columns
FROM tablename
ORDER BY key ASC
LIMIT n OFFSET skip
DOCUMENTATION
DB2 Supports both standard approaches.
DOCUMENTATION
MSSQL Doesn't support ROW_NUMBER(); supports cursors.
Alternative to ROW_NUMBER():
SELECT * FROM (
SELECT TOP n * FROM (
SELECT TOP z columns -- (z=n+skip)
FROM tablename
ORDER BY key ASC
) AS FOO ORDER BY key DESC -- ('FOO' may be anything)
) AS BAR ORDER BY key ASC -- ('BAR' may be anything)
DOCUMENTATION
SELECT columns
FROM tablename
ORDER BY key ASC
LIMIT n OFFSET skip
DOCUMENTATION
Oracle Supports ROW_NUMBER(). Oracle's cursor support doesn't look
standards-compliant.
SELECT * FROM (
SELECT
ROW_NUMBER() OVER (ORDER BY key ASC) AS rn,
columns
FROM tablename
)
WHERE rn > skip AND rn <= (n+skip)
DOCUMENTATION
Note:
LIMIT/TOP/FIRST queries with offset are often used in a result presentation context: To retrieve
only—say—30 rows at a time so that the end-user isn't overwhelmed by the complete result set,
but instead is offered a paginated result presentation. In this case, be careful not to (only) sort on
a non-unique column.
When ordering is performed on the non-unique age-value, ties may occur and it's not guaranteed
that the DBMS will fetch the rows in the same order every time.
Instead of the above listing, the DBMS is allowed to return the following display order where
Michael and Marianne are displayed in the opposite order compared to above:
Now, suppose the end-user wants the results displayed five rows at a time. The result set is
fetched in two queries where the DBMS happens to sort differently, as above. We will use
PostgreSQL's syntax in the example:
Notice that Marianne was not displayed in any of the two split result set presentations.
The problem could be avoided if the result set ordering had been done in a deterministic way, i.e.
where the unique person_id value was considered in case of a tie:
SELECT * FROM person ORDER BY age ASC, person_id ASC ...
This is safer than to pray for the DBMS to behave in a predictable way when handling non-unique
values.
Note: If the table is updated between parts of the result set pagination, then the user might still
get an inconsistent presentation. If you want to guard against this, too, then you should see if use
of an insensitive cursor is an option in your application. Use of cursors to paginate result sets
usually require that your application is stateful, which is not the case in many web-application
settings.
Alternatively, you could copy the result set to a table and use the copied table for pagination; with
this solution, you might accumulate a vast amount of result-set generated tables if you don't
remember to clean up. If you end up playing with such ideas, then it's probably a good idea to
start considering if the entire result set may be cached by the application (e.g. in a session if your
web application environment provides for sessions).
Data types
The BOOLEAN type
Standard The BOOLEAN type is optional (has feature ID T031), which is a bit
surprising for such a basic type. However, it seems that endless
discussions of how NULL is to be interpreted for a boolean value is
holding BOOLEAN from becoming a core type.
• TRUE
• FALSE
• UNKNOWN or NULL (unless prohibited by a NOT NULL
constraint)
It's defined that TRUE > FALSE (true larger than false).
PostgreSQL Follows the standard.
DOCUMENTATION
DB2 Doesn't support the BOOLEAN type.
Possible alternative type: the BIT type which may have 0 or 1 (or
NULL) as value. If you insert an integer value other than these into a
field of type BIT, then the inserted value will silently be converted to
1.
Rudy Limeback has some notes about oddities with the MSSQL BIT
type.
DOCUMENTATION
MySQL Offers a non-conforming BOOLEAN type. MySQL's BOOLEAN is
one of many aliases to its TINYINT(1) type.
(Never use TINYINT(1) as the column type if you use JDBC with MySQL and
expect to get non-boolean values from it.)
If you use JDBC with MySQL, then BOOLEAN is the preferred type
for booleans: MySQL's JDBC-driver implicitly converts between
Java's boolean and MySQL's pseudo-BOOLEAN type.
DOCUMENTATION
Oracle Doesn't support the BOOLEAN type.
For the following section, I have used this test-SQL to try to illuminate differences
(unfortunately, even standard SQL as simple as this has to be adjusted for some
products):
Test steps:
CREATE TABLE chartest (
charval1 CHAR(10) NOT NULL,
charval2 CHAR(10) NOT NULL,
varcharval VARCHAR(30) NOT NULL
);
INSERT INTO chartest VALUES ('aaa','aaa','aaa');
INSERT INTO chartest
VALUES ('aaaaaa ','aaa','aaa'); -- should truncate to
'aaaaaa '
INSERT INTO chartest
VALUES ('aaaaaaaaaaaa','aaa','aaa'); -- should raise error
SELECT * FROM chartest; -- should show two rows
DELETE FROM chartest WHERE charval1='aaaaaa';
SELECT * FROM chartest; -- should show one row
SELECT * FROM chartest WHERE charval1=varcharval;
SELECT charval1 || 'X' AS res FROM chartest;
SELECT CHAR_LENGTH(charval1 || charval2) AS res FROM chartest;
SELECT CHAR_LENGTH(charval1) + CHAR_LENGTH(charval2)
AS res
FROM chartest;
Actual results.
DOCUMENTATION
DB2 Follows the standard.
DOCUMENTATION
MSSQL Generally follows standard, but (conceptually) truncates trailing
white-space before performing some functions (at least before
LEN()).
DOCUMENTATION
MySQL Breaks the standard by silently inserting the string, truncated to
specified column CHAR-length.
(It's actually not completely silent, as it issues warnings if values were truncated: If
you manually check for warnings, you will know that something bad happened, but
not which of the rows are now invalid.)
DOCUMENTATION
Oracle Follows the standard, with a minor exception: Oracle doesn't remove
trailing spaces which exceed the specified CHAR length, but raises
an exception.
DOCUMENTATION
It's strange that TIMESTAMP WITH TIME ZONE literals are not represented as,
e.g., TIMESTAMP WITH TIME ZONE '2003-07-29 13:19:30+01:00', but
according to Melton & Simon's book, they aren't.
PostgreSQL Follows that standard with one exception:
TIMESTAMP '2003-08-23 01:02:03 +02:00' is interpreted as a
TIMEZONE WITHOUT TIME ZONE (discarding the '+02:00' part)—
not as a TIMESTAMP WITH TIME ZONE value. The standard may
be illogical regarding this, but a standard is a standard...
DOCUMENTATION
DB2 DB2 has the TIMESTAMP data type, but not the extended
TIMESTAMP WITH TIME ZONE type.
DB2 accepts TIMESTAMP literals like '2003-07-23 00:00:00',
however it doesn't accept the typed TIMESTAMP '2003-07-23
00:00:00' variant.
DOCUMENTATION
MSSQL Note that MSSQL's choice of words related to date and time is
confusing: In MSSQL's vocabulary, datetime is a concrete data type,
whereas in the SQL standard, datetime is a general term covering
the DATE, TIME and TIMESTAMP types.
DOCUMENTATION
MySQL In general: No matter what date/time data type chosen in MySQL,
storage of fractional seconds and time zones are not supported. You
will have to invent your own systems for such information.
Note also, that MySQL's choice of words related to date and time is
confusing: In MySQL's vocabulary, datetime is a concrete data type,
whereas in the SQL standard, datetime is a general term covering
the DATE, TIME and TIMESTAMP types.
MySQL's sanity checks with regard to dates and time are poor. For
example, MySQL gladly accepts a DATETIME value of '2003-02-
29 00:05:00'. DATETIME values with errors that are easier to detect
(like a value of '2003-01-32 00:00:00') yield a warning (which you
must check for if you want to be warned) and inserts a value of zero.
This means that if you want to be able to prevent the insertion of
invalid DATETIME values in MySQL, your application must either
use transactions (and hope that the tables support them) or record
the state of the relevant rows before doing an update/insert, so that
changes may manually be undone, in case a warning should be
issued by MySQL.
DOCUMENTATION
Oracle Follows the standard. Oracle has both the TIMESTAMP and the
extended TIMESTAMP WITH TIME ZONE types.
DOCUMENTATION
See also this link to an article timestamp handling in the 'Big Three' (DB2, MSSQL, Oracle).
SQL functions
CHARACTER_LENGTH
DOCUMENTATION
DB2 Doesn't have CHARACTER_LENGTH. Provides the LENGTH function
instead.
Note that CHAR values are space-padded (like the standard says
they should be), so the length of 'HEY ' is 5. Consider using
LENGTH(RTRIM(foo)) if you want the length without trailing spaces.
DOCUMENTATION
MSSQL Doesn't have CHARACTER_LENGTH. Provides the LEN and
DATALENGTH functions instead (the latter is especially valid for
'special' data types like the TEXT type).
Note that MSSQL's LEN-function removes trailing (not leading)
spaces from CHAR values before counting; MSSQL's DATALENGTH
doesn't discard spaces.
DOCUMENTATION
Oracle Doesn't have CHARACTER_LENGTH. Provides the LENGTH function
instead.
Note that CHAR values are space-padded (like the standard says
they should be), so the length of 'HEY ' is 5. Consider using
LENGTH(TRIM(TRAILING FROM foo)) if you want the length without
leading/trailing spaces.
DOCUMENTATION
SUBSTRING
Standard The standard defines two variants of the SUBSTRING function:
DOCUMENTATION
DB2 Doesn't provide the standard SUBSTRING function. Provides
SUBSTR(input,start-pos[,length]) instead (i.e. length is optional).
A start-pos of 1 is the first character in the input string.
DB2 is less permissive than the standard: Out-of-range values are
not permitted, and NULL cannot be the value for start-pos or length.
DB2 doesn't seem to provide regular expression facilities for
queries.
DOCUMENTATION
MSSQL MSSQL has a SUBSTRING function, but its syntax differs from that of
the standard. The syntax is:
DOCUMENTATION
MySQL MySQL supports the standard's ordinary SUBSTRING function, with
a twist (see below). No regular expression based substring
extraction is supported.
DOCUMENTATION
Oracle Doesn't provide the standard SUBSTRING function. Provides
SUBSTR(input,start-pos[,length]) instead (i.e. length is optional).
Oracle provides a number of SUBSTR-variants (SUBSTRB,
SUBSTRC, SUBSTR2, SUBSTR4, same syntax as for SUBSTR),
mainly for handling various kinds of non-latin-only string-types.
Oracle doesn't have support for string-extraction with the special
SQL-style regular expressions. Instead, it has the
REGEXP_SUBSTR function which offers string extraction, using
POSIX-style regular expression pattern matching.
Note: If you find yourself using SUBSTRING in a WHERE-expression, then consider if LIKE could
be used instead: The use of LIKE will typically make your DBMS try to use an index, whereas it
will typically not try to do so in connection with functions.
REPLACE
REPLACE (haystack:string,needle:string,replacement:string)
Note 1:
In this author's opinion, it's confusing that most (if not all) string-related functions in MySQL work
case sensitively, while MySQL's default behaviour is to work case insensitively in plain WHERE-
clauses involving string comparisons.
TRIM
DOCUMENTATION
DB2 Doesn't support the standard TRIM function.
Provides
LTRIM(string_to_be_trimmed)
and
RTRIM(string_to_be_trimmed)
Provides
LTRIM(string_to_be_trimmed)
and
RTRIM(string_to_be_trimmed)
DOCUMENTATION
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:
DOCUMENTATION
DB2 Doesn't have the LOCALTIMESTAMP function.
DOCUMENTATION
MySQL Follows the standard.
DOCUMENTATION
Oracle Follows the standard.
Concatenation
string1 || string2
DOCUMENTATION
DB2 Follows the standard, partly.
DOCUMENTATION
MSSQL Breaks the standard by using the '+' operator instead of '||'.
DOCUMENTATION
MySQL Badly breaks the standard by redefining || to mean OR.
DOCUMENTATION
Oracle Follows the standard, partly.
DOCUMENTATION
Constraint handling
The UNIQUE constraint
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.
DOCUMENTATION
DB2 Follows the non-optional parts of the UNIQUE-constraint. Doesn't
implement the optional NULLs allowed feature.
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.
DOCUMENTATION
MySQL Follows the standard, including the optional NULLs allowed feature.
Oracle Follows the standard—with a twist:.
DOCUMENTATION
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.
or
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.
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.
DOCUMENTATION
Oracle Oracle doesn't support the standard's IDENTITY attribute.
Fortunately, a tool like HenPlus exists. It can be a pain to install, but once
working, it's nice to work with.
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
DB2 also has 'utilities' called db2sql92 and db2batch which some
might find at bit nicer to work with, although they lack support for
some useful non-SQL db2 commands like LIST TABLES.
MSSQL The command line interface is started by running
osql
osql 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 osql: It has command
history, so you may press arrow-up for previous commands in the
current osql session.
DOCUMENTATION
MSSQL Follows that standard, except that 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).
Sometimes, the SP_TABLES system stored procedure is easier to use.
DOCUMENTATION
MySQL Doesn't provide the standard INFORMATION_SCHEMA.
Use:
SHOW TABLES
DOCUMENTATION
Oracle Doesn't provide the standard INFORMATION_SCHEMA. Provides a
data dictionary system instead.
DOCUMENTATION
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.
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.
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'
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'
If you don't care about potential namespace conflicts, you may leave
out the lines commented with "-- see remark".
DOCUMENTATION:
DOCUMENTATION
MySQL Doesn't provide the standard INFORMATION_SCHEMA.
Use:
DESCRIBE tablename
Oracle Doesn't provide the standard INFORMATION_SCHEMA. Offers
data dictionary views instead.
DOCUMENTATION:
If the tablename parameter is left out, then statistics are gathered for
all tables in the current database.
DOCUMENTATION
DB2 RUNSTATS ON TABLE schema-name.table-name AND INDEXES ALL
DOCUMENTATION
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;
DOCUMENTATION
DOCUMENTATION
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".
DOCUMENTATION
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
DOCUMENTATION
MySQL EXPLAIN <query>
DOCUMENTATION
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).
DOCUMENTATION
DB2 Run the query in the "db2batch" command line processor; db2batch
prints the elapsed time of each query.
MSSQL SET STATISTICS TIME ON
DOCUMENTATION
MySQL MySQL's command line interface prints query times by default.
Oracle SET TIMING ON
DOCUMENTATION
Other topics
Dummy table use
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.
DB2 Dummy-table: SYSIBM.SYSDUMMY1.
DOCUMENTATION
DB2 Run the special db2level program.
DOCUMENTATION
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
MySQL SELECT VERSION()
DOCUMENTATION
Oracle SELECT banner FROM v$version
DOCUMENTATION
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.
• 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-92) approach and then how to
adjust to the real World, using MS Access 2002, MSSQL 2000, Oracle 9,
MySQL 4.0 and PostgreSQL 7.1.
• Peter Gulutzan has written several articles related to the subject. He has
also written two related books:
o SQL-99 Complete, Really (co-authored with Trudy Pelzer) is said to
be good.
o SQL Performance Tuning (also co-authored with Trudy Pelzer),
mentions quite a few cross-product SQL issues (primarily related to
performance, of course).
Gulutzan has been hired by MySQL AB. Here's hoping that it's not just a
let's-look-nice marketing move by MySQL AB; with luck (and
management's backing), it could help bring MySQL out of its state of
almost anti-standard SQL support.
Acknowledgments
The following people have provided comments, suggestions and/or fixes,
resulting in content changes on this page:
• Ian Barwick
• Chester Kustarz
• Bruno Wolff III
• Carsten Pedersen
• Jürgen Auer
• Edi Stocker
• Tzvetan Tzankov
• Jess Robinson
• Gordon P. Hemsley
• Philip Nelson
• Andreas Plesner Jacobsen
• Clive Page
• Holger Jakobs
• Dennis Björklund
• Chris Fehily
• Alf-Ivar Holm
• Joseph Fuda
• J M Sykes
• Greg Sabino Mullane
This work is licensed under a Creative Commons License which allows anyone