MW211 EXIT

devlog
SQL/末端階層の前詰(2)
2015年07月06日
親列と子列のある表で、子列を前詰で作成したい場合(前詰にしたい場合)
┌───┐  ┌───┐
│親  子│→│親  子│
│--  --│  │--  --│
│ 1   3│  │ 1   1│
│ 1   4│  │ 1   2│
│ 2   6│  │ 2   1│
│ 2   8│  │ 2   2│
└───┘  └───┘
┌──────────────────────────────────────┐
│UPDATE 表                                                                   │
│    SET 子列 = 入力D.新子列                                                │
│    FROM 表 AS 出力D                                                       │
│        INNER JOIN (                                                        │
│            SELECT 親列,                                                    │
│                   子列,                                                    │
│                   RANK() OVER(                                             │
│                       PARTITION BY 親列                                    │
│                       ORDER BY 子列 ASC                                    │
│                   ) AS 新子列                                              │
│                FROM 表                                                     │
│        ) AS 入力D                                                         │
│          ON  入力D.親列 = 出力D.親列                                     │
│          AND 入力D.子列 = 出力D.子列;                                    │
└──────────────────────────────────────┘
分類:SQL
SQL/末端階層の前詰(1)
2015年07月05日
連番と親列のある表で、子列を前詰で作成したい場合(前詰にしたい場合)
┌──────┐  ┌──────┐
│連番  親  子│→│連番  親  子│
│----  --  --│  │----  --  --│
│   1   1   0│  │   1   1   1│
│   2   1   0│  │   2   1   2│
│   3   2   0│  │   3   2   1│
│   4   2   0│  │   4   2   2│
└──────┘  └──────┘
┌──────────────────────────────────────┐
│UPDATE 表                                                                   │
│    SET 子列 = 入力D.子列                                                  │
│    FROM 表 AS 出力D                                                       │
│        INNER JOIN (                                                        │
│            SELECT 連番,                                                    │
│                   RANK() OVER(                                             │
│                       PARTITION BY 親列                                    │
│                       ORDER BY 連番 ASC                                    │
│                   ) AS 子列                                                │
│                FROM 表                                                     │
│        ) AS 入力D                                                         │
│          ON  入力D.連番 = 出力D.連番;                                    │
└──────────────────────────────────────┘
分類:SQL
SQL/中間階層の前詰
2015年07月04日
大列、中列、小列のある表で、中列を前詰にしたい場合
┌─────┐  ┌─────┐
│大  中  小│→│大  中  小│
│--  --  --│  │--  --  --│
│A    3   1│  │A    1   1│
│A    3   2│  │A    1   2│
│A    5   1│  │A    2   1│
│A    5   2│  │A    2   2│
└─────┘  └─────┘
┌──────────────────────────────────────┐
│UPDATE 表                                                                   │
│    SET 中列 = 入力D.新中列                                                │
│    FROM 表 AS 出力D                                                       │
│        INNER JOIN (                                                        │
│            SELECT 大列,                                                    │
│                   中列,                                                    │
│                   RANK() OVER(                                             │
│                       PARTITION BY 大列                                    │
│                       ORDER BY 中列 ASC                                    │
│                   ) AS 新中列                                              │
│                FROM (                                                      │
│                    SELECT DISTINCT 大列,                                   │
│                                    中列                                    │
│                        FROM 表                                             │
│                ) AS 中D                                                   │
│        ) AS 入力D                                                         │
│          ON  入力D.大列 = 出力D.大列                                     │
│          AND 入力D.中列 = 出力D.中列;                                    │
└──────────────────────────────────────┘
分類:SQL
SQL/初心者が犯しがちなミス
2015年06月19日
・TRUCATE TABLE文ではなく、DELETE文で全件削除
  →ロールバックに備えながら処理をするのでものすごく遅い
・相関副問い合わせではなく、内部結合で行抽出
  →抽出が先に行われない場合があり、ものすごく遅くなる
・インデックスを張らない
  →ものすごく遅い
分類:SQL
SQL/SQLインジェクション
2015年06月16日
┌──────────────────────────────────────┐
│SELECT 列 FROM 表 WHERE 検索列 = '■';                                      │
└──────────────────────────────────────┘
上記のようなSQL文を用意しておいて、画面から■の部分を入力してもらい、
任意の検索を行う場合を例にする。

以下のような値(文字列)を入力された場合…。
┌──────────────────────────────────────┐
│' OR 1 = 1;--                                                               │
└──────────────────────────────────────┘
そのままはめ込むと以下のようになってしまい(「--」以降はコメントとなる)
┌──────────────────────────────────────┐
│SELECT 列 FROM 表 WHERE 検索列 = '' OR 1 = 1;--';                           │
└──────────────────────────────────────┘
OR条件の「1 = 1」が如何なる時も成立してしまい全件ヒットする。

これが基本的なSQLインジェクション手法だ。

じゃ、「'」を「\'」に置換して、単なる文字にしてしまえばよいか?
┌──────────────────────────────────────┐
│SELECT 列 FROM 表 WHERE 検索列 = '\' OR 1 = 1;--';                          │
└──────────────────────────────────────┘
単なる「\' OR 1 = 1;--」という文字列で検索しにいくので成功!

それならこれでどうだ!
┌──────────────────────────────────────┐
│\' OR 1 = 1;--                                                              │
└──────────────────────────────────────┘
置換しなければ前出と同じなのだが、置換してしまうと以下のようになってしまう。
┌──────────────────────────────────────┐
│SELECT 列 FROM 表 WHERE 検索列 = '\\' OR 1 = 1;--';                         │
└──────────────────────────────────────┘
「'\\'」の部分が「\」という文字を意味することになり、
「1 = 1」が如何なる時も成立してしまう

ちなみに「'」を「''」に置換する場合も以下の通り。
┌──────────────────────────────────────┐
│SELECT 列 FROM 表 WHERE 検索列 = '\'' OR 1 = 1;--';                         │
└──────────────────────────────────────┘
こちらは「'\''」が「'」という文字を意味するが他は同じ。全て成立してしまう。

そんなに簡単な話ではないのだ。
分類:SQL
DB設計/年齢と誕生日に見るDB設計
2015年06月13日
【管理】
  ・「誕生日」から「年齢」を計算できるが、
    「年齢」からは「誕生日」を逆算できない
    よって、管理したいのは「誕生日」
    →値の価値を見極めること
【計算】
  ・「誕生日」と現在日から「年齢」を算出できるが、
    都度やっていては手間がかかり反応が遅い
    (あらかじめ算出しておいた)「年齢」を直接もっていれば速い
    但し、「年齢」は日々変わるので日次バッチ処理にて再計算する必要あり
    →CPUの性能向上により現実には気にならない差異ではあるが
      件数が膨大な場合、計算量が多い場合には馬鹿にならない
  ・「満年齢」なら日次処理が必要だが、「数え年」なら(元旦の)年次処理で済む
    →効率を優先しての前提条件の変更も選択肢としてはある
【入手】
  ・情報を入手しやすいのは「年齢」
    #例えば新聞記事においては、たまに関係者の「年齢」の記載があるが、
      「誕生日」まで記載されているのは稀
    →現実問題として、できること(入手できる情報)は限られている
  ・「年齢」とそれが公表された日を記録しておけば、
    ある程度「誕生日」を絞り込むことができる(生年など)
    →時には付加情報も管理すべし
  ・「年齢」と「公表日」の対の情報は複数に渡る場合も予想される
    →正規化の必要性
  ・「年齢」と「公表日」の情報は必須ではないし、ない場合が多い
    #「誕生日」がわかっていれば、「年齢」や「公表日」はそもそもいらない
    →表形式の限界(表の分離も必要)
分類:SQL
SQL/COUNT()の勘違い
2015年04月11日
昔、「COUNT()」は、種類の件数をカウントするものだと勘違いしていた。

┌──────────────────────────────────────┐
│SELECT COUNT(*) FROM [表];                                                  │
└──────────────────────────────────────┘
つまり、上記みたいな場合いっぱい件数があっても、
下記みたいな場合、「2件」になるという勘違いだ。
┌──────────────────────────────────────┐
│SELECT COUNT([性別]) FROM [表];                                             │
└──────────────────────────────────────┘

これは、こうしたらそのように(「2件」)になる。
┌──────────────────────────────────────┐
│SELECT COUNT(DISTINCT [性別]) FROM [表];                                    │
└──────────────────────────────────────┘

つまり、「COUNT()」は、NULL以外の件数をカウントするものなのだ。

というわけで、以下はいずれも全件数を取得できるというわけだ。
┌──────────────────────────────────────┐
│SELECT COUNT(*) FROM [表];                                                  │
├──────────────────────────────────────┤
│SELECT COUNT([主キー]) FROM [表];                                           │
├──────────────────────────────────────┤
│SELECT COUNT([複合主キーの一部列]) FROM [表];                               │
├──────────────────────────────────────┤
│SELECT COUNT([非NULL項目]) FROM [表];                                       │
└──────────────────────────────────────┘
分類:SQL
SQL/日時範囲と日付範囲の混同
2015年03月25日
「WHERE 日付 <= '2015-12-25'」の場合は、「2015-12-25」のデータは抽出される。

でも…
「WHERE 日時 <= '2015-12-25'」の場合は、「2015-12-25」のデータは除外される。

この点に注意。

なぜなら、上記は「WHERE 日時 <= '2015-12-25 00:00:00'」と同じ。
「00:00:01」以降のデータは除外されてしまうのだ

なので、「WHERE 日時 <= '2015-12-25 23:59:59'」とするか
「WHERE 日付変換(日時) <= '2015-12-25'」とする必要あり。

「日付変換(日時)」については、
MSSQLの場合は「CONVERT(date, 日時)」みたいな感じで。
分類:SQL
SQL/相関副問い合わせは速い
2015年02月22日
だいたい速い。

結果は同じでも以下の二つの経路があったとする
・レコードを絞り込んで、それについてのみ計算を行う
・全て計算を行った上で、レコードを絞り込む

一つの計算あたり複雑で負荷がかかる場合、断然前者の方が速い。
なんといっても、日の目を見ない計算をハナからやらないのだから。

例えば、その複雑な計算を全件について行う[重いVIEW]というのがあった場合、
必要なデータである[抽出D]についてだけ結果が欲しいとすると、
単純に以下の様に内部結合を行う。
┌──────────────────────────────────────┐
│SELECT *                                                                    │
│    FROM [重いVIEW]                                                         │
│        INNER JOIN [抽出D]                                                 │
│          ON [重いVIEW].[キー] = [抽出D].[キー];                           │
└──────────────────────────────────────┘
この場合、「全て計算してから抽出する」パターンになりがちだ。
(「INNER JOIN」の順番を逆にしても同じ)

WHERE文を明示的に指定する、例えば以下のような場合は
「抽出してから計算する」パターンにもっていきやすい。
┌──────────────────────────────────────┐
│SELECT *                                                                    │
│     FROM [重いVIEW]                                                        │
│     WHERE [キー] = (SELECT TOP 1 [キー] FROM [抽出D]);                    │
└──────────────────────────────────────┘
ただ、これだと、一件しか対応できない。

で、これを複数件でも耐えうる形にするのが相関副問い合わせだ。
┌──────────────────────────────────────┐
│SELECT *                                                                    │
│    FROM [重いVIEW]                                                         │
│    WHERE EXISTS (                                                          │
│              SELECT *                                                      │
│                  FROM [抽出D]                                             │
│                  WHERE [抽出D].[キー] = [重いVIEW].[キー]                 │
│          );                                                                │
└──────────────────────────────────────┘
相関副問い合わせ自体が速いということではないらしいのだが
手続きの順番が抽出優先になるという点で、相関副問い合わせは速く感じる。

つまり、相関副問い合わせは難しげだがマスタすると強力な武器になるということだ。
分類:SQL
SQL/あいまい検索の空文字
2015年02月18日
あいまい検索の空文字指定は、すべてにヒットする。
┌──────────────────────────────────────┐
│SELECT *                                                                    │
│    FROM 表                                                                 │
│    WHERE 列 LIKE '%%';                                                     │
└──────────────────────────────────────┘

よって、検索条件に指定がない場合(空文字の場合)、
そのままLIKEにつっこめば、全検索になってくれる。

空文字の場合はWHERE句(の一部)自体を削らなければいけないと勘違いしがちだが
その必要はない(条件分岐でSQL文を作成し分けるみたいなことは不要)
分類:SQL
前へ 1 2 3 4 5 6 次へ