web-dev-qa-db-ja.com

参照行と比較して異なる値を持つ列を検索する

私のテーブルであるMARCには、WERKS(プラント)をキーとして200を超える列があります。

MARCのほとんどのエントリには、参照プラントからの値があります。したがって、通常、200列すべてに同じ値が含まれます。

偏差を見つけて異なる値を持つ列の名前を表示するために、参照プラント行と比較して異なる値を持つ列を見つける必要があります。

主な質問は、どの列を選択する必要があるかわからない場合の選択方法です。

data screen shot

上記のABASは参照プラントです。

最初の行のみが返されることを期待します。これは、その行だけが参照行と比較して差異を含んでいるためです。その行の違いは、次の列で発生します。

  • 動作している
  • PSTAT
  • DISMM
  • DISPO

返される行にはこれらの4つの列のみが含まれている必要があります。

これらの3つの列がその特定のWERKSに対して異なる値を持っていることをUIに表示するために、他の列は必要ありません。

SAP HANAを使用しています。

2
user2293813

この回答は、質問に対するコメントでMichael Greenが言及した手法の実装を示しています。 SQL Serverを使用しますが、基本的な考え方は、動的SQLをサポートするSQL互換のデータベースエンジンに適用できます。 UNPIVOTおよびPIVOTのサポートは必要ありません

大まかなアイデアは次のとおりです。

  1. 行をキーと値のペアに変換する
  2. 参照行と比較して違いを特定する
  3. 動的SQLを使用して、差異をWERKSごとに1行に変換します

どの列をSELECTにするかわからないという問題を回避するのは動的SQLの使用です-それらは動的に決定されます。

サンプルテーブルとデータ

CREATE TABLE #MARC
(
    c1 integer PRIMARY KEY,
    c2 integer NULL,
    c3 integer NULL,
    c4 integer NULL,
    c5 integer NULL
);

INSERT #MARC
    (c1,c2,c3,c4,c5)
VALUES
    (1, 2, 3, 4, 5), -- The reference row
    (2, 2, 3, 4, 5),
    (3, 2, 3, 4, 5),
    (4, 2, 3, 7, 9), -- Differences: 7 in c4; 9 in c5 
    (5, 6, 3, 4, 8); -- Differences: 6 in c2; 8 in c5

ピボットを解除して違いを見つける

-- Holds differences found (WERKS keys and column key-value pairs)
CREATE TABLE #Differences
(
    c1 integer NOT NULL,
    name varchar(2) NOT NULL,
    value integer NOT NULL
);

WITH 
    Ref AS
    (
        -- Unpivoted reference row
        SELECT 
            M.c1, 
            CA.name,
            CA.value
        FROM #MARC AS M
        CROSS APPLY 
        (
            VALUES
                ('c2', M.c2),
                ('c3', M.c3),
                ('c4', M.c4),
                ('c5', M.c5)
        ) AS CA (name, value)
        WHERE
            M.c1 = 1
    ), 
    NonRef AS
    (
        -- Unpivoted non-reference rows
        SELECT 
            M.c1, 
            CA.name,
            CA.value
        FROM #MARC AS M
        CROSS APPLY 
        (
            VALUES
                ('c2', M.c2),
                ('c3', M.c3),
                ('c4', M.c4),
                ('c5', M.c5)
        ) AS CA (name, value)
        WHERE
            M.c1 <> 1
    ), 
    Differences AS
    (
        -- Find column value differences
        SELECT 
            NonRef.c1, 
            NonRef.name, 
            NonRef.value
        FROM NonRef
        JOIN Ref
            ON Ref.name = NonRef.name      -- Same column name
            AND Ref.value <> NonRef.value  -- Different value
    )
INSERT #Differences
    (c1, name, value)
SELECT
    D.c1,
    D.name,
    D.value
FROM Differences AS D;

違い

-- Show differences for illustrative purposes
SELECT
    D.c1,
    D.name,
    D.value
FROM #Differences AS D;

╔════╦══════╦═══════╗
║ c1 ║ name ║ value ║
╠════╬══════╬═══════╣
║  4 ║ c4   ║     7 ║
║  4 ║ c5   ║     9 ║
║  5 ║ c2   ║     6 ║
║  5 ║ c5   ║     8 ║
╚════╩══════╩═══════╝

動的SQL

-- Header
DECLARE @sql varchar(max) =
    'SELECT D.c1' + CHAR(13);

-- Add aggregation
WITH D AS
(
    SELECT DISTINCT D2.name
    FROM #Differences AS D2
)
SELECT @sql +=
    (
        SELECT 
            ', MAX(CASE WHEN D.name = ' + CHAR(39) + D.name + CHAR(39) + 
            ' THEN D.value END) AS ' + QUOTENAME(D.name) + CHAR(13) AS [text()]
        FROM D
        FOR XML PATH (''), TYPE
    ).value('(./text())[1]', 'varchar(max)')

-- Footer    
SET @sql +=
    'FROM #Differences AS D' + CHAR(13) +
    'GROUP BY D.c1;'

@sqlで作成されたSQLステートメントは次のとおりです。

SELECT D.c1
, MAX(CASE WHEN D.name = 'c2' THEN D.value END) AS [c2]
, MAX(CASE WHEN D.name = 'c4' THEN D.value END) AS [c4]
, MAX(CASE WHEN D.name = 'c5' THEN D.value END) AS [c5]
FROM #Differences AS D
GROUP BY D.c1;

これは手動のPIVOT構文です。最終的な結果は、SQLを実行することによって取得されます。

EXECUTE (@sql);

結果:

╔════╦══════╦══════╦════╗
║ c1 ║  c2  ║  c4  ║ c5 ║
╠════╬══════╬══════╬════╣
║  4 ║ NULL ║ 7    ║  9 ║
║  5 ║ 6    ║ NULL ║  8 ║
╚════╩══════╩══════╩════╝

これは、必要に応じて、参照行に対する列の違いを説明する最小限の列のセットです。

-- Clean up temporary tables
DROP TABLE 
    #Differences,
    #MARC;

上記のSQL Server固有の構文はいくつかありますが、UNPIVOTまたはAPPLYがサポートされていない場合は、手動でLATERALを他の方法で実現できます。適切な単位行列テーブルへのクロス結合を使用します。動的SQLの生成では、文字列を連結する効率的な方法としてFOR XML PATHを使用しますが、これも任意の言語で実行できます。

Stack Exchange Data Explorer Demo

3
Paul White 9