Normalización de una tabla: Encontrar columnas únicas sobre series de filas (Oracle 10.x)
-
26-09-2019 - |
Pregunta
Tengo una tabla con la siguiente estructura:
WorkerPersons
-------------------------------
ID (PK)
PersonID (Indicates which version of Person the record describes)
SomeColumn1 (data specific to Worker)
SomeColumn2 (data specific to Person)
....
SomeColumnN
-------------------------------
Como puede ver, es una tabla desnormalizada, que contiene datos de trabajador y persona (y muchas versiones de una persona) en una tabla. Sin embargo, mi deseo es normalizar esa tabla, ya que la tabla contiene muchos datos (muchas columnas), debo asegurarme de qué columnas deben ir a la tabla de trabajadores y qué columnas a la tabla de personas. El resultado debe ser así:
Workers Persons
----------------------- ---------------------
ID ID
PersonID (now a FK) PersonColumn1
WorkerColumn1 PersonColumn2
WorkerColumn2 ...
... PersonColumnN
WorkerColumnN
----------------------- ---------------------
Para hacer eso, necesito analizar qué datos difieren en el alcance de la persona sobre todas las personas únicas (que están separados por personajes en trabajadores). Por ejemplo:
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
En este caso, hay 3 versiones en Persona y 2 versiones de Personb. Los valores de la columna1 son siempre los mismos en todas las versiones de la persona, y podemos mover esa columna al trabajador de la tabla. Pero los valores de la columna 2 y la columna3 cambian en diferentes versiones de la persona, por lo que esos valores deben moverse a la tabla de personas.
No Imagine, tengo alrededor de 10 tablas como esta que deben normalizarse, con aproximadamente 40 columnas en cada una. La tabla Eeach mantiene aproximadamente 500k a 5 m filas.
Necesito un script que me ayude a analizar qué columnas mover a dónde. Necesito un script que genere todas las columnas que cambian en el alcance de una persona única en toda la tabla. Sin embargo, no tengo ideas sobre cómo hacer eso. Experimenté con la función analítica de LAG para comparar con la siguiente fila, pero cómo en el mundo de la salida de las columnas cambió está más allá de mí.
Por favor avise.
Mis mejores deseos, Andrew
Solución
Dado que 10 tablas no son mucho, aquí está (algún tipo de) código pseudo
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
Con el esquema de información y las consultas dinámicas, puede hacer el PL/SQL adecuado anterior o tomar la consulta principal y escribirlo en su idioma favorito.
EDITAR:Lo anterior asume que no NULL
pecado column_name
.
Edit2:Otras variantes de la consulta principal pueden ser
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)
Que devolverá una fila si todos los valores por persona son los mismos. (Esta consulta también tiene problemas con los nulos, pero considero que los nulos un problema separado; siempre puede lanzar un valor nulo a algún valor fuera de dominio para los fines de la consulta anterior)
La consulta anterior también se puede escribir como
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
Que probará varias columnas al mismo tiempo que devuelve verdadero para columnas que pertenecen a 'trabajadores' y falsos para columnas que deberían entrar en 'personas'.
Otros consejos
Gracias, pero lo resolví dejando que Excel creara una serie de seleccionadas sobre la información del esquema de la tabla. La consulta final que generó fue una larga lista de selecciones, pero funciona (aunque se ejecuta más de una hora). La "consulta principal" (en realidad una fórmula en Excel para crear a la consulta del núcleo):
=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")