MW211 EXIT

devlog
MSSQL/仮想表へのインデックス
2015年02月25日
┌──────────────────────────────────────┐
│CREATE VIEW [dbo].[仮想表] WITH SCHEMABINDING                               │
│AS                                                                          │
│    SELECT [列]                                                             │
│        FROM [dbo].[表]                                                     │
│;                                                                           │
├──────────────────────────────────────┤
│CREATE UNIQUE CLUSTERED INDEX [AK_仮想表] ON [dbo].[V_仮想表] (             │
│    [キー]                  ASC                                             │
│);                                                                          │
└──────────────────────────────────────┘
・仮想表作成時に「WITH SCHEMABINDING」をつける
  ・仮想表作成時にDB名を指定しない  ×「[DB].[dbo].[表]」→「[dbo].[表]」
    →つまりは他DBを絡めたビューには指定できないということ
・仮想表の中で参照している元の仮想表にも「WITH SCHEMABINDING」をつける
  →上記ルールが元の仮想表まですべて適用されるということ
・仮想表の中で参照している元の仮想表においては集約関数は使用不可
分類:MSSQL
MSSQL/秒の時分秒換算
2015年02月23日
「秒」を「時分秒」に換算するのに以下を使ってみた。
┌──────────────────────────────────────┐
│SELECT RTRIM(CONVERT(char(12),                                              │
│                     DATEADD(SECOND,                                        │
│                             [秒数],                                        │
│                             CONVERT(datetime, 0)),                         │
│                     108)                                                   │
│       )                                                                    │
└──────────────────────────────────────┘
ところが、24時間(86400秒)を超えると、「日」が繰り上がり「時」が「0」に戻る。
つまり本来「25時間」であるべきところが「(1日と)1時間」になってしまう。

MySQLだったら「SEC_TO_TIME()」という便利な関数があり、
「25時間」もへっちゃらなのだが。
┌──────────────────────────────────────┐
│SELECT SEC_TO_TIME(`秒数`);                                                 │
└──────────────────────────────────────┘

ということで、自前で実装してみた。
┌──────────────────────────────────────┐
│CREATE FUNCTION [dbo].[sec_to_time] (                                       │
│    @引数秒数               int                                             │
│) RETURNS varchar(max)                                                      │
│AS                                                                          │
│BEGIN                                                                       │
│    RETURN (                                                                │
│        SELECT CONVERT(varchar, FLOOR(@引数秒数 / 3600))                    │
│             + RIGHT(                                                       │
│                   RTRIM(CONVERT(char(12),                                  │
│                                 DATEADD(SECOND,                            │
│                                         ABS(@引数秒数),                    │
│                                         CONVERT(datetime, 0)),             │
│                                 108)),                                     │
│               6)                                                           │
│    );                                                                      │
│END;                                                                        │
└──────────────────────────────────────┘
分類:MSSQL
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
MSSQL/文字列末尾のスペースと比較
2015年02月21日
┌─┬────────────────────────────────────┐
│真│'abc' = 'abc'                                                           │
├─┼────────────────────────────────────┤
│真│'abc' = 'abc '                                                          │
└─┴────────────────────────────────────┘
上記のような場合、後者は「偽」になりそうなものである。

しかしながら、これは(「真」になるのは)、
「ANSI/ISO SQL-92」の立派な規格なのである(比較では後続の空白は無視)。

ということで、データを管理・入力する時は、トリムを推奨しようねという話でした。

では、既に混入済みの場合は、どうやってみつけだすのか?

基本的にバイナリに変換して比較すれば、厳密に比較ができる。
┌─┬────────────────────────────────────┐
│真│CONVERT(binary, 'abc') = CONVERT(binary, 'abc')                         │
├─┼────────────────────────────────────┤
│偽│CONVERT(binary, 'abc') = CONVERT(binary, 'abc ')                        │
└─┴────────────────────────────────────┘

但し、型が違うと(特に「nvarchar」と「varchar」など)、
バイナリ変換される結果も変わってくるので、
念のため以下の様に事前に型を合わせておくというのも検討せねばならない。
┌─┬────────────────────────────────────┐
│真│CONVERT(binary, CONVERT(nvarchar(4000), 'abc'))                         │
│  │                       = CONVERT(binary, CONVERT(nvarchar(4000), 'abc'))│
├─┼────────────────────────────────────┤
│偽│CONVERT(binary, CONVERT(nvarchar(4000), 'abc'))                         │
│  │                      = CONVERT(binary, CONVERT(nvarchar(4000), 'abc '))│
└─┴────────────────────────────────────┘
変換を介するということは処理速度が遅くなので、必要なければ端折るのがよい。

また、以下の様な方法もある(「COLLATE JAPANESE_BIN =」ではないので注意)。
┌─┬────────────────────────────────────┐
│真│'abc' COLLATE JAPANESE_BIN LIKE 'abc'                                   │
├─┼────────────────────────────────────┤
│偽│'abc' COLLATE JAPANESE_BIN LIKE 'abc '                                  │
└─┴────────────────────────────────────┘
分類:MSSQL
ExcelVBA/ダイアログアイコン
2015年02月20日
┌─┬───────┬─┬─┬──┬─────────────────────┐
│16│vbCritical    │○│×│警告│MsgBox "エラー発生", vbCritical           │
├─┼───────┼─┼─┼──┼─────────────────────┤
│32│vbQuestion    │Q│?│問合│If MsgBox("しますか?", _                 │
│  │              │  │  │    │          vbOKCancel + vbQuestion, _      │
│  │              │  │  │    │          "確認") <> vbOK Then            │
├─┼───────┼─┼─┼──┼─────────────────────┤
│48│vbExclamation │△│!│注意│MsgBox "見直してください", vbExclamation  │
├─┼───────┼─┼─┼──┼─────────────────────┤
│64│vbInformation │Q│i│情報│MsgBox "正常終了", vbInformation          │
└─┴───────┴─┴─┴──┴─────────────────────┘
分類:ExcelVBA
PHP/パスワード暗号化関数
2015年02月19日
以下の関数がある(強度の弱い順)。
┌────────┬──────┬────────────┬─────────┐
│   暗号化関数   │ 暗号化結果 │          環境          │     照合手段     │
├────────┼──┬───┼────────────┼─────────┤
│md5()           │固定│32文字│PHP5.3~                │md5()             │
├────────┼──┼───┼────────────┼─────────┤
│crypt()         │可変│34文字│PHP5.3~                │crypt()           │
├────────┼──┼───┼────────────┼─────────┤
│password_hash() │可変│60文字│PHP5.5~(PHP5.3.7~(*1))│password_verify() │
└────────┴──┴───┴────────────┴─────────┘
  *1:標準関数となったのはPHP5.5以降だが、その前に別途ライブラリとしてあった

  ・「md5()」は結果が固定なので解読されるリスクが高い
  ・「crypt()」は「salt」の指定等を自前でチューニングする必要あり
  ・「password_hash()」が簡潔にして強固(現状最適)

使用例は以下のような感じ。
┌────────────────┬─────────────────────┐
│             暗号化             │                   照合                   │
├────────────────┼─────────────────────┤
│$暗号文 = md5($平文);           │if (md5($平文) === $暗号文) {             │
│                                │    echo '○';                            │
│                                │} else {                                  │
│                                │    echo '×';                            │
│                                │}                                         │
├────────────────┼─────────────────────┤
│$暗号文 = crypt($平文);         │if (crypt($平文, $暗号文) === $暗号文) {  │
│                                │    echo '○';                            │
│                                │} else {                                  │
│                                │    echo '×';                            │
│                                │}                                         │
├────────────────┼─────────────────────┤
│$暗号文 = password_hash(        │if (password_verify($平文, $暗号文)) {    │
│              $平文,            │    echo '○';│                          │
│              PASSWORD_BCRYPT   │} else {                                  │
│          );                    │    echo '×';│                          │
│                                │}                                         │
└────────────────┴─────────────────────┘
  ・「crypt()」照合で比較する左辺と右辺の「$暗号文」は同じ値である必要あり
    「$暗号文」を「crypt($平文)」に置換すると値に相違が発生し照合できない
  ・「password_hash()」には「PASSWORD_DEFAULT」も指定でき
    その時の最善策が選択されるが、現状は「PASSWORD_BCRYPT」が採用
分類:PHP
SQL/あいまい検索の空文字
2015年02月18日
あいまい検索の空文字指定は、すべてにヒットする。
┌──────────────────────────────────────┐
│SELECT *                                                                    │
│    FROM 表                                                                 │
│    WHERE 列 LIKE '%%';                                                     │
└──────────────────────────────────────┘

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

空文字の場合はWHERE句(の一部)自体を削らなければいけないと勘違いしがちだが
その必要はない(条件分岐でSQL文を作成し分けるみたいなことは不要)
分類:SQL
経営会計/セオリー
2015年02月17日
好き嫌いは別として、基本的なセオリーは以下の通り
────────────────────────────────────────
┌────────────────┐
│現金は手許に長い間あった方がよい│
└────────────────┘
  ・現金を受取るなら早い方がよい(金利分損する(さもないと割引現在価値))(*1)
    →返済日を早める
    →返済日が遅い場合には別途金利分を請求する

  ・現金を支払うなら遅い方がよい(上記の反対)
    →返済日を遅くする

  ・貯金(資産)と借金(負債)は相殺した方がよい(「貯金の利子<借金の利子」なので)
    →繰上返済

  *1:実際に貯金しなくとも、これにより他で借金しないで済めば
      その金利分を得することになる
────────────────────────────────────────
┌─────────────┐
│固定的な費用はない方がよい│
└─────────────┘
  ・無駄な在庫は抱え込まない(保管料がかかる)
    →生産調整(減産)
    →安売り

  ・無駄な設備は抱え込まない(減価償却費がかかる)(*2)
    →稼働率の向上(薄利多売)
    →設備の売却(使う必要がある場合にはリース化)

  ・無駄な人員は抱え込まない(固定労務費がかかる)(*2)
    →稼働率の向上(薄利多売)
    →内製化(外注廃止・派遣切り)
    →解雇(使う必要がある場合には非正規化)

  ・無駄な損失は抱え込まない(税金を早く払うことになる)
    →評価損・減損で適正に費用化

  *2:設備購入(減価償却費)は前払の固定費、
      社員の雇用(労働契約)は後払の固定費と見ることができる
────────────────────────────────────────
┌───────────┐
│身軽な方が優秀に見える│
└───────────┘
  ・無駄な内部留保は抱え込まない(資本回転率が鈍る)
    →配当
────────────────────────────────────────
┌─────────┐
│目立たない方がよい│
└─────────┘
  ・儲かっているように見えれば投資家は安心し、
    儲かっていないように見えれば国は税金をとらない
    →期間ごとに平準化できれば…でも粉飾決算は絶対ダメ
────────────────────────────────────────
分類:経営学
MSSQL/疑似カレンダマスタ
2015年02月16日
2015/01/01以降から今日までの疑似カレンダマスタは
以下のような感じで作成できる。
┌──────────────────────────────────────┐
│SELECT CONVERT(nvarchar,                                                    │
│               DATEADD(DAY, generate_series, getdate()),                    │
│               111) AS [日付]                                               │
│    FROM generate_series(DATEDIFF(DAY,                                      │
│                                  getdate(),                                │
│                                  CONVERT(DATETIME, '2015-01-01')),         │
│                         0,                                                 │
│                         1);                                                │
└──────────────────────────────────────┘

なお、「generate_series()」は、PostgreSQLの関数でMSSQLには存在しないので
自前のユーザ関数で代替(別途参照)。
分類:MSSQL
MSSQL/generate_series()
2015年02月15日
PostgreSQLには、自動で連番テーブルを作成してくれる「generate_series()」という
便利な関数があるが、MSSQLにはない。

ということで、自前で作ると以下のような感じとなる。
┌──────────────────────────────────────┐
│CREATE FUNCTION [dbo].[generate_series] (                                   │
│    @引数開始               int,                                            │
│    @引数終了               int,                                            │
│    @引数増分               int = 1                                         │
│) RETURNS @結果 table (                                                     │
│    [generate_series]       int                                             │
│)                                                                           │
│AS                                                                          │
│BEGIN                                                                       │
│    DECLARE @値             int;                                            │
│    DECLARE @増分           int;                                            │
│    SET @値   = CASE                                                        │
│                  WHEN @引数開始 IS NULL THEN 1                             │
│                  ELSE                        @引数開始                     │
│                END;                                                        │
│    SET @増分 = CASE                                                        │
│                  WHEN @引数増分 IS NULL OR @引数増分 = 0 THEN 1            │
│                  ELSE                                         @引数増分    │
│                END;                                                        │
│    IF SIGN(@増分) =  1 AND @引数開始 > @引数終了                           │
│        RETURN;                                                             │
│    IF SIGN(@増分) = -1 AND @引数開始 < @引数終了                           │
│        RETURN;                                                             │
│    WHILE (1 = 1)                                                           │
│    BEGIN                                                                   │
│        INSERT INTO @結果([generate_series]) VALUES (@値);                  │
│        SET @値 = @値 + @増分;                                              │
│        IF SIGN(@増分) =  1 AND @値 > @引数終了                             │
│            BREAK;                                                          │
│        IF SIGN(@増分) = -1 AND @値 < @引数終了                             │
│            BREAK;                                                          │
│    END;                                                                    │
│    RETURN;                                                                 │
│END;                                                                        │
└──────────────────────────────────────┘
分類:MSSQL
前へ 1 … 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 … 156 次へ