MW211 EXIT

devlog
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
MSSQL/区切り文字で結合(横)
2021年01月20日
PHPのimplode()みたいに単純に文字列で結合する方法。
┌──────────────────────────────────────┐
│SELECT CONCAT_WS(',','A','B','C')                                           │
└──────────────────────────────────────┘
分類:MSSQL
MSSQL/PDOとOracleリンクサーバと論理型
2020年11月30日
┌──────────────────────────────────────┐
│$sql = <<<___SQL___                                                         │
│SELECT [論理] FROM OPENQUERY(ORACLE, 'SELECT 0 AS "論理" FROM DUAL') AS [o] │
│___SQL___;                                                                  │
│$sth = PDO->prepare($sql);                                                  │
│$sth->execute();                                                            │
│$row = $sth->fetch(PDO::FETCH_ASSOC);                                       │
│if (!$row['論理']) {                                                        │
│    // 偽:偽のつもりで0を返却しているのでこちらを期待するのだが            │
│} else {                                                                    │
│    // 真:こちらになってしまう                                             │
│}                                                                           │
└──────────────────────────────────────┘
論理型はOracleでもMSSQLでも、「0」と「1」を用いるのが普通だ。
ところが、上記のようにPDOを用いて、リンクサーバ経由で
Oracleから偽のつもりで「0」を取得したら、PHP上では真となってしまった。

原因は以下の通り。
(1) PHPで数値型の「0」や「0.0」、文字列型の「0」は、偽(FALSE)扱いなのだが
    文字列型の「0.0」は真(TRUE)扱いとなってしまう
(2) PDOで値を取得すると、(基本的に)文字列型となる
(3) OPENQUERY()でOracleから0を取得すると、float型の「0.0」となってしまう
    整数ではなく小数のnumber型がOracleでは基本のため

よって、(2)と(3)の組み合わせにより、(1)の条件に適合してしまったようだ。

ということで、MSSQLの世界ではbit型で論理型の代用をするので
SQL(MSSQL)上で以下のように変換してから、PDOで読み込むのがよいようだ。
┌──────────────────────────────────────┐
│SELECT CONVERT([bit], [論理]) AS [論理]~                                   │
└──────────────────────────────────────┘
分類:PDO、MSSQL
MSSQL/単純なCSV
2020年10月06日
一列をすべてCSVにする方法。
┌──────────────────────────────────────┐
│WITH [表] (                                                                 │
│         [列]                                                               │
│     ) AS (                                                                 │
│         SELECT [列]                                                        │
│             FROM (                                                         │
│                 VALUES ('A'),('B'),('C')                                   │
│             ) AS [表]([列])                                                │
│     )                                                                      │
│SELECT REPLACE((SELECT [表].[列] AS [data()]                                │
│                    FROM [表]                                               │
│                    FOR XML PATH ('')       ), ' ', ',') AS [列たち];       │
└──────────────────────────────────────┘
分類:MSSQL
MSSQL/MERGE INTOで差分がある時だけ更新
2020年09月25日
USINGの前にNOT EXISTSで同一データを除外。
(後で除外するとNOT MATHCED扱いとなるので注意)
同一判定をする時にはNULLに注意(NULLのままでは不一致になるので空文字に置換)。
┌──────────────────────────────────────┐
│MERGE INTO [出力D]                                                         │
│    USING (                                                                 │
│        SELECT [キー],                                                      │
│               [データ],                                                    │
│               GETDATE() AS [更新日時]                                      │
│            FROM [入力D]                                                   │
│            WHERE NOT EXISTS (                                              │
│SELECT *                                                                    │
│    FROM [出力D]                                                           │
│    WHERE ISNULL([出力D].[キー]  , '') = ISNULL([入力D].[キー]  , '')     │
│      AND ISNULL([出力D].[データ], '') = ISNULL([入力D].[データ], '')     │
│                  )                                                         │
│    ) AS [入力D]                                                           │
│      ON  [入力D].[キー] = [出力D].[キー]                                 │
│    WHEN MATCHED THEN                                                       │
│        UPDATE SET [データ]   = [入力D].[データ],                          │
│                   [更新日時] = [入力D].[更新日時]                         │
│    WHEN NOT MATCHED BY TARGET THEN                                         │
│        INSERT (                                                            │
│                [キー],                                                     │
│                [データ],                                                   │
│                [更新日時]                                                  │
│            ) VALUES (                                                      │
│                [入力D].[キー],                                            │
│                [入力D].[データ],                                          │
│                [入力D].[更新日時]                                         │
│            );"                                                             │
└──────────────────────────────────────┘
こうすると、更新日時の変更を差分があった時に限定できる。
分類:MSSQL
MSSQL/所有者を確認
2020年07月30日
データベースの所有者を確認するSQL文。
┌──────────────────────────────────────┐
│SELECT [データベースDB].[name] AS [データベース],                           │
│       [プリンシパルDB].[name] AS [所有者]                                  │
│    FROM [sys].[databases] AS [データベースDB]                              │
│        LEFT JOIN [sys].[server_principals] AS [プリンシパルDB]             │
│          ON [データベースDB].[owner_sid] = [プリンシパルDB].[sid]          │
│    ORDER BY [データベースDB].[database_id] ASC;                            │
└──────────────────────────────────────┘
分類:MSSQL
MSSQL/ユーザ権限
2020年07月28日
階層関係は以下の通り
DBサーバ→インスタンス→ログイン→ユーザ(→ロール)→データベース

詳細は以下の通り(●■は読取専用ユーザの設定を想定)

ログイン
├○Windows認証
└●SQLServer認証

サーバロール
├□sysadmin           …全部
├□serveradmin        …サーバ
├□securityadmin      …ロール
├□processadmin       …プロセス
├□setupadmin         …リンクサーバ
├□bulkadmin          …BULK INSERT
├□diskadmin          …ディスクファイル
├□dbcreator          …データベース更新
└■public             …

データベース
├□master             …システムデータベース
├□model              …  〃
├□msdb               …  〃
├□tempdb             …  〃
└■(ユーザDB)

固定データベースロール
├□db_owner           …全部
├□db_securityadmin   …ロール
├□db_accessadmin     …アクセス
├□db_backupoperator  …バックアップ
├□db_ddladmin        …定義(CREATE,ALTER)
├□db_datawriter      …更新(INSERT,UPDATE,DELETE)
├■db_datareader      …参照(SELECT)
├■db_denydatawriter  …更新の禁止
├□db_denydatareader  …参照の禁止
└■public
※msdbの場合はさらに以下もある
├□db_ssisadmin
├□db_ssisltduser
├□db_ssisoperator
├□dc_admin
├□dc_operator
├□dc_proxy
├□DatabaseMailUserRole
├□PolicyAdministratorRole
├□ServerGroupAdministratorRole
├□ServerGroupReaderRole
├□SQLAgentOperatorRole
├□SQLAgentReaderRole
├□SQLAgentUserRole
├□TargetServersRole
├□UtilityCMRReader
├□UtilityIMRReader
└□UtilityIMRWriter
分類:MSSQL
MSSQL/リンクサーバーでエラー
2020年02月20日
【現象】
  リンクサーバを用いて、Oracleの内容をMSSQLに引っぱってくる場合
  テーブル項目にtimestamp型の列が含まれていると以下エラーとなる
  →SELECTでその列を指定しなくともエラーが発生する
┌──────────────────────────────────────┐
│メッセージ 7354、レベル 16、状態 1、行 1                                    │
│リンク サーバー "■" の OLE DB プロバイダー "OraOLEDB.Oracle" により、      │
│無効なメタデータが列 "(timestamp型列)" に指定されました。                   │
│The data type is not supported.                                             │
└──────────────────────────────────────┘

【対策】
  OPENQUERYを用いる
┌──────────────────────────────────────┐
│SELECT *                                                                    │
│    FROM OPENQUERY(■, 'SELECT * FROM "表"');                               │
└──────────────────────────────────────┘
分類:MSSQL
MSSQL/一時表の削除方法
2020年01月22日
事前に存在チェックをしてから削除する方法。
┌──────────────────────────────────────┐
│IF OBJECT_ID(N'tempdb..#一時表', N'U') IS NOT NULL                          │
│BEGIN                                                                       │
│    DROP TABLE #一時表;                                                     │
│END;                                                                        │
└──────────────────────────────────────┘
分類:MSSQL
前へ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 次へ