【SQL入門】読みやすく書きやすく安全なSQLを作るためのポイント

データベース
スポンサーリンク

SQLは構文の制約が非常に少なく、書き方の自由度が高い言語です。
長いSQLを1行でまとめて書くこともできますし、任意の位置に改行やインデントを置いて、視覚的に読みやすく整形することも可能です。

このような特徴から、SQLは書く人ごとにインデントの入れ方や改行の位置などの書き方に対する「ルール」が自然と生まれていきます。
また、複数人でシステム開発を行う場合は、プロジェクトごとに共通の「書き方ルール」を設けることも一般的です。

私も仕事柄、業務でSQLを書く機会が多いのですが、SQLのインデントや改行位置などの書き方には自分なりの「ルール」があります。

今回の記事では、SQLを読みやすく書きやすくし、且つ安全に実行できるようにするための、私の個人的な「SQLの書き方ルール」を紹介していきます。

SQL初心者の方は参考にしてください。

 

インデントの入れ方や改行位置など

まず「インデント」について紹介していきます。

「インデント」とは「字下げ」とも呼び、「行頭に空白を設けて文字開始位置を他行よりも下がった位置から始めた文字組み」を指す用語です。

SQLにおいて、適切にインデントを組み込むことで、記述された処理を視覚的に構造化することができます。

読みやすく書きやすく安全なSQL」を作るにあたって、もっとも大事なポイントです。

また、SQLでは改行も柔軟に使用することができます。
この改行を入れる入れない、入れる場合はどこに入れるかなども、「読みやすく書きやすく安全なSQL」を作るうえで重要です。

次項から、SELECT文におけるSQLのインデントの入れ方や改行位置の例を複数のパターンで紹介していきます。

 

SELECTやFROMなどの基本句は先頭に寄せる

SQLでSELECT文を作る際に、構造上インデントを入れる箇所とインデントを入れずに左に寄せる(空白を設けない)箇所を分けていきます。

当記事では、以下の句をSQLの「基本句」と呼称して区別していきます。

説明
SELECT句 取得する列(カラム)を指定する
FROM句 どのテーブルからデータを取得するかを指定する
WHERE句 行の絞り込み条件を指定する
GROUP BY句 行のグループ化する条件を指定する
HAVING句 グループ化された行の絞り込み条件を指定する
ORDER BY句 取得した表の並び替え条件を指定する

これらの基本句は、必ずSQL構文上左側に余白を設けず、左寄せをし、これらの基本句の配下にぶら下がる「列名」や「テーブル名」、条件文などは改行して基本句と行を分けた後、インデントを設けて、基本句の配下にあることを視覚的に識別しやすくします。

具体的には以下のような書き方です。

  -- 改行やインデントを設けないSQL例
  SELECT * FROM table1 WHERE column1 IS NOT NULL;
  
  -- 改行やインデントを適切に設けたSQL例
  SELECT
      *
  FROM
      table1
  WHERE
      column1 IS NOT NULL;

当項の内容は、SQLを作るうえで基本とする書き方のルールとしてください。

 

列名を列挙する場合のカンマは前に置く

前項のSQL作成例では、SELECTで取得する列を *(アスタリスク) で省略していますが、個々の列名を指定して取得する場合は、個々の列を ,(カンマ)で区切ります。
列名ごとに改行を行う場合、カンマは列の前に置く場合と後ろに置く場合の二通りの記述方法があります。

例えば以下のような書き方です。

  -- SELECT句配下の取得列を改行しないSQL例
  SELECT column1,column2,column3
  FROM table1;
  
  -- 列ごとに改行し、カンマを後ろに置いた場合のSQL例
  SELECT
      column1,
      column2,
      column3
  FROM
      table1;
  
  -- 列ごとに改行し、カンマを前に置いた場合のSQL例
  SELECT
      column1
      ,column2
      ,column3
  FROM
      table1;

カンマを列名の前に置くか後ろに置くかについては、SQLの構文的にはどちらでも問題はありませんが、SQLの保守性を考慮した場合、一般的には列名の前に置くことが推奨されています。
カンマを後ろではなく、前に置いたほうが良い理由としては以下があります。

カンマを前に置くべき理由

  • カンマの位置が縦に揃うため、矩形選択などの編集がやりやすい
  • 列の並び替えや追加、削除時にミスが起こり難い
  • 列の入れ替え時などに各行が独立した変更単位にしやすい

カンマを後ろに配置すると、列名によってカンマの位置はバラバラになるため、テキストエディタでSQLを効率よく整形しようとする場合に若干面倒になります。

また、列の並び替えや追加、削除などをする際に、最終列の後ろにカンマが残り、それに気付かないままSQLを実行しようとして構文エラーになるようなミスは起こりがちです。

また、列の入れ替えをするようなケースでも、カンマが後ろにあると、入れ替え対象の行以外にも編集を伴う場合があり、編集箇所の差分を取るような場合は、編集した箇所がわかり難くなります。

これらの理由により、カンマは列の前に置くことを当記事では推奨しています。

 

列名を列挙する場合は必ず改行を入れる

前項のSQL例では、SELECT句内の列名を列挙する箇所で、列ごとに改行を入れています。
列名を列挙する場合、カンマで列を区切ったうえで、1行ですべての列を取得するように記述することもできますが、改行を入れずに1行で列を列挙してしまうと、以下の問題があります。

列の列挙時改行をしない場合の問題

  • 列の入れ替えが非常に面倒
  • 列の入れ替え時の差分がわかりにくい
  • 列の個数や種類、順番を視覚的に識別しにくい

例えば以下のような書き方になります。

  -- 列ごとの改行をしない場合のSQL例
  SELECT column1,column2,column3
  FROM table1
  ORDER BY column1,column3 DESC;
  
  -- 列ごとに改行を入れた場合のSQL例
  SELECT
      column1
      ,column2
      ,column3
  FROM
      table1
  ORDER BY
      column1
      ,column3 DESC;

尚、列の列挙は、SELECT句のなかだけではなく、GROUP BY句やORDER BY句でも同じように発生します。
よって、列の列挙時に列ごとに改行を入れた方が良いのは、GROUP BY句やORDER BY句でも同様です。

特に、ORDER BY句では、指定した列名が前に来るほど、昇順または降順として優先すべき列として扱われます。
よって、どの列が並び順の最優先として扱われているかを視覚的にわかりやすくすることは非常に重要です。

 

結合時は改行してJOIN句を置き、ON句にはインデントを入れる

SQLでは内部結合(INNER JOIN句)や外部結合(OUTER JOIN句)を使用して、テーブル同士を結合することができます。
複雑なSQLでは、複数のテーブルを結合し、更に結合したテーブルを入れ子する場合もあり、結合処理を如何に見やすく書くかは、SQLの書き方における重要な要素です。

この結合時のインデントの入れ方や改行位置については、SQLを書く人によって個性が出ます。

当記事で推奨する書き方は以下です。

  • JOIN句の同じ行内にテーブル名を書く
  • JOIN句の行に改行し、インデントを入れたうえでON句を書く
  • ON句内で結合条件をANDで指定する場合は条件ごとに改行する

例えば以下のような書き方になります。

  -- 見やすい結合処理の書き方を考慮しない場合のSQL例
  SELECT table1.column1,table1.column2,table1.column3
  FROM table1 INNER JOIN table2;
  
  -- 見やすい結合処理の書き方をした場合のSQL例
  SELECT
      table1.column1
      ,table1.column2
      ,table1.column3
  FROM
      table1
      INNER JOIN table2
          ON table1.column1 = table2.column1
          AND table2.column2 IS NOT NULL;

 

サブクエリは括弧の位置を合わせる

SQLではFROM句やWHERE句のなかなどで、更に別のSELECT文を作成して、その導出表を入れ子することができます。
これを「サブクエリ」と呼びます。

サブクエリを使用する場合、入れ子するSELECT文を括弧で括る必要がありますが、その括弧をどこに置くか、入れ子したSELECT文のインデントや改行をどのように書くかについても、SQL全体の見やすさに大きく影響を与えます。
サブクエリをどのように書くかも、人によって色々ありますが、私の場合は以下のような書き方をする場合が多いです。

  • 括弧の始めと終わりの位置を合わせる
  • 改行を入れて括弧のみの行を作る
  • サブクエリ内のSELECT文の改行やインデントもメインクエリと合わせる

例えばサブクエリをFROM句内で使用する場合は以下のような書き方になります。

  -- 見やすいサブクエリの書き方を考慮しない場合のSQL例
  SELECT table1.column1,table1.column2,table1.column3
  FROM table1 
  INNER JOIN (SELECT * FROM table2 WHERE column2 IS NOT NULL) subTable1
  ON table1.column1 = subTable1.column1;
  
  -- 見やすいサブクエリの書き方をした場合のSQL例
  SELECT
      table1.column1
      ,table1.column2
      ,table1.column3
  FROM
      table1
      INNER JOIN
          (
              SELECT
                  *
              FROM
                  table2
              WHERE
                  column2 IS NOT NULL
          ) subTable1
          ON table1.column1 = subTable1.column1;

サブクエリをWHERE句内で使用する場合は以下のような書き方になります。

  -- 見やすいサブクエリの書き方を考慮しない場合のSQL例
  SELECT column1,column2,column3
  FROM table1
  WHERE column1 IN (SELECT column1 FROM table2 WHERE column2 IS NOT NULL);
  
  -- 見やすいサブクエリの書き方をした場合のSQL例
  SELECT
      column1
      ,column2
      ,column3
  FROM
      table1
  WHERE
      column1 IN
          (
              SELECT
                  column1
              FROM
                  table2
              WHERE
                  column2 IS NOT NULL
          );

最初と最後の括弧の位置を合わせることで、サブクエリの範囲を明確にしたいという意図により上記のように書いていますが、一般的には、最初の括弧を分離せず前の句と同一行内に書き、終わりの括弧の位置にも特別なルールを設けない書き方をされる場合も多いです。
これが正解という決まりはないため、この辺りはご自身が使いやすい方法で書いてください。

  -- サブクエリの最初の括弧を分離せず、前の句と同一行に書くSQL例
  SELECT
    column1
    ,column2
    ,column3
  FROM
    table1
  WHERE
    column1 IN (
            SELECT
                column1
            FROM
                table2
            WHERE
                column2 IS NOT NULL
    );

 

CASE式ではCASEとENDの位置を揃える

データの値によって条件分岐をさせる場合はCASE式を使います。
CASE式では、CASEとENDで囲まれたブロック内で、WHEN、THEN、ELSEを使用して分岐処理を記述します。

CASE式も条件分岐で使用されることから、適切な改行位置やインデントで見やすくすることは非常に重要です。

CASE式では以下のようなルールを推奨します。

  • CASEとENDの位置を揃える
  • WHEN, THEN, ELSE をインデントして同じ位置を揃える
  • CASEをネストする場合はさらにインデントを入れる

具体的には以下のように記述します。

  -- CASE式に適切な改行やインデントを入れたSQL例
  SELECT
    column1
    ,column2
    CASE
        WHEN column3 = 'abc' THEN '0'
        WHEN column3 = 'def' THEN '1'
        ELSE '9'
    END case_sample
  FROM
    table1;
  
  -- CASE式をネストする場合のSQL例
  SELECT
    column1
    ,column2
    CASE
        WHEN column3 = 'abc' THEN '0'
        WHEN column3 = 'def' THEN '1'
        WHEN column3 = 'hij' THEN
            CASE
                WHEN column4 = 'klm' THEN '10'
                WHEN column4 = 'nop' THEN '11'
                ELSE '20'
            END
        ELSE '99'
    END case_sample
  FROM
    table1;

 

WHERE句は1行1条件とする

SQLではWHERE句でデータを絞り込みますが、単一の条件で絞り込むより、複数の条件を組み合わせて絞り込む場合が多いです。
その場合の改行位置やインデントも統一感を持った書き方で徹底することで、読みやすさを向上し、ミスを未然に防ぐことができます。

WHERE句内では、AND や OR などで使って複数の条件を1行で書くこともできますが、WHERE句内の条件部分はインデントを入れて、且つ条件ごとの改行を入れることで見やすくなります。

具体的には以下ように記述します。

  -- WHERE句内の条件を1行で書いた場合のSQL例
  SELECT
    column1,
    column2,
    column3
  FROM
    table1
  WHERE column1 >= 100 AND column2 = 'abc';

  -- WHERE句内の条件にインデントを入れて、条件ごとに改行した場合のSQL例
  SELECT
    column1,
    column2,
    column3
  FROM
    table1
  WHERE
    column1 >= 100
    AND column2 = 'abc';

また、条件に OR を組み合わせ場合は、併せて括弧で条件を囲むことも多いですが、その場合も条件の構造がわかりやすいように記述するべきです。

括弧で囲んだ OR 条件が比較的簡単な内容であれば、括弧部分だけは一行でまとめたほうが、直感的に構造を認識しやすいでしょう。
括弧で囲んだ OR 条件が複雑な場合は、その括弧部分も改行及びインデントを入れたうえで、条件ごとでも改行を入れた方が、構造がわかりやすいと思います。

例えば以下のような書き方になります。

  -- WHERE句内の条件に簡単なORを組み合わせた場合のSQL例
  SELECT
    column1,
    column2,
    column3
  FROM
    table1
  WHERE
    column1 >= 100
    AND (column2 = 'abc' OR column2 = 'def')
    AND column3 IS NULL;

  -- WHERE句内の条件に複雑なORを組み合わせた場合のSQL例
  SELECT
    column1,
    column2,
    column3
  FROM
    table1
  WHERE
    column1 >= 100
    AND 
       (
         column2 = 'abc'
         OR column2 = 'def'
         OR column2 = 'ghi'
       )
    AND column3 IS NULL;

人によってどのような形式が見やすいかは様々であり、上記の書き方が正解とは言えませんが、重要なのは、インデントや改行位置を適切に工夫し、どの条件同士が AND で、どこからどこまでが OR なのかを一目でわかるようにすることです。

 

その他のルールやポイント

前項では、主にSQLを書くにあたってのインデントや改行位置に着目して、推奨する書き方を紹介しましたが、当項では、インデントや改行位置以外のおすすめしたいルールや書き方のポイントを紹介していきます。

 

予約語・キーワードは常に大文字を使用する

標準的なSQLでは、アルファベットの大文字と小文字を区別しません。
よって、「select * from table1」と「SELECT * FROM table1」のどちらで書いても動作します。

しかし、この大文字と小文字も適切に使い分けることで、SQLの読みやすさは大きく変わり、より安全に扱えるようになります。

一般的にも推奨されていることが多いですが、当記事でも「SQLにおける予約語・キーワードは常に大文字を使用する」ことを推奨します。
尚、ここで呼称した「予約語・キーワード」とは、SELECT 、FROM 、INSERT 、UPDATEなどの基本句や、LEFT 、SUM 、MAX 、などの組み込み関数などを指します。

予約語・キーワードを大文字で統一することによるメリットは以下です。

  • 予約語・キーワードが目立ち可読性が向上する
  • カラム名やユーザー定義関数名などとも区別でき構文エラーに気付きやすくなる
  • シンプルなルールであり、複数名で開発する場合でもルール化しやすい

予約語・キーワードを常に大文字を使用する場合とそうでない場合のSQL例を以下で掲載します。

  -- 予約語・キーワードを小文字で書いた場合のSQL例
  select
    column1
    ,column2
    case
        when column3 = 'abc' then '0'
        when column3 = 'def' then '1'
        else '9'
    end case_sample
  from
    table1;

  -- 予約語・キーワードを大文字で統一した場合のSQL例
  SELECT
    column1
    ,column2
    CASE
        WHEN column3 = 'abc' THEN '0'
        WHEN column3 = 'def' THEN '1'
        ELSE '9'
    END case_sample
  FROM
    table1;

どちらでも読めるとは思いますが、やはり予約語・キーワードを大文字で統一したほうが、より読みやすいのではないでしょうか。

 

常にORDER BY句で並び順を指定する

SQLのORDER BY句を使用することで、SELECTで取得した行の並び順を指定することができます。

ORDER BY句で明示的に並び順を指定しない場合、単一のテーブルを使用したシンプルなSELECT文であれば、通常は取得した行の主キーでデータが並びます。

但し、複数のテーブルを参照するようなSQLの場合、データベースのオプティマイザがそのSQLをどう解釈してどう実行するのかによって、取得したデータの並び順も変わってきます。

要するに、ORDER BY句で明示的にデータの並び順を指定しない限り、データベースが返すデータの並び順は保証されません

場合によっては、同じSELECT文を同じデータに対して繰り返し実行した結果、毎回異なる並び順でデータが返ってくるケースもあり得ます。

SELECT文を実行して、意図しない並び順になっていると気付いてからORDER BY句を書き足すのではなく、初めからどのような並びでデータを取得したいかも想定し、明示的にORDER BY句で並び順を指定しておくことは、「安全なSQL」という観点ではとても重要です。

SELECTした行に対して、人は意図した順番で並んでくれていると勝手に思い込みがちです。
この思い込みがトラブルを生む原因になります。

よって、このようなトラブルを未然に防ぐためにも、SELECT文を書く際には、できるだけ初めからORDER BY句も含めて書くようにすることをオススメします。

 

RIGHT OUTER JOINは使わない

標準的なSQLのテーブル結合では、内部結合用に INNNER JOIN、外部結合用に、LEFT OUTER JOIN、RIGHT OUTER JOIN、FULL OUTER JOIN があります。

RDBはデータの重複を排除し、複数のテーブルで効率よくデータを管理するためのアーキテクチャになっています。
よって、JOIN句を使用して複数のテーブルを結合しながら利用することになるのですが、このJOIN句にも上記のように種類があり、テーブルの結合用途によってJOIN句を使い分けていきます。

このJOIN句の種類でも、必ず使えるようにしておくべきものと、敢えて使わないほうが良いものがあります。

まず、必ず使えるようにしておくべきJOINの種類は以下です。

  • INNER JOIN:内部結合
  • LEFT OUTER JOIN:左外部結合

INNER JOIN は テーブル結合における基本の構文であり、結合元、結合先で結合条件に合致する行のみを返します。
また、LEFT OUTER JOIN は結合元は全行を返し、結合先は結合条件に合致しない場合はNULLを返します。

SQLにおいてテーブル結合をする場合、この INNER JOIN と LEFT OUTER JOIN の二種類だけ習得しておけば充分です。

尚、「FULL OUTER JOIN:完全外部結合」は特殊な結合であり、結合元、結合先の全行を取得し、結合条件を満たす場合は結合し、条件に合致しない場合はそれぞれのテーブルのNULL行を返します。
個人的には殆ど使用したことがありません。
よって、もしこれからSQLを習得しようと考えている人の場合、FULL OUTER JOIN は覚えなくて結構です。

後、「RIGHT OUTER JOIN:右外部結合」は、結合先の全行を返し、結合元では結合条件に合致しない場合にNULLを返す結合であり、前述した LEFT OUTER JOIN と主従関係が逆になります。

LEFT OUTER JOIN の場合、左側:結合元テーブル → 右側:結合先テーブル という並びでSQLを書くことになり、直感的にテーブル同士の関係性が理解し易いですが、RIGHT OUTER JOIN は、右側に結合元テーブルがあり、左側に結合先テーブルが来るイメージです。
右から左への結合方向は直感的とは言いにくく、テーブル同士の関係性が一気に識別し難くなります。

そもそも、LEFT OUTER JOIN と RIGHT OUTER JOIN は、結合時のテーブルの向き先が逆になっただけであり、SQLでテーブルを結合する順番を適切に記述していれば、RIGHT OUTER JOIN を使う必要性はなくなり、LEFT OUTER JOIN 一つ覚えておけば事足ります。

よって、読みやすい書きやすく安全なSQLを作ろうとした場合、RIGHT OUTER JOIN は一切に使用しないようにして、外部結合が必要な場合は、LEFT OUTER JOIN で統一してください。

私自身、IT技術者として昔からSQLは書いていますが、RIGHT OUTER JOIN を使ったことは数えるほどしかありません。

FULL OUTER JOIN や RIGHT OUTER JOIN がどのような結合方式なのかを概要レベルで理解しておくことは重要ですが、実際にSQLを書く際には、当記事で紹介したように、INNER JOIN か LEFT OUTER JOIN だけを適切に使い分けてください。

 

結合する場合はテーブルの別名も必ず定義する

当項の見出しでは「結合する場合」と用途を限定していますが、結合の有無に関わらず標準的なSQLでは、テーブル名やカラム名に対して別名を定義することができます。

別名を定義する場合は、AS句 を使用します。

テーブル名やカラム名に対して別名を定義することで、SQL自体の可読性が向上するのはもちろんのこと、SQLを書く際の速度や効率も大きく向上します。

特にテーブルをJOIN句で結合して使用する場合は、結合したテーブル同士に同じカラム名があれば、どちらのテーブルのカラムかをSQL文内で指定する必要があり、別名を活用することによりメリットはより大きいと言えます。

例えば、別名を使用せずに作成した結合を含むSQLと、別名を利用したSQLの例を以下で記載します。

  -- 別名をテーブルに定義せずに結合した場合のSQL例
  SELECT
      table1.column1
      ,table1.column2
      ,table2.column3
  FROM
      table1
      INNER JOIN table2
          ON table1.column1 = table2.column1
          AND table2.column2 IS NOT NULL;

  -- 別名をテーブルに定義して結合した場合のSQL例
  SELECT
      t1.column1
      ,t1.column2
      ,t2.column3
  FROM
      table1 AS t1
      INNER JOIN table2 AS t2
          ON t1.column1 = t2.column1
          AND t2.column2 IS NOT NULL;

テーブルに別名を定義して作成したSQLの場合、SELECT句配下で省略したテーブル名を指定してカラムを列挙しており、見た目もすっきりしているかと思います。
別名を使用せずに結合を含むSQLを書いた場合、参照しているテーブル名が長くなるほど、SQL全体が見辛くなります。

また、別名を利用することは、SQLの読みやすさを改善するだけではなく、SQLを書く際の作業効率の向上や、タイプミスの軽減にも有効です。

データベース接続ツールに組み込まれているSQLエディタ機能の多くでは入力補完機能が利用できます。

例えば、先にFROM句以降を書いておき、後からSELECT句の中身を書く際に、上記のSQL例で言えば、「t1.」と入力することで、別名t1で定義されたテーブルが持つカラム名のリストが画面上に表示されます。
カラム名をタイピングしなくても済むため、文字を入力する手間を軽減し、且つタイプミスを無くせます。

この入力補完機能は、テーブル名を別名で定義しないと利用できないわけではなく、テーブルの実名を入力してもリストは表示されます。
例えば、上記SQL例の場合であれば、「table1.」と入力することで、別名を指定した場合と同様にカラム名のリストが表示されます。

但し、省略した別名を入力して、入力補完機能を呼び出すのと、長い実名のテーブル名を入力して入力補完機能を呼び出すのでは、SQLを書く際の入力効率が大きく異なります。

よって、特に上記の例のように、結合を伴うSQLを書く場合は、必ずテーブルに別名を定義しておくことをおススメします。

因みに私の場合は、書くSQLが使い捨てで再利用の予定もないのであれば、テーブルに指定する別名は、a とか b など簡単にアルファベット1文字で指定し、何らかのプログラムに組み込む場合や、今後の再利用を予定しているSQLであれば、そのテーブル名の略称などを別名に指定しています。

 

UPDATE文は改行せず1行で書く

UPDATE文を書く場合も、見やすさを優先する場合は、これまでの記事で書いたように、適切な位置で改行やインデントを入れることになりますが、UPDATE文は対象行を一気に更新する処理になるため、時には見やすさよりも安全性を重要視した方が良いケースもあります。

例えば、改行やインデントを入れて、見やすくUPDATE文を書いた場合は以下のようになります。

  -- 改行やインデントを使用して見やすく整形したUPDATE文のSQL例
  UPDATE
      table1
  SET
      column3 = 'xyz'
  WHERE
      column1 >= 100
      AND column1 < 200;

改行やインデントで見やすくなっており、一見問題はないように思えますが、UPDATE文を扱う場合に意識していただきたいのは、UPDATE文の場合、仮にWHERE以降が欠落したSQLであっても、それが構文エラーにならない限り実行できてしまうところです。

一般的なデータベース接続ツールでは、SQLエディタ画面内のSQL文字列を選択した状態にしたままSQLを実行すると、選択中の文字列部分だけを使用してSQLを実行します。
よって、上記のUPDATE文の例で言えば、何らかの作業の都合により、UPDATE句とSET句までを選択状態にし、WHERE句を未選択状態にしたままでSQLを実行してしまった場合、table1は全行更新されることになります。

例えば、SQLを作成する際に、具体的には以下のような作業を行っていたとします。

  1. データベース接続ツールのSQLエディタ内でUPDATE文を作る。
  2. 作成したUPDATE文を流用して別のUPDATE文を作る為に、WHERE句を除いたSQL文字列のみ選択してコピーする。
  3. SQLエディタウィンドウを新しく開くために、メニュー内の「新規クエリ」を押下する。
  4. 新しく開いたSQLエディタ画面内にコピーしたUPDATE文をペーストし、必要な処理を追記する。

上記の作業の流れのなかで重要なのは、二つ目の作業時に一部のSQL文字列をコピーする目的で選択状態にしたまま、三つ目の作業で「新規クエリ」ボタンを押下しています。
この三つ目の作業で操作を誤り、SQLの「実行」ボタンを押下してしまうことも起こり得ます。

例えば、Microsoftの「SQL Server Managemet Studio(SSMS)」のスクリーンショットを例にしますが、メニューの構成によっては、「新規クエリ」ボタンと「実行」ボタンが非常に近く、間違えて押してしまうことも十分考えられます。

不完全なSQLを誤って実行してしまう例

そのデータベース接続ツールのトランザクションの設定や仕様によっては、UPDATE文は即座に実行され、意図せず全行が更新されてしまいます。
上記の画像であげたSSMSでも、既定のトランザクション設定は即時実行です。

また、別の例では、アプリケーション開発者がプログラム内にSQL文字列を組み込む場合、以下のように記述するケースがあります。※VBAでの例

  Dim strSQL As String
  Dim intLimit As Integer

      intLimit = 200
  
  'エラーが発生しても無視させます。
  On Error Resume Next
      strSQL = "UPDATE "
      strSQL = strSQL & "     table1 "
      strSQL = strSQL & " SET "
      strSQL = strSQL & "     column3 = 'xyz' "
      strSQL = strSQL & " WHERE "
      strSQL = strSQL & "     column1 >= 100 "
      '数値変数に0除算でエラーを発生させる
      strSQL = strSQL & "     AND column1 <" & intLimit / 0
  
      Debug.Print strSQL

このコードを実行すると、エラーが発生した行の処理はまるっと省略されたSQL文がDebug.Printで出力されます。
本来のこのSQL文では、column1の下限と上限を指定し、その範囲内の行に対して更新を掛ける想定であり、その上限が条件に組み込まれないままSQLが実行されれば、やはり大惨事になります。

当項で提唱した「UPDATE文は改行せず1行で書く」ようにすることで、不完全なSQLを実行しようとした場合は、データベース側で構文エラーとして弾かれるようになり、不完全なまま実行されることはなくなります。

複雑な内容のUPDATE文を書かないといけないケースもあり、常に1行で書くことを徹底できない場合も当然ありますが、万が一不完全な状態で実行されたケースも想定し、改行を挟む場合に、例えばSET句と同じ行内にWHERE句の最初だけは含めるなど、最低でも全行更新が走らないような配慮はしておくと良いかと思います。

 

可能な限りUPDATE文のWHERE句では主キーを指定する

UPDATE文を作る際には、WHERE句で更新対象のデータを絞り込まず、対象のテーブル全行に対して更新を行うときもありますが、通常はWHERE句で更新対象の条件を指定します。

WHERE句内で指定した条件に合致する行を、SET句で指定した値で一括更新することになりますが、WHERE句で一定範囲の行を絞り込んで一気に更新を掛けるUPDATEを実行する場合、データベース経験をある程度積んでいたとしても、やはり万が一の事態が頭を過り、不安になったり怖くなったりします。

更新するデータ件数によっては、UPDATE文内でWHERE句を使用して広い範囲で行を絞り込んで更新を掛けるしか術がない場合もありますが、もし更新対象の行が数百件程度であれば、主キーをWHERE条件に指定したUPDATE(1SQLで1行更新)を数百件分作成し、それを一気に実行する方が安心です。

作業の流れとしては以下です。

  1. いったんSELECT文を作成し、更新対象となる行をすべて取得する。
  2. 取得した更新対象の行すべてをExcelなどに貼り付ける。
  3. 取得した更新対象の行の主キーをWHERE句内で指定したUPDATE文をExcel内で作成する。
  4. 更新対象の行数分作成したUPDATE文をデータベースに対して一気に実行する。

この手順における、ExcelでUPDATE文を作成する具体的な方法としては以下の画像を参考にしてください。

ExcelでSQLを一気に作成する作業イメージ

Excelに貼り付けられた更新対象の行を参照してUPDATE文を作る計算式を1行分だけ手作業で作成し、残りの行は計算式を入れたセルを全行分コピーするだけです。
仮に数百行分のUPDATE文があっても、個々のUPDATE文自体の更新対象は主キーを指定した1件のみなので、精神的な不安は小さくなります。

データを一括で更新する際にこの方法で行うことによるメリットは以下です。

  • 更新前の簡易的なバックアップ代わりになる
  • 更新作業のエビデンスとしても使える
  • 事前に1件だけ更新して異常がないか確認することもできる
  • 更新対象の行に異常があり更新が失敗した場合に対象行の特定が容易

数百件の行を更新するUPDATE文を1回実行するのと、1件のデータを更新するUPDATE文を数百回実行するのでは、前者の方が明らかに合理的であり効率的ですが、万が一のトラブルやミスも想定し、そういった事態を可能な限り未然に防ぐことも重要です。
データベースを使う場合は、これぐらい慎重に作業をするぐらいがちょうどよいと思っています。

 

最後に

当記事では、私がSQLを書く時に意識している書き方や、安全にSQLを実行するためのポイントを、SQL初心者向けの記事として簡単に紹介してみました。

ここまで色々と書き方の例を紹介してきましたが、SQLの書き方について正解はありません。
どんな書き方をしようが、SQLの構文上問題がなければ、データベースは渡されたSQLをコンパイルして実行してくれます。

今回の記事の内容についても、あくまで私の個人的な「書き方ルール」であり、「他の人はこういった意図でこういった書き方をするんだな」と知ってもらう程度の受け止め方で十分です。

自分以外のメンバーと共同で開発するようなプロジェクトのなかで書くSQLであれば別ですが、貴方がSQL初心者で、且つ貴方が単独で実行するSQLであれば、細かい書き方のルールなどをいちいち意識せず、自身の書きやすい書き方でどんどん書いてみてください。
すべてのプログラミングについて言えますが、技術を身に付け上達させるためには、1文字でも多くコードを書くことです。

SQLもどんどん書いて技術が身に付いていけば、自然と自身の「書き方ルール」が作られていきます。
その頃にはSQLの初心者も卒業していることでしょう。

ここまで長々と読んでいただきましてありがとうございました。
それでは皆さまごきげんよう!

タイトルとURLをコピーしました