规范化表:在一系列行中查找唯一列(Oracle 10.x)

时间:2010-09-29 08:14:27

标签: oracle normalization denormalization database-normalization

我有一个具有以下结构的表:

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(以及一个Person的许多版本)数据保存在一个表中。我的愿望是规范化该表,但是,由于该表包含大量数据(许多列),我需要确定哪些列应该转到Workers表,哪些列转到Persons表。结果应该是这样的:

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

为此,我需要分析哪些数据在Person的范围上与所有唯一的Persons(在WorkerPersons中由PersonID分隔)不同。例如:

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。但是,第2列和第3列值会在不同版本的Person上更改,因此这些值应该移动到Person表。

没想象,我有大约10个这样的表需要规范化,每个表大约有40列。 Eeach表可容纳约500k至5m的行。

我需要一个脚本来帮助我分析哪些列移动到哪里。我需要一个脚本,输出在整个表中唯一Person范围内更改的所有列。我不知道如何做到这一点。我尝试使用LAG分析函数与下一行进行比较,但是世界上输出更改列的方式超出了我的范围。

请告知。

祝福, 安德鲁

2 个答案:

答案 0 :(得分: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中没有column_name s。

<强> 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。

答案 1 :(得分:1)

谢谢,但我通过让Excel在表架构信息上创建一系列选择来解决它。它生成的最终查询是一个很长的选择列表,但它可以工作(虽然它运行超过一个小时)。 “核心查询”(实际上是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")