1
ID  State   Name        Department         City
1   O       George      Sales              Phoenix
1   N       George      Sales              Denver
2   O       Michael     Order Process      San diego
2   N       Michael     Marketing          San jose

上記のテーブルの値を次の形式に変換する必要がある状況が発生しました (一番上の行が列名であると考えてください)。

ID  Column      OldValue       New Value
1   Department  Phoenix        Denver
2   Department  Order Process  Marketing
2   City        San diego      San jose

つまり、テーブルの変更された列の値を古いレコードと新しいレコードから取得し、別のテーブルに記録する必要があります。しかし、問題は、そのようなテーブルが多数あり、テーブルごとに列名と列数が異なることです.

誰かが大歓迎される解決策を持ってきたら..!

前もって感謝します。

4

2 に答える 2

2

これは、あなたの望むことですか?

 ID  Column      OldValue      New Value
 1   City        Phoenix       Denver
 2   Department  Order Process Marketing
 2   City        San Diego     San jose

動的コードは次のとおりです。

DECLARE @sqlStm varchar(max);
DECLARE @sqlSelect varchar(max);

DECLARE @tablename varchar(200);
SET @tablename = 'testtable';

-- Assume table has ID column and State column.
SET @sqlSelect = ''
SET @sqlStm = 'WITH old AS
(
   SELECT *
   FROM '+@tablename+'
   WHERE State=''O''
), new AS
(
   SELECT *
   FROM '+@tablename+'
   WHERE State=''N''
)';

  DECLARE @aCol varchar(128)
  DECLARE curCols CURSOR  FOR
    SELECT column_name
    FROM information_schema.columns
    WHERE table_name = @tablename
      AND UPPER(column_name) NOT IN ('ID','STATE')

  OPEN curCols
  FETCH curCols INTO @aCol

  WHILE (@@FETCH_STATUS = 0)
  BEGIN
    SET @sqlStm = @sqlStm +
      ', changed'+@aCol+' AS
(
   SELECT n.ID, '''+@aCol+''' AS [Column], o.['+@aCol+'] AS oldValue, n.['+@aCol+'] AS newValue
   FROM new n
   JOIN old o ON n.ID = o.ID AND n.['+@aCol+'] != o.['+@aCol+']
)'
    IF LEN(@sqlSelect) > 0 SET @sqlSelect = @sqlSelect + ' UNION ALL '

    SET @sqlSelect = @sqlSelect + '
SELECT * FROM changed'+@aCol

    FETCH curCols INTO @aCol

  END

  CLOSE curCols
  DEALLOCATE curCols

    SET @sqlSelect = @sqlSelect + '
ORDER BY id, [Column]'


  PRINT @sqlStm+@sqlSelect

  EXEC (@sqlStm+@sqlSelect)

私のテストでは、次のように出力されました。

WITH old AS
(
   SELECT *
   FROM testtable
   WHERE State='O'
), new AS
(
   SELECT *
   FROM testtable
   WHERE State='N'
), changedName AS
(
   SELECT n.ID, 'Name' AS [Column], o.[Name] AS oldValue, n.[Name] AS newValue
   FROM new n
   JOIN old o ON n.ID = o.ID AND n.[Name] != o.[Name]
), changedDepartment AS
(
   SELECT n.ID, 'Department' AS [Column], o.[Department] AS oldValue, n.[Department] AS newValue
   FROM new n
   JOIN old o ON n.ID = o.ID AND n.[Department] != o.[Department]
), changedCity AS
(
   SELECT n.ID, 'City' AS [Column], o.[City] AS oldValue, n.[City] AS newValue
   FROM new n
   JOIN old o ON n.ID = o.ID AND n.[City] != o.[City]
)
SELECT * FROM changedName UNION ALL 
SELECT * FROM changedDepartment UNION ALL 
SELECT * FROM changedCity
ORDER BY id, [Column]

以下の元の回答:

私はこのようにします-より速いかもしれない他の方法よりも明確だと思うので:

with old as
(
   Select ID, Name,Department,City
   From table1
   Where State='O'
), new as
(
   Select ID, Name,Department,City
   From table1
   Where State='N'
), oldDepartment as
(
   Select ID, 'Department' as Column, o.Department as oldValue, n.Department as newValue
   From new
   join old on new.ID = old.ID and new.Department != old.Department
), oldCity as
(
   Select ID, 'City' as Column, o.City as oldValue, n.City as newValue
   From new
   join old on new.ID = old.ID and new.City != old.City
)
select * from oldDepartment
union all
select * from oldCity

多くのこと (テーブルやインデックスのサイズなど) によっては、ピボット、ケース、またはグループ化を使用するよりも実際には高速になる場合があります。それは本当にあなたのデータに依存します。これが1回限りの実行である場合、私は理解するのが最も簡単な方を選びます.

于 2013-07-19T23:31:23.433 に答える
0

おそらく最もクリーンな方法は、データのピボットを解除してから集計を使用することです。これには、テーブルごとにカスタム コーディングが必要ですが、動的 SQL を使用して一般化できる場合があります。

あなたの特定の例では、何をすべきかを以下に示します。

select id, col,
       max(case when OldNew = 'Old' then value end) as OldValue,
       max(case when OldNew = 'New' then value end) as NewValue
from ((select ID, OldNew, 'Name' as col, Name as value
       from t
      ) union all
      (select ID, OldNew, 'Department' as col, Department as value
       from t
      ) union all
      (select ID, OldNew, 'City' as col, City as value
       from t
      )
     ) unpvt
group by id, col
having max(value) <> min(value) and max(value) is not null;

これは説明用です。union allアンピボットは、特に多数のスキャンがある場合に、を使用するよりも効率的に実行できます。正確な構文はデータベースによって異なりますが、より効率的なバージョンを次に示します。

select id, col,
       max(case when OldNew = 'Old' then value end) as OldValue,
       max(case when OldNew = 'New' then value end) as NewValue
from (select ID, OldNew, cols.col,
             (case when cols.col = 'Name' then Name
                   when cols.col = 'Department' then Department
                   when cols.col = 'City' then City
              end) as value
      from t cross join
           (select 'Name' as col union all select 'Department' union all select 'City') cols
     ) unpvt
group by id, col
having max(value) <> min(value) and max(value) is not null;

union allこれは、バージョンのように列ごとに 1 回ではなく、通常、テーブルを 1 回だけスキャンするため、より効率的です。

どちらのバージョンでも、すべての列が同じ文字型であるという暗黙の前提があります。これは、すべての値が 1 つの列にある変換先の形式では暗黙的です。

于 2013-07-19T23:31:16.543 に答える