MW211 EXIT

devlog
MSSQL/更新時のROW_NUMBER()
2018年12月21日
┌──────────────────────────────────────┐
│UPDATE [表]                                                                 │
│    SET [列] = ROW_NUMBER() OVER(PARTITION BY [集団]                        │
│                                 ORDER BY [順] ASC);                        │
└──────────────────────────────────────┘
上記のように直接使うと以下エラーとなる。
  ┌────────────────────────────────────┐
  │メッセージ 4108、レベル 15、状態 1、行 3                                │
  │ウィンドウ関数は、SELECT 句または ORDER BY 句だけで使用できます。       │
  └────────────────────────────────────┘
この場合、下記のように間接的に使えばよい。
┌──────────────────────────────────────┐
│UPDATE [表]                                                                 │
│    SET [列] = [入力D].[列]                                                │
│    FROM [表] AS [出力D]                                                   │
│        INNER JOIN (                                                        │
│            SELECT [主キー],                                                │
│                   ROW_NUMBER() OVER(PARTITION BY [集団]                    │
│                                     ORDER BY [順] ASC)  AS [列]            │
│                FROM [表]                                                   │
│        ) AS [入力D]                                                       │
│          ON [入力D].[主キー] = [出力D].[主キー];                         │
└──────────────────────────────────────┘
分類:MSSQL
MSSQL/入力データの順番を出力データに反映させる方法
2018年11月20日
┌──────────────────┬┬──────────────────┐
│            ×ダメな例×            ││             ○いい例○             │
├──────────────────┼┼──────────────────┤
│INSERT INTO [出力D] (              ││INSERT INTO [出力D] (              │
│        [出力ID],                   ││        [出力ID],                   │
│        [データ]                    ││        [データ]                    │
│    )                               ││    )                               │
│    SELECT ISNULL(                  ││    SELECT ROW_NUMBER() OVER(       │
│               (SELECT MAX([出力ID])││               ORDER BY [出力ID] ASC│
│                    FROM [出力]),   ││           ) AS [出力ID]            │
│               0                    ││                                    │
│           ) + 1 AS [出力ID],       ││                                    │
│           [データ]                 ││           [データ]                 │
│        FROM [入力D]               ││        FROM [入力D];              │
│        ORDER BY [入力ID] ASC;      ││                                    │
└──────────────────┴┴──────────────────┘
  左記の例は、一件ずつやる分に問題ない(トリガなどで)。
  だが、まとめてとなると「SELECT MAX([出力ID]) FROM [出力]」は
  初期の値が全件に対して(二件目以降も)適用される。
  なので、「ROW_NUMBER()」を用いる必要がある。

  なお、出力データに既存のレコードがある場合には、複合した以下となる。
┌──────────────────────────────────────┐
│INSERT INTO [出力D] (                                                      │
│        [出力ID],                                                           │
│        [データ]                                                            │
│    )                                                                       │
│    SELECT ISNULL((SELECT MAX([出力ID]) FROM [出力]), 0)                    │
│             + ROW_NUMBER() OVER(ORDER BY [出力ID] ASC) AS [出力ID]         │
│           [データ]                                                         │
│        FROM [入力D];                                                      │
└──────────────────────────────────────┘
分類:MSSQL
MSSQL/ジャーナル
2018年10月30日
┌──────────────────────────────────────┐
│CREATE TABLE [DB].[dbo].[T_表] (                                            │
│    [キー]          [int]               NOT NULL,                           │
│    [値]            [nvarchar](256)     NULL,                               │
│    [更新者]        [nvarchar](32)      NOT NULL,                           │
│    [更新日時]      [datetime]          NOT NULL    DEFAULT (GETDATE()),    │
│    CONSTRAINT [PK_表] PRIMARY KEY CLUSTERED (                              │
│        [キー]                  ASC                                         │
│    )                                                                       │
│);                                                                          │
└──────────────────────────────────────┘
例えば上記テーブルのジャーナルテーブル(更新履歴テーブル)を実装する方法。
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
【1】ジャーナルテーブルを作る
┌──────────────────────────────────────┐
│CREATE TABLE [DB].[dbo].[T_ジャーナル_表] (                                 │
│    [ジャーナルID]      [int] IDENTITY(1,1) NOT NULL,                   --※│
│    [ジャーナル区分]    [nchar](2)          NOT NULL,                   --※│
│    [キー]              [int]               NULL,                           │
│    [値]                [nvarchar](256)     NULL,                           │
│    [更新者]            [nvarchar](32)      NULL,                           │
│    [更新日時]          [datetime]          NULL,                           │
│    CONSTRAINT [PK_ジャーナル_表] PRIMARY KEY CLUSTERED (                   │
│        [ジャーナルID]          ASC                                     --※│
│    )                                                                       │
│);                                                                          │
└──────────────────────────────────────┘
  対象テーブルの各列について、制約を一切はずして、継承する。
  また、代わりに主キーとして連番([ジャーナルID])をあてる。
  それと、「追加」か「削除」を記録する区分([ジャーナル区分])も設置。
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
【2】トリガを仕込む
┌──────────────────────────────────────┐
│CREATE TRIGGER [dbo].[TRIGGER_ジャーナル_表] ON [DB].[dbo].[T_表]           │
│    AFTER INSERT, UPDATE, DELETE                                            │
│AS                                                                          │
│BEGIN                                                                       │
│    INSERT INTO [DB].[dbo].[T_ジャーナル_表] (                              │
│            [ジャーナル区分],                                               │
│            [キー],                                                         │
│            [値],                                                           │
│            [更新者],                                                       │
│            [更新日時]                                                      │
│        )                                                                   │
│        SELECT '削除' AS [ジャーナル区分],                                  │
│               [キー],                                                      │
│               [値],                                                        │
│               [更新者],                                                    │
│               [更新日時]                                                   │
│            FROM deleted;                                                   │
│    INSERT INTO [DB].[dbo].[T_ジャーナル_表] (                              │
│            [ジャーナル区分],                                               │
│            [キー],                                                         │
│            [値],                                                           │
│            [更新者],                                                       │
│            [更新日時]                                                      │
│        )                                                                   │
│        SELECT '追加' AS [ジャーナル区分],                                  │
│               [キー],                                                      │
│               [値],                                                        │
│               [更新者],                                                    │
│               [更新日時]                                                   │
│            FROM inserted;                                                  │
│END;                                                                        │
└──────────────────────────────────────┘
  MSSQLの場合は、「deleted」で更新前(削除前)のレコードが、
  「inserted」で更新後(追加後)のレコードが取得できるので
  それをそのままジャーナルにしてしまった方が間違いないようだ。

  もちろん、INSERT・UPDATE・DELETEに分けるという方法もあるが
  一つのトリガ内で、これを(何をトリガとしたのかを)識別することは難しい模様。
  そうなると、トリガを三つ用意した方が間違いないようではあるが。。。
分類:MSSQL
MSSQL/bit型とExcel出力
2018年08月04日
MSSQLにおけるbool型相当はbit型であるが、
SQL文中では「TRUE/FALSE」ではなく「1/0」で扱う。
よって、int型と同じなのかなと思うと当然ながら違うこともある。

その一つがExcelにおける文字列型のセルに出力した際に
「TRUE/FALSE」と表示される点だ。
なお、「TRUE」や「FALSE」といった文字列ではない点に注意。
分類:MSSQL
MSSQL/Key-Valueチェーン
2018年08月03日
Key-Value形式のテーブルで、Valueに他のKeyがある場合
それを繋げて最終的なValueを取得するSQL文。
なお、循環(無限ループ)の場合は、最初の階層の結果を返却する。
┌──────────────────────────────────────┐
│WITH [起点] (                                                               │
│        [キー],                                                             │
│        [バリュー]                                                          │
│    ) AS (                                                                  │
│        SELECT [キー],                                                      │
│               [バリュー]                                                   │
│            FROM 表 [表]                                                    │
│            WHERE [キー] = 値                                               │
│    ),                                                                      │
│    [再帰] (                                                                │
│        [キー],                                                             │
│        [階層],                                                             │
│        [直前キー],                                                         │
│        [バリュー],                                                         │
│        [循環]                                                              │
│    ) AS (                                                                  │
│        SELECT [キー],                                                      │
│               1                 AS [階層],                                 │
│               [キー]            AS [直前キー],                             │
│               [バリュー],                                                  │
│               0                 AS [循環]                                  │
│            FROM [起点]                                                     │
│        UNION ALL                                                           │
│        SELECT [再帰].[キー],                                               │
│               [再帰].[階層] + 1 AS [階層],                                 │
│               [表].[キー]       AS [直前キー],                             │
│               [表].[バリュー],                                             │
│               CASE                                                         │
│                 WHEN [再帰].[キー] = [表].[バリュー]                       │
│                   OR [再帰].[階層] > 99              THEN 1                │
│                 ELSE                                      0                │
│               END AS [循環]                                                │
│            FROM [再帰]                                                     │
│                INNER JOIN 表 AS [表]                                       │
│                  ON [表].[キー] = [再帰].[バリュー]                        │
│            WHERE [再帰].[循環] = 0                                         │
│    )                                                                       │
│SELECT [キー],                                                              │
│       [階層],                                                              │
│       [直前キー],                                                          │
│       [バリュー]                                                           │
│    FROM [再帰]                                                             │
│    WHERE [循環] = 0                                                        │
│      AND NOT EXISTS (                                                      │
│              SELECT *                                                      │
│                  FROM [再帰] AS [他]                                       │
│                  WHERE [他].[階層] > [再帰].[階層]                         │
│          )                                                                 │
│UNION ALL                                                                   │
│SELECT [キー],                                                              │
│       NULL       AS [階層],                                                │
│       NULL       AS [直前キー],                                            │
│       [バリュー]                                                           │
│    FROM [起点]                                                             │
│    WHERE EXISTS (                                                          │
│              SELECT *                                                      │
│                  FROM [再帰]                                               │
│                  WHERE [循環] = 1                                          │
│          )                                                                 │
└──────────────────────────────────────┘
分類:MSSQL
MSSQL/MERGE文を片方だけ
2018年07月30日
UPSERT的な以下MERGE文、WHEN句は削ることができる。
(でも一つは残さないとエラーだけど)
┌──────────────────────────────────────┐
│MERGE INTO [出力D]                                                         │
│    USING [入力D]                                                          │
│      ON [入力D].[キー] = [出力D].[キー]                                  │
│    WHEN MATCHED THEN                                                       │
│        UPDATE SET [列] = [入力D].[列]                                     │
│    WHEN NOT MATCHED BY TARGET THEN                                         │
│        INSERT ([列]) VALUES ([入力D].[列])                                │
│;                                                                           │
└──────────────────────────────────────┘

以下だとINSERT同等。(重複していたらINSERTと違ってエラーにはならない)
┌──────────────────────────────────────┐
│MERGE INTO [出力D]                                                         │
│    USING [入力D]                                                          │
│      ON [入力D].[キー] = [出力D].[キー]                                  │
│----WHEN MATCHED THEN                                                       │
│----    UPDATE SET [列] = [入力D].[列]                                     │
│    WHEN NOT MATCHED BY TARGET THEN                                         │
│        INSERT ([列]) VALUES ([入力D].[列])                                │
│;                                                                           │
└──────────────────────────────────────┘

以下だとUPDATE同等。(該当がなかったらUPDATE同様無視)
┌──────────────────────────────────────┐
│MERGE INTO [出力D]                                                         │
│    USING [入力D]                                                          │
│      ON [入力D].[キー] = [出力D].[キー]                                  │
│    WHEN MATCHED THEN                                                       │
│        UPDATE SET [列] = [入力D].[列]                                     │
│----WHEN NOT MATCHED BY TARGET THEN                                         │
│----    INSERT ([列]) VALUES ([入力D].[列])                                │
│;                                                                           │
└──────────────────────────────────────┘

ちなみに「WHEN MATCHED THEN」は、「WHEN MATCHED THEN DELETE」(DELETE文)もできる。

なお、いずれも「THEN NEXT SENTENSE」的な空処理はできないようだ。
つまりWHEN句は上記のようにコメントアウトするか文を削除する他ない。

一つ以上WHEN句が必要だけどそれを空処理にすることはできない、
ということになるが、そうなるとまったく意味不明なので
そんなことできなくてよいのかもしれない。
分類:MSSQL
MSSQL/ストアド関数と動的可変テーブル
2018年03月02日
ストアド関数で戻り値を可変のテーブル型とし、
関数内で動的にSQL文を生成した結果を戻り値として返すことはできるのか?

・ストアド関数で戻り値を可変のテーブル型とする場合には、
  処理を書くことができず(つまり動的にSQL文を書けない)、
  RETURN処理に固定のSELECT文を書けるのみ
  RETURN処理では、ストアドプロシージャを呼び出すこともできない

よって、無理のようだ
分類:MSSQL
MSSQL/マトリクス出力(pivot利用版)
2018年02月08日
┌──────────────────────────────────────┐
│GO                                                                          │
│    DECLARE @sql    nvarchar(max);                                          │
│    DECLARE @sql0   nvarchar(max);                                          │
│    DECLARE @sql1   nvarchar(max);                                          │
│    DECLARE @sql1a  nvarchar(max);                                          │
│    DECLARE @sql1b  nvarchar(max);                                          │
│    -- ---------------------------------------------------------------------│
│    --   見出                                                               │
│    -- ---------------------------------------------------------------------│
│    SELECT @sql0 = ISNULL(@sql0 + ',','')                                   │
│                 + '(SELECT [列名]'                                         │
│                 + '     FROM [列M]'                                       │
│                 + '     WHERE [列ID] = ' + CONVERT(varchar,[列ID])         │
│                 + ') AS [' + CONVERT(varchar,[列ID]) + ']'                 │
│        FROM [列M]                                                         │
│        ORDER BY [列順];                                                    │
│    SET @sql0 = 'SELECT 0    AS [行順],'                                    │
│              + '       NULL AS [行ID],'                                    │
│              + @sql0;                                                      │
│    -- ---------------------------------------------------------------------│
│    --   明細                                                               │
│    -- ---------------------------------------------------------------------│
│    SELECT @sql1a = ISNULL(@sql1a + ',','')                                 │
│                  + '[' + CONVERT(varchar,[列ID]) + ']',                    │
│           @sql1b = ISNULL(@sql1b + ',','')                                 │
│                  + '[ピボット].[' + CONVERT(varchar,[列ID]) + ']'          │
│        FROM [列M]                                                         │
│        ORDER BY [列順];                                                    │
│    SET @sql1 = 'SELECT [行M].[行順],'                                     │
│              + '       [ピボット].[行ID],'                                 │
│              + @sql1b                                                      │
│              + '    FROM ('                                                │
│              + '        SELECT [列M].[列ID],'                             │
│              + '               [行M].[行ID],'                             │
│              + '               [行列D].[データ]'                          │
│              + '            FROM [列M]'                                   │
│              + '                CROSS JOIN [行M]'                         │
│              + '                LEFT JOIN [行列D]'                        │
│              + '                  ON  [行列D].[列ID] = [列M].[列ID]'     │
│              + '                  AND [行列D].[行ID] = [行M].[行ID]'     │
│              + '    ) AS [行列M]'                                         │
│              + '    PIVOT ('                                               │
│              + '        MAX([データ]) FOR [列ID] IN (' + @sql1a + ')'      │
│              + '    ) AS [ピボット]'                                       │
│              + '        INNER JOIN [行M]'                                 │
│              + '          AND [行M].[行ID] = [ピボット].[行ID]';          │
│    -- ---------------------------------------------------------------------│
│    --   統合                                                               │
│    -- ---------------------------------------------------------------------│
│    SET @sql = 'SELECT *'                                                   │
│             + '    FROM (' + @sql0 + ' UNION ALL ' + @sql1 + ') AS [行列]' │
│             + '    ORDER BY [行順] ASC;';                                  │
│    EXECUTE (@sql);                                                         │
│GO                                                                          │
└──────────────────────────────────────┘
分類:MSSQL
MSSQL/マトリクス出力
2018年02月07日
┌──────────────────────────────────────┐
│GO                                                                          │
│    DECLARE @sql    [nvarchar](max);                                        │
│    DECLARE @sql0   [nvarchar](max);                                        │
│    DECLARE @sql1   [nvarchar](max);                                        │
│    -- ---------------------------------------------------------------------│
│    --   見出                                                               │
│    -- ---------------------------------------------------------------------│
│    SELECT @sql0 = ISNULL(@sql0 + ',','')                                   │
│                 + '(SELECT [列名]'                                         │
│                 + '     FROM [列M]'                                       │
│                 + '     WHERE [列ID] = ' + CONVERT([varchar], [列ID])      │
│                 + ') AS [' + CONVERT([varchar], [列ID]) + ']'              │
│        FROM [列M]                                                         │
│        ORDER BY [列順];                                                    │
│    SET @sql0 = 'SELECT 0    AS [行順],'                                    │
│              + '       NULL AS [行ID],'                                    │
│              + @sql0;                                                      │
│    -- ---------------------------------------------------------------------│
│    --   明細                                                               │
│    -- ---------------------------------------------------------------------│
│    SELECT @sql1 = ISNULL(@sql1 + ',','')                                   │
│                 + '(SELECT [行列D].[データ]'                              │
│                 + '     FROM [行列D]'                                     │
│                 + '     WHERE [行列D].[列ID] = '                          │
│                                                + CONVERT([varchar], [列ID])│
│                 + '       AND [行列D].[行ID] = [行M].[行ID]'             │
│                 + ') AS [' + CONVERT([varchar], [列ID]) + ']'              │
│        FROM [列M]                                                         │
│        ORDER BY [列順];                                                    │
│    SET @sql1 = 'SELECT [行M].[行順],'                                     │
│              + '       [行M].[行ID],'                                     │
│              + @sql1                                                       │
│              + '    FROM [行M]';                                          │
│    -- ---------------------------------------------------------------------│
│    --   統合                                                               │
│    -- ---------------------------------------------------------------------│
│    SET @sql = 'SELECT *'                                                   │
│             + '    FROM (' + @sql0 + ' UNION ALL ' + @sql1 + ') AS [行列]' │
│             + '    ORDER BY [行順] ASC;';                                  │
│    EXECUTE (@sql);                                                         │
│GO;                                                                         │
└──────────────────────────────────────┘

なお、[列ID]が文字列の場合は以下に変更。
┌──────────────────────────────────────┐
│                 + '     WHERE [行列D].[列ID] = '                          │
│                                                + CONVERT([varchar], [列ID])│
└──────────────────────────────────────┘
  ↓
┌──────────────────────────────────────┐
│                 + '     WHERE [行列D].[列ID]                              │
│                                          = ' + CHAR(39) + [列ID] + CHAR(39)│
└──────────────────────────────────────┘
分類:MSSQL
MSSQL/自動採番に0を入力
2017年05月10日
┌──────────────────────────────────────┐
│CREATE TABLE [表] (                                                         │
│    [連番]              [int] IDENTITY(1,1) NOT NULL,                       │
│    CONSTRAINT [PK_表] PRIMARY KEY CLUSTERED (                              │
│        [連番]              ASC                                             │
│    )                                                                       │
│);                                                                          │
└──────────────────────────────────────┘
上記のような、主キーが自動採番のレコードに、
後から「0」のレコードを挿入する方法。

以下のように、特殊な挿入を許可した上で行えばよい。もちろん終わったら元に戻す。
┌──────────────────────────────────────┐
│SET IDENTITY_INSERT [表] ON;                                                │
│INSERT INTO [表] ([連番]) VALUES (0);                                       │
│SET IDENTITY_INSERT [表] OFF;                                               │
└──────────────────────────────────────┘
ちなみに、その後に挿入した場合、「0」の次の「1」から
始まったりはしない(いつものように最大値の次の値から始まる)のでご安心を。
分類:MSSQL
前へ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 次へ