3

次の構造のテーブルがあります。

WorkerPersons
-------------------------------
ID          (PK)
PersonID    (Indicates which version of Person the record describes)
SomeColumn1 (data specific to Worker)
SomeColumn2 (data specific to Person)
....
SomeColumnN
-------------------------------

ご覧のとおり、これは非正規化されたテーブルであり、Worker と Person (および 1 つの Person の多くのバージョン) の両方のデータを 1 つのテーブルに保持します。私の願いはそのテーブルを正規化することですが、テーブルには大量のデータ (多数の列) が含まれているため、どの列を Workers テーブルに、どの列を Persons テーブルに移動する必要があるかを確認する必要があります。結果は次のようになります。

Workers                 Persons
----------------------- ---------------------
ID                      ID
PersonID (now a FK)     PersonColumn1
WorkerColumn1           PersonColumn2
WorkerColumn2           ...
...                     PersonColumnN
WorkerColumnN
----------------------- ---------------------

そのためには、すべての一意の Person (WorkerPersons の PersonID で区切られている) で Person の範囲が異なるデータを分析する必要があります。例えば:

WorkerPersons
-------------------------------------------------------
ID      PersonID      Column1      Column2      Column3
-------------------------------------------------------
1       PersonA       10.1         John Doe     Single
2       PersonA       10.1         John Doe     Single
3       PersonA       10.1         John Doe     Married
4       PersonB       09.2         Sully        Single
5       PersonB       09.2         Sullivan     Single

この場合、PersonA には 3 つのバージョンがあり、PersonB には 2 つのバージョンがあります。Column1 の値は Person のすべてのバージョンで常に同じであり、その列をテーブル Worker に移動できます。ただし、Column 2 と Column3 の値は Person のバージョンが異なると変化するため、これらの値は Person テーブルに移動する必要があります。

想像もつきませんが、このような正規化が必要なテーブルが約 10 個あり、それぞれに約 40 列あります。各テーブルには、約 500k から 5m の行が保持されます。

どの列をどこに移動するかを分析するのに役立つスクリプトが必要です。テーブル全体で一意の Person の範囲で変化するすべての列を出力するスクリプトが必要です。しかし、それを行う方法はわかりません。次の行と比較するためにLAG分析機能を試しましたが、変更された列をどのように出力するかは私にはわかりません。

お知らせ下さい。

よろしくお願いします、 アンドリュー

4

2 に答える 2

3

10 個のテーブルは多くないので、(何らかの) 疑似コードを次に示します。

for each table_name in tables
  for each column_name in columns
    case (exists (select 1
          from table_name
          group by PersonID
          having min(column_name) = max(column_name))
       when true then 'Worker'
       when false then 'Person'
    end case
  end for
end for

情報スキーマと動的クエリを使用すると、上記の適切な PL/SQL を作成したり、コア クエリを使用して好みの言語でスクリプトを作成したりできます。

編集: 上記は、NULLに sがないことを前提としていcolumn_nameます。

EDIT2:コアクエリ の他のバリアントは

SELECT 1
FROM 
(SELECT COUNT(DISTINCT column_name) AS distinct_values_by_pid
FROM table_name
GROUP BY PersonID) T
HAVING MIN(distinct_values_by_pid) = MAX(distinct_values_by_pid)

PersonID ごとのすべての値が同じ場合、行が返されます。(このクエリにも NULLS に関する問題がありますが、私は NULL を別の問題と考えています。上記のクエリの目的で、NULL をドメイン外の値にいつでもキャストできます)

上記のクエリは、次のようにも記述できます。

SELECT MIN(c1)=MAX(c1), MIN(c2)=MAX(c2), ...
FROM 
(SELECT COUNT(DISTINCT column_name_1) AS c1, COUNT(DISTINCT column_name_2) AS c2, ...
FROM table_name
GROUP BY PersonID) T

複数の列を同時にテストし、'Workers' に属する列の場合は true を返し、'Persons' に入る列の場合は false を返します。

于 2010-09-29T08:34:08.947 に答える
1

ありがとうございます。テーブル スキーマ情報に対して一連の選択を Excel に作成させることで解決しました。生成された最終的なクエリは選択の長いリストでしたが、機能します (1 時間以上実行されますが)。「コア クエリ」(実際にはコア クエリを作成するための Excel の数式):

=IF(AND(C17<>"CLOB";C17<>"NCLOB");"SELECT '"&A17&".'||initcap('"&B17&"') description,
decode(count(*),0,'SAME OVE VERSIONS','DIFFERENT OVER VERSIONS') values FROM (SELECT 
objektid, count(DISTINCT nvl("&B17&","&IF(C17="DATE";"'01.02.0004'";IF(C17="VARCHAR2"
;"'!#¤¤%¤(%#¤%AS'";"-1234561"))&")) OVER (PARTITION BY objectid) arv FROM "&A17&") 
WHERE number > 1 union all";"SELECT '"&A17&".'||initcap('"&B17&"') description, 'CLOB
field' values from dual union all")
于 2010-10-02T15:41:05.110 に答える