MW211 EXIT

devlog
MSSQL/インデックスをとなりのDB…にはできない
2023年11月01日
Q.インデックスをテーブルのあるDBとは別のDBにはることができるか?
    (インデックス領域を別DBに移し、領域の効率化をはかる)
A.できない
┌──────────────────────────────────────┐
│CREATE NONCLUSTERED INDEX [索引] ON [DB1].[dbo].[表] …                     │
└──────────────────────────────────────┘
    上記を[DB2]で実行して、[DB1]上でインデックスがはられる
    ([DB1]で実行したのと同じ結果になる)
    また、下記のような指定もできない
┌──────────────────────────────────────┐
│CREATE NONCLUSTERED INDEX [DB2].[dbo].[索引] ON [DB1].[dbo].[表] …         │
└──────────────────────────────────────┘
分類:MSSQL
MSSQL/インデックスの違い
2023年10月31日
┌────────────┬─────────────────────────┐
│クラスタ化インデックス  │(基本的に)主キー(*1)、テーブルに一つのみ設定可能  │
│                        │実データがその並びになるので検索は爆速            │
│                        │*1:敢えて主キーを非クラスタ化し                  │
│   PRIMARY KEY CLUSTERED│    他インデックスにクラスタ化を譲ることも可能    │
├────────────┼─────────────────────────┤
│非クラスタ化インデックス│一般的なインデックス(クラスタ化以外のインデックス)│
│                        │実データとは別に検索列と行ポインタの並びを保存する│
│      NONCLUSTERED INDEX│更新時に時間がかかり、インデックスサイズを消費する│
├────────────┼─────────────────────────┤
│カバリングインデックス  │インデックスに検索列以外に参照列も含めて          │
│複合インデックス(の活用)│行を参照することなくインデックス内で参照を完結させ│
│                        │検索を高速化する手法(参照列は検索列と相違なし)    │
│                  ON (,)│但し、その分インデックスサイズが肥大化する        │
├────────────┼─────────────────────────┤
│付加列インデックス      │カバリングインデックスのサイズを削減したもの      │
│             INCLUDE (,)│参照列を検索列と分ける(参照専門にする)ことで実現  │
└────────────┴─────────────────────────┘
分類:MSSQL
MSSQL/容量確認
2023年10月30日
┌──────────────────────────────────────┐
│EXECUTE sp_spaceused;                                                       │
└──────────────────────────────────────┘
上記をDBにて実行すると、以下の結果が取得できる。
┌─────────┬─────────┬─────────┐
│database_name     │database_size     │unallocated space │
├─────────┼─────────┼─────────┤
│(DB名)            │①       xxxx.xxMB│②       xxxx.xxMB│
└─────────┴─────────┴─────────┘
┌───────┬───────┬───────┬───────┐
│reserved      │data          │index_size    │unused        │
├───────┼───────┼───────┼───────┤
│③      xxxxKB│④      xxxxKB│⑤      xxxxKB│⑥      xxxxKB│
└───────┴───────┴───────┴───────┘

実体ファイル(のサイズ)との関係は以下の通り
・データ「(DB名).mdf」  =②+③  (なお、③=④+⑤+⑥)
・ログ「(DB名)_log.ldf」=①-(②+③)

つまり
・①=データ「(DB名).mdf」+ログ「(DB名)_log.ldf」
  →プロパティに表示される「サイズ」も①である
分類:MSSQL
MSSQL/ストアドプロシージャでSQL文出力
2023年05月17日
ストアドプロシージャでSQL文を出力する方法。

例えば、以下のような感じでSQL文を編集して、実行できたりする。
┌──────────────────────────────────────┐
│CREATE PROCEDURE [dbo].[PROCEDURE_テスト]                                   │
│AS                                                                          │
│BEGIN                                                                       │
│    DECLARE @sql    [nvarchar](max);                                        │
│    SET @sql = 'SELECT ' + CHAR(39) + '結果' + CHAR(39) + ';';              │
│    EXECUTE (@sql);                                                         │
│END;                                                                        │
│--→出力されるのは「結果」一行                                              │
└──────────────────────────────────────┘
EXECUTE()のところを、直接SQL文に置き換えてもOK

以下のような感じで実行する。
┌──────────────────────────────────────┐
│EXEC [dbo].[PROCEDURE_テスト];                                              │
└──────────────────────────────────────┘

スドアドファンクションのように、他SQL文との連携に融通は利かないが
Power BIでもSQL文指定でテーブルに値をひっぱってきたりできるし
PHPでもPDOで連想配列にひっぱってきたりできる。
分類:MSSQL
MSSQL/文字列をいい感じで数値変換
2022年11月29日
JavaScriptのparseInt()のように、文字列込みの数値の文字列を
数値にいい感じで変換するには、TRANSLATE()を使えば実現できる。
けど、ちとめんどくさい。(もっといい方法がありそうだが)
例)「1個」→「1」
┌──────────────────────────────────────┐
│SELECT [文字列],                                                            │
│       CONVERT([int],                                                       │
│               REPLACE(TRANSLATE([文字列],                                  │
│                                 [除外文字],                                │
│                                 REPLICATE(' ', LEN([除外文字]))            │
│                       ),                                                   │
│                       ' ',                                                 │
│                       '')                                                  │
│       ) AS [数値]                                                          │
│    FROM (                                                                  │
│        SELECT [文字列],                                                    │
│               TRANSLATE([文字列],                                          │
│                         '0123456789',                                      │
│                         REPLICATE(' ', 10)) AS [除外文字]                  │
│            FROM (                                                          │
│                VALUES                                                      │
│('1個'),                                                                    │
│('全23セット')                                                              │
│            ) AS [表]([文字列])                                             │
│    ) AS [表]                                                               │
└──────────────────────────────────────┘
分類:MSSQL
MSSQL/FOR XML PATHを復習
2022年05月28日
複数行を横一列にする方法として、MSSQLでは「FOR XML PATH」を用いる。
その意味をもう一度復習

「FOR XML PATH」をつけると、XML形式として横一列に表示ができる。
┌──────────────────────────────────────┐
│WITH [表] AS (                                                              │
│         SELECT * FROM (VALUES ('A'),                                       │
│                               ('B'),                                       │
│                               ('C')) AS [表]([列])                         │
│     )                                                                      │
│SELECT [列]                                                                 │
│    FROM [表]                                                               │
│    FOR XML PATH ('');                                                      │
│--→「<列>A</列><列>B</列><列>C</列>」                                      │
└──────────────────────────────────────┘
通常は列名でタグが付いてくる。

これを、列名[data()]にすると、スペース区切りにすることができる
┌──────────────────────────────────────┐
│WITH [表] AS (                                                              │
│         SELECT * FROM (VALUES ('A'),                                       │
│                               ('B'),                                       │
│                               ('C')) AS [表]([列])                         │
│     )                                                                      │
│SELECT [列] AS [data()]                                                     │
│    FROM [表]                                                               │
│    FOR XML PATH ('');                                                      │
│--→「A B C」                                                               │
└──────────────────────────────────────┘
内側だけにスペースが付くので便利。
このスペースを区切り文字に置換すればよいわけだ。

では列名をなくしてしまえばどうだろう。
どのようなやり方でもよいがAS句で列名を指定しなければないけない形にもっていく。
(以下の場合は人畜無害な空文字を付加する形をとってみた)
┌──────────────────────────────────────┐
│WITH [表] AS (                                                              │
│         SELECT * FROM (VALUES ('A'),                                       │
│                               ('B'),                                       │
│                               ('C')) AS [表]([列])                         │
│     )                                                                      │
│SELECT [列] + ''                                                            │
│    FROM [表]                                                               │
│    FOR XML PATH ('');                                                      │
│--→「ABC」                                                                 │
└──────────────────────────────────────┘
これで列名によるタグがなくなるが、今度は区切りがまったくなくなってしまう。

ということで、自前で区切り文字をあらかじめつけたのが以下。
┌──────────────────────────────────────┐
│WITH [表] AS (                                                              │
│         SELECT * FROM (VALUES ('A'),                                       │
│                               ('B'),                                       │
│                               ('C')) AS [表]([列])                         │
│     )                                                                      │
│SELECT [列] + ','                                                           │
│    FROM [表]                                                               │
│    FOR XML PATH ('');                                                      │
│--→「A,B,C,」                                                              │
└──────────────────────────────────────┘
好きな区切り文字を指定できるが、外側にもついてしまうのが難点。


ちなみに「',' + [列]」と逆にすると、「,A,B,C」になる。
┌──────────────────────────────────────┐
│WITH [表] AS (                                                              │
│         SELECT * FROM (VALUES ('A'),                                       │
│                               ('B'),                                       │
│                               ('C')) AS [表]([列])                         │
│     )                                                                      │
│SELECT STUFF((SELECT ',' + [列]                                             │
│                  FROM [表]                                                 │
│                  FOR XML PATH ('')), 1, 1, '');                            │
│--→「A,B,C」                                                               │
└──────────────────────────────────────┘
そこでSTUFF関数と組み合わせればなんとなくいけるかも。

列名[data()]のやり方だとデータ中に半角スペースが混じっていると
破綻してしまうのだが、以下のような感じで
使われないであろう文字を区切り文字にして回避することができてしまう。
┌──────────────────────────────────────┐
│WITH [表] AS (                                                              │
│         SELECT * FROM (VALUES ('A X'),                                     │
│                               ('B Y'),                                     │
│                               ('C Z')) AS [表]([列])                       │
│     )                                                                      │
│SELECT REPLACE(STUFF((SELECT '\n' + [列]                                    │
│                          FROM [表]                                         │
│                          FOR XML PATH ('')), 1, 2, ''), '\n', CHAR(10));   │
│--→「A X                                                                   │
│      B Y                                                                   │
│      C Z」                                                                 │
└──────────────────────────────────────┘
分類:MSSQL
MSSQL/JOINによるUPDATEで複数候補
2022年05月27日
他テーブルをJOINしてUPDATEする場合、複数行になったらどうなるのか?
┌──────────────────────────────────────┐
│GO                                                                          │
│    DECLARE @テストD table (                                               │
│        [列]    [nvarchar](16)  NULL                                        │
│    );                                                                      │
│    INSERT INTO @テストD VALUES (NULL);                                    │
│    UPDATE [出力D]                                                         │
│        SET [列] = [入力D].[列]                                            │
│        FROM @テストD AS [出力D]                                          │
│            CROSS JOIN (                                                    │
│                SELECT *                                                    │
│                    FROM (                                                  │
│                        VALUES ('A'),                                       │
│                               ('B'),                                       │
│                               ('C')                                        │
│                    ) AS [入力D]([列])                                     │
│            ) AS [入力D];                                                  │
│    SELECT * FROM @テストD;                                                │
│GO                                                                          │
│--→「A」に更新された                                                       │
└──────────────────────────────────────┘
最初のレコードが優先される。

でも、最初ってなんだよ?問題が発生する恐れがあるので、
あまり使わない方がよいだろう。
ひとまず二重キーエラーとかにならないので、対策は軽くてよいということで。
分類:MSSQL
MSSQL/(年と)週番号から週の開始日を求める
2022年02月24日
┌──────────────────────────────────────┐
│SELECT DATEADD([week],                                                      │
│               週番号 - 1,                                                  │
│               DATEADD([dd],                                                │
│                       1 - DATEPART([dw], DATEFROMPARTS(年, 1, 1)),         │
│                       DATEFROMPARTS(年, 1, 1))) AS [週頭日]                │
└──────────────────────────────────────┘
もっといい方法はないものか?
分類:MSSQL
MSSQL/マトリクス月間表
2021年11月10日
┌──────────────────────────────────────┐
│GO                                                                          │
│    DECLARE @引数年 [int];  SET @引数年 = 2021;                             │
│    DECLARE @引数月 [int];  SET @引数月 = 11;                               │
│    -- ---------------------------------------------------------------------│
│    SET NOCOUNT ON;--EXECUTEによる参照の場合はなくても大丈夫そうだが念のため│
│    DECLARE @sql    [nvarchar](max);                                        │
│    -- ---------------------------------------------------------------------│
│    --   列(@テーブル変数)                                                  │
│    -- ---------------------------------------------------------------------│
│    DECLARE @列 table (                                                     │
│        [日付]  [date]  NOT NULL,                                           │
│        PRIMARY KEY (                                                       │
│            [日付]  ASC                                                     │
│        )                                                                   │
│    );                                                                      │
│    WITH [カレンダM] AS (                                                  │
│             SELECT DATEFROMPARTS(@引数年, @引数月, 1) AS [日付]            │
│             UNION ALL                                                      │
│             SELECT DATEADD(dd, 1, [日付])             AS [日付]            │
│                 FROM [カレンダM]                                          │
│                 WHERE YEAR(DATEADD(dd, 1, [日付]))  = @引数年              │
│                   AND MONTH(DATEADD(dd, 1, [日付])) = @引数月              │
│         )                                                                  │
│    INSERT INTO @列                                                         │
│        SELECT [日付]                                                       │
│            FROM [カレンダM];                                              │
│    -- ---------------------------------------------------------------------│
│    --   行(#一時テーブル)                                                  │
│    -- ---------------------------------------------------------------------│
│    CREATE TABLE #行 (                                                      │
│        [キー]  [int]   NOT NULL,                                           │
│        PRIMARY KEY (                                                       │
│            [キー]  ASC                                                     │
│        )                                                                   │
│    );                                                                      │
│    INSERT INTO #行                                                         │
│        SELECT DISTINCT [キー]                                              │
│            FROM 入力データ                                                 │
│            WHERE YEAR([日付])  = @引数年                                   │
│              AND MONTH([日付]) = @引数月;                                  │
│    -- ---------------------------------------------------------------------│
│    --   行列(#一時テーブル)                                                │
│    -- ---------------------------------------------------------------------│
│    CREATE TABLE #行列 (                                                    │
│        [キー]  [int]   NOT NULL,                                           │
│        [日付]  [date]  NOT NULL,                                           │
│        [値]    [int]   NULL,                                               │
│        PRIMARY KEY (                                                       │
│            [キー]  ASC,                                                    │
│            [日付]  ASC                                                     │
│        )                                                                   │
│    );                                                                      │
│    INSERT INTO #行列                                                       │
│        SELECT [キー],                                                      │
│               [日付],                                                      │
│               SUM([値]) AS [値]                                            │
│            FROM 入力データ                                                 │
│            WHERE YEAR([日付])  = @引数年                                   │
│              AND MONTH([日付]) = @引数月                                   │
│            GROUP BY [キー],                                                │
│                     [日付];                                                │
│    -- ---------------------------------------------------------------------│
│    --   明細                                                               │
│    -- ---------------------------------------------------------------------│
│    SELECT @sql = ISNULL(@sql + ',','')                                     │
│                + '(SELECT COUNT(*)'                                        │
│                + '     FROM #行列'                                         │
│                + '     WHERE #行列.[キー] = #行.[キー]'                    │
│                + '       AND #行列.[日付] = '                              │
│                          + CHAR(39) + CONVERT([varchar], [日付]) + CHAR(39)│
│                + ') AS [' + FORMAT(DAY([日付]), '00') + ']'                │
│        FROM @列                                                            │
│        ORDER BY [日付] ASC;                                                │
│    SET @sql = 'SELECT #行.[キー],'                                         │
│             + @sql                                                         │
│             + '    FROM #行'                                               │
│             + '    ORDER BY #行.[キー] ASC;';                              │
│    EXECUTE (@sql);                                                         │
│    -- ---------------------------------------------------------------------│
│    --   一時テーブルの削除                                                 │
│    -- ---------------------------------------------------------------------│
│    DROP TABLE #行列;                                                       │
│    DROP TABLE #行;                                                         │
│GO                                                                          │
└──────────────────────────────────────┘
分類:MSSQL
MSSQL/文字列を除外した数値変換の罠
2021年03月02日
数値(実は文字列)と文字列が混在する列から、
数値だけをとりだすには、以下のように
数値以外を除外した上で、数値変換すればよいように思われる。
┌──────────────────────────────────────┐
│SELECT CONVERT([int], [列]) AS [列]                                         │
│    FROM (                                                                  │
│        VALUES ('1'),                                                       │
│               ('a')                                                        │
│    ) AS [表]([列])                                                         │
│    WHERE ISNUMERIC([列]) = 1;                                              │
└──────────────────────────────────────┘
確かに、これ単体だとうまくいく。

ところがこれをVIEWとかサブ表とかにすると、途端にエラーになる
┌──────────────────────────────────────┐
│SELECT [列]                                                                 │
│    FROM (                                                                  │
│        SELECT CONVERT([int], [列]) AS [列]                                 │
│            FROM (                                                          │
│                VALUES ('1'),                                               │
│                       ('a')                                                │
│            ) AS [表]([列])                                                 │
│            WHERE ISNUMERIC([列]) = 1                                       │
│    ) AS [表]                                                               │
│    WHERE [列] = 1;                                                         │
│//→メッセージ 245、レベル 16、状態 1、行 1                                 │
│//  varchar の値 'a' をデータ型 int に変換できませんでした。                │
└──────────────────────────────────────┘

以下のように列として仕分けしてあげないといけないようだ。
┌──────────────────────────────────────┐
│SELECT [列]                                                                 │
│    FROM (                                                                  │
│        SELECT CASE                                                         │
│                 WHEN ISNUMERIC([列]) = 1 THEN                              │
│                   CONVERT([int], [列])                                     │
│                 ELSE                                                       │
│                   NULL                                                     │
│               END AS [列]                                                  │
│            FROM (                                                          │
│                VALUES ('1'),                                               │
│                       ('a')                                                │
│            ) AS [表]([列])                                                 │
│    ) AS [表]                                                               │
│    WHERE [列] = 1;                                                         │
└──────────────────────────────────────┘
分類:MSSQL
前へ 1 2 3 4 5 6 7 8 9 10 11 12 13 次へ