You are on page 1of 5

SQL coding conventions

 Drupal version: Drupal 4.5.x or older, Drupal 4.6.x, Drupal 4.7.x, Drupal 5.x, Drupal 6.x


 Audience: Developers and coders
 Last modified: August 9, 2010

Don't use Reserved Words


Don't use (ANSI) SQL / MySQL / PostgreSQL / MS SQL Server / ... Reserved Words for
column and/or table names. Even if this may work with your (MySQL) installation, it may
not with others or with other databases. Some references:

 (ANSI) SQL Reserved Words


 MySQL Reserved Words: 5.1, 5.0, 3.23.x, 4.0, 4.1
 PostgreSQL Reserved Words
 Oracle Reserved Words (in particular UID is a problem in our context)
 MS SQL Server Reserved Words
 DB2 Reserved Words
Some commonly misused keywords: TIMESTAMP, TYPE, TYPES, MODULE, DATA, DATE,
TIME, ...
See also [bug] SQL Reserved Words.

Capitalization and user-supplied data


 Make SQL reserved words UPPERCASE. This is not a suggestion. Drupal db
abstraction commands will fail if this convention is not followed.
 Make column and constraint names lowercase.
 Enclose each table name with {} (this allows Drupal to prefix table names).
 Variable arguments (which are often user-supplied) should be moved out of the
query body and passed in as separate parameters to db_query(), db_query_range(),
and db_query_temporary(), etc. The query body should instead contain placeholders
specifying the type of each argument (%d|%s|%%|%f|%b). This ensures that the data will be
properly escaped and prevents SQL injection attacks.
 Preventing SQL injection is easy; db_query provides a way to use parametrized
queries. Drupal's database functions replace the sprintf-like placeholders with the properly
escaped arguments in order of appearance:
 %d - integers
 %f - floats
 %s - strings, enclose in single quotes
 %b - binary data, do not enclose in single quotes
 %% - replaced with %
 Read more about Database Access
 Literal (constant) arguments can either be included in the query body or handled in
the same way as variable arguments.
 Any string literal or %s placeholder must be enclosed by single quotes: ' . Never use
double quotes.
Example:

<?php
db_query("INSERT INTO {filters} (format, module, delta, weight) VALUES (%d, 'php', 0,
0)", $format);
?>

NOTE: as of Drupal 6.x, table definitions and constraints (e.g. primary keys, unique
keys, indexes) should be always handled by the Schema API, which solves cross-
database compatibility concerns automatically.

Naming
 Use singular nouns for table names since they describe the entity the table
represents. Drupal 6.x mixed singular/plural usage and this convention changed for Drupal
7.x.
 Name every constraint (primary, foreign, unique keys) yourself. Otherwise, you'll see
funny-looking system-generated names in error messages. This happened with
the moderation_roles table which initially defined a key without explicit name as KEY (mid).
This got mysqldump'ed as KEY mid (mid) which resulted in a syntax error as mid() is a
mysql function (see [bug] mysql --ansi cannot import install database).
 Index names should begin with the name of the table they depend on, eg. INDEX
users_sid_idx.
NOTE: as of Drupal 6.x, table definitions and constraints should be always handled
by the Schema API.

Configure your Database server for standard compliance


Most Database Servers use extension to standard SQL. However, many of them can be
configured to run in a (more) standard compliant mode. Every developer is encouraged to
use the mode most standard compliant to avoid sloppy coding and compatibility problems.

MySQL
Enable ANSI and Strict Mode
Please help growing this list for other database servers!

References
 Joe Celko - Ten Things I Hate About You
 Joe Celko - SQL for Smarties: Advanced SQL Programming
 RDBMS Naming conventions

Indentation
Drupal does not have a standard method for indentation or formating of longer SQL queries
on multiple lines. Some competing strategies include:

<?php
if (!(db_query(
  "
    INSERT INTO {mlsp_media_file_type}
    SET extension   = '%s',
    attributes      = '%s'
  ",
  $file_type_entry['extension'],
  $selected_attributes
))) {
  $errors = TRUE;
}
?>

or
<?php
$sql = "SELECT t.*, j1.col1, j2.col2"
. " FROM {table} AS t"
. " LEFT JOIN {join1} AS j1 ON j1.id = t.jid"
. " LEFT JOIN {join2} AS j2 ON j2.id = t.jjid"
. " WHERE t.col LIKE '%s'"
. " ORDER BY %s"
;
$result = db_query($sql, 'findme', 't.weight');
?>

 Avoid "SELECT * FROM ..."


 List of SQL reserved words
‹ PHP ExceptionsupAvoid "SELECT * FROM ..." ›

Login or register to post comments

Comments
In the past I used the
Posted by foobar3000 on February 18, 2010 at 12:49pm

In the past I used the version with a lot of dots, which was at some point not good
anymore.

I do prefer a simmilar style like the first example above, but with a little twist.

<?php
$sql = "
  SELECT    t.*
  ,         j1.col1
  ,         j2.col2
  FROM      {table} AS t
  LEFT JOIN {join1} AS j1 ON j1.id = t.jid
  LEFT JOIN {join2} AS j2 ON j2.id = t.jjid
  WHERE     t.col LIKE '%s'
  AND       t.status = 1
  AND       t.type = 'foobar'
  ORDER BY  t.bogus ASC
  ,         t.bar DESC
  LIMIT     10, 30
";
$result = db_query($sql, 'findme');
?>

This type of SQL statement is, at least for me, very good readable even for quite long sql
statements and you can add comments inside the sql statement very easy:

<?php
$sql = "
  SELECT    t.*
  ,         j1.col1
  ,         j2.col2
  -- ,         j2.col3
  FROM      {table} AS t
  LEFT JOIN {join1} AS j1 ON j1.id = t.jid
  LEFT JOIN {join2} AS j2 ON j2.id = t.jjid
  WHERE     t.col LIKE '%s'
  AND       t.status = 1
  AND       t.type = 'foobar'
  ORDER BY  t.bogus ASC
  -- ,         t.bar DESC
  LIMIT     10, 30
";

$result = db_query($sql, 'findme');


?>

Another Example:

<?php
$sql = "
  SELECT    t.*
  ,         j1.col1
  ,         j2.col2
  ,         j2.col3
  ,         (
    SELECT    b.dateme
    FROM      {date_persons} AS dp
    WHERE     dp.person_id = t.person_id
    LIMIT     1
  ) AS dateme
  FROM      {table} AS t
  LEFT JOIN {join1} AS j1 ON j1.id = t.jid
  LEFT JOIN {join2} AS j2 ON j2.id = t.jjid
  WHERE     t.col LIKE '%s'
  AND       t.status = 1
  AND       t.type = 'foobar'
  AND       t.person_id IN(
    SELECT    t.id
    FROM      {persons}
    WHERE     t.name LIKE '%s%%'
  )
  ORDER BY  t.bogus ASC
  ,         t.bar DESC
  LIMIT     10, 30
";

$result = db_query($sql, 'findme', 'A');


?>

I think you get the idea. I like readability in SQL querys very much in my code.

You might also like