優柔不断なプログラマの日記

思いつきでポチポチ書きます

(SQL)SQL文の字下げルール

2007年08月24日 | プログラミング
業務システムのプログラムコーディングしていると言語を問わずSQL文を組み立てる場面があります。
私の周りではみんな独自色…もちろん私もその一人ですが。
それなりにルールがあれば良いのですが、全然お構いなしですごく見づらいケースもありますよね。

現在のマイルール

1.SELECTは単独記述。
・Oracle時代はヒント書く場所にしていたので、そのなごりかな。
・SELECTに続けて列を記述していた時期もある。

2.列を列挙する場合は1行1列。カンマは前に置く。
・コメントアウト時、列追加時に、該当行のみ変更で済むので。
・当然、先頭に列を追加する場合は複数行変更することになるが…経験上それは少数派。

3.予約後を大文字で記述する。
・これは最近。Oracle時代は全部大文字で書いていた。

4.真ん中に合わせる。
・SELECT、FROM、WHEREなどを右側で揃える。下記のサンプル参照。
SELECT
       tb1.column1
  FROM table1 tb1
 WHERE tb1.column3 = 123
 GROUP BY tb1.column1



5.テーブルは必ず別名。
・なにかの記事で、そうすることで僅かだがパフォーマンスが上がると見かけたので始めた。(多分パースが早くなるのかな)
・最近は単一テーブルのSELECTでも記述する。後になってJOINするケースはしばしばあるが、そのときに別名をつけると変更箇所が増える。

6.JOIN ~ ON の条件は左側にJOINするテーブルの列を記述。
・右側に記述すると固定値や別テーブル列を条件とする場合、美しくない。
・そもそも左側に比較対象を記述するっていうのがおかしいと思っている。
・最近はコードでも if ( 1 == destvalue ) …って書くのがいるけど許せない。間違って代入してバグの元になる?しませんって。
  FROM table1 tb1
  LEFT JOIN table2 tb2
       ON   tb2.column1 = tb1.column1
       AND  tb2.column2 = 1            /* OK! */
  LEFT JOIN table3 tb3
       ON   tb1.column1 = tb3.column1
       AND  1 = tb3.column2            /* 美しくない! */
  LEFT JOIN table4 tb4
       ON   tb1.column1 = tb4.column1
       AND  tb4.column2 = 2            /* 美しくない! */



(プログラミング)木を見て森を見ないデバッグ

2007年08月09日 | プログラミング
ある外注さんのソースをデバッグすることになりました。言語はPHP5です。
月次帳票用のSQL文を組み立てる部分です。

ソースを見る。コメントは一切ない。ぱっと見ると、まずシステム日付を取得しています。月次帳票だから、基準となる日付は必要ですよね。
次は…システム日付の「月」部分が1なら、「年」を1減して「月」を12に。「月」が1以外なら「月」を1減。つまり、前月を求めているようです。
次は…ifが大量に並んでいます。システム日付の「日」部分が1なら"01"、2なら"02"…以降9まで並んでいます。ついでに「月」部分も同様に1~9までif文が並んでいます。
なるほど、8月9日なら"0809"のように文字列を組み立てようとしているんですね。でもこの場合はsprintf('%02d', $day)これで済みますよね。まあ関数を知らないために、同じことをするロジックを作ることはしばしばあります。
ここは書き換えてしまいましょう。作者はif () の後の波カッコを改行してから記述するスタイルのようですので、if文1つあたり4行消費しています。「日」「月」それぞれ9個なので4行×9個×2種類で72行あったのがたったの2行になりました。
次回このソースを見る人も解読しやすいでしょう。いいことしたなー。

さて、問題のSQL部分の分析です。作者はあまり字下げしない人みたいです。でも気分次第では改行するらしいので非常に見づらい。
でもまあ、SQLの構造としてはシンプルですね…
それで…
えーと…
前段で作成した年月文字列変数どこでも使ってない………
せっかくわかりにくい日付編集処理を解読して修正したのに…さっきの充実感は吹っ飛び、ため息をつきながら直した部分のソースを全部消しました。
まあこんなこともしばしばあります。

無意味な連番をプライマリーキーにする意義

2007年08月06日 | プログラミング
自分が設計したわけではないのですが、“あるシステム”のテーブル設計はこんな感じになっています。(ちなみにDBMS=PostgreSQL)

CREATE TABLE m_shouhin
(
interid serial NOT NULL,
shouhin_code character varying(8) NOT NULL,
eda_code numeric(2) NOT NULL,
shouhin_name character varying(20),

CONSTRAINT m_shouhin_pkey PRIMARY KEY (interid),
CONSTRAINT m_shouhin_unqidx UNIQUE (shouhin_code, eda_code)
)

プライマリーキーは、データとは関連のない(意味の無い)連番にして、データとして意味のある部分は一意制約とするルールのようです。
ケチをつける訳ではないですが、この「無意味な連番をプライマリーキーにする」ってどう使って欲しいのでしょう。

やりたいことはきっと、レコードを更新する際にUPDATEのWHERE句が短くなるからかな??と予想。ある程度以上のデータ数になった場合、きっとWHERE interid=~と記述すれば最速だろうと思います。
開発も終盤となってきましたが、そのような記述はシステムの中でもごくごく一部のようです。(一部=私が意図的に使ったのみ)

以前私の作った小さなシステムでも連番使いました。MS-Accessで「オートナンバー」などという機能があり、使ってみました。
そこでは、「発行番号」という、本当に意味の無いただの連番が欲しかったので、そのままプライマリーキーとして利用。マッチしていました。

しかし“あるシステム”では、もれなくinteridがプライマリーキー、別の(データとして意味のある)フィールドが一意制約+NOT NULL制約になっています。
一意制約+NOT NULL制約 = プライマリーキー制約なのに…

PostgreSQLではUNIQUE制約はインデックスも作成される実装のようなので、パフォーマンス的には問題にならないのかもしれませんが。
全テーブルについている使えないinteridが正直目障り。あ、結局ケチつけちゃった。

円周率が3はウソなのか

2007年08月01日 | つぶやき
今さらなんですが。以前テレビなんかで騒いでましたよね。小学校では「円周率はおよそ3」と教えるらしいと。
そのときは、何で3なんだろうと、すごく戸惑いました。
3.141592…って習いましたよね。それが「およそ3」。。。
よく考えたら3.14って「およそ3」じゃないですか。すごく合理的。(とはちょっと違うか)

はじめっから、マジックナンバー「3.14」で習ったので、それが「およそ3」だということすら考えたことが無かったのです。
「そんな大まかじゃけしからん」みたいな意見もあったみたいですが、私としてはこの事実に気づけたことが純粋に嬉しかったのです。


(PHP)PHP5からの参照foreach

2007年08月01日 | プログラミング

PHP5からforeach($arr as &$value) と記述すると、配列要素の値を変更可能になっていますね。各要素に値を追加する場面で使用してみたのですが、なんかおかしい。最後の要素が書き換わるようです。 実験してみました。
テストソース1:
$test = array( 1, 2, 3, 4, 5);
foreach ($test as &$value) {}
foreach ($test as $value) {}
print_r($test);

結果1:
Array
(
    [0] => 1
    [1] => 2
    [2] => 3
    [3] => 4
    [4] => 4
)

最初のforeachの$valueの参照ポインタが残ってるみたいです。つまり最初のforeachを抜けた時点で、$test[4]のポインタを指している。($value = &test[4])
テストソース2:
$test = array( 1, 2, 3, 4, 5);
foreach ($test as &$value) {}
$value=99999;
print_r($test);

結果2:
Array
(
    [0] => 1
    [1] => 2
    [2] => 3
    [3] => 4
    [4] => 99999
)

うーん。理屈はなんとなく分かったけれど、意図しない結果を招きそうです。回避策を考えてみます。ポインタが残ってるようなので、クリアすればいいだろうということで。
テストソース3:
$test = array( 1, 2, 3, 4, 5);
foreach ($test as &$value) {}
unset($value);
$value=99999;
print_r($test);

結果3:
Array
(
    [0] => 1
    [1] => 2
    [2] => 3
    [3] => 4
    [4] => 5
)

まあ、こんなところもPHPか。