2010-08-28 26 views
3

Je travaille avec une table MyISAM fiche 12 millions avec le nom, l'adresse, les champs de genre et date de naissance:approche MySQL: grandes auto-jointures pour définir des valeurs?

ID SURNAME GENDER  BDATE COUNTY   ADDRESS   CITY 
1 JONES  M 1954-11-04  015  51 OAK ST SPRINGFIELD 
2  HILL  M 1981-02-16  009  809 PALM DR JONESVILLE 
3  HILL  F 1979-06-23  009  809 PALM DR JONESVILLE 
4  HILL  F 1941-10-11  009  809 PALM DR JONESVILLE 
5 SMITH  M 1914-07-27  035 1791 MAPLE AVE  MAYBERRY 
6 SMITH  F 1954-02-05  035 1791 MAPLE AVE  MAYBERRY 
7 STEVENS  M 1962-05-05  019 404 CYPRESS ST  MAYBERRY 
.  .  .   .  .    . 
.  .  .   .  .    . 
.  .  .   .  .    . 

Nom de famille, bdate et champs d'adresse sont indexés. Mon but est d'ajouter un champ pour l'état matrimonial inféré, défini par les critères suivants: Pour chaque enregistrement, s'il existe un autre enregistrement dans le tableau avec (1) un nom de famille identique, (2) un genre différent, (3) une adresse identique , et (4) une différence d'âge de moins de 15 ans, set marié = T; autre ensemble = F. marié

être un novice SQL, mon approche initiale était d'ajouter un champ matrimonial que par défaut « F », puis utiliser un autojointure pour définir MARIÉ = T.

ALTER TABLE MY_TABLE 
ADD COLUMN MARRIED CHAR(1) NOT NULL DEFAULT 'F'; 

UPDATE MY_TABLE T1, MY_TABLE T2 
SET T1.MARRIED = 'T' WHERE 
    T1.SURNAME = T2.SURNAME AND 
    T1.GENDER != T2.GENDER AND 
    T1.ADDRESS = T2.ADDRESS AND 
    T1.CITY = T2.CITY AND 
    ABS(YEAR(T1.BDATE)-YEAR(T2.BDATE)) < 15; 

Bien que cela fonctionne bien sur les petites tables, j'ai appris rapidement que je vais probablement prendre ma retraite avant que ce processus se termine sur une table de 12 millions de lignes. Mes connaissances SQL sont très limitées, donc je suis sûr que c'est une approche sous-optimale. Des alternatives suggérées? Peut-être indexer NOM + ADRESSE + VILLE? Grouper par ADRESSE + CITY en premier? Un meilleur design de table? Toute suggestion serait appréciée.

Merci d'avance pour votre aide!

Répondre

0

Eh bien, l'indexation de tous les champs de votre clause WHERE épelerait définitivement la requête.

Cela signifie NOM, GENRE, ADRESSE, VILLE et BDATE.

annother chose que vous pouvez essayer est de définir les règles pour limiter le résultat dans la partie ON:

UPDATE MY_TABLE T1 
    LEFT JOIN MY_TABLE T2 
    ON T1.SURNAME = T2.SURNAME 
    AND T1.GENDER != T2.GENDER 
    AND T1.CITY = T2.CITY 
    SET T1.MARRIED = 'T' 
    WHERE ABS(YEAR(T1.BDATE)-YEAR(T2.BDATE)) < 15; 
1

Méfiez-vous des frères & sœurs!

+0

En effet. C'est une idée absurde. Pendant de nombreuses années, j'ai vécu à la même adresse que 4 femmes avec mon nom de famille dans les 15 ans de mon âge, et n'a été marié à aucun d'entre eux. Et le mariage avec eux serait tout à fait illégal dans ce pays. –

+0

Bien que je suis d'accord avec les deux sentiments, c'est un commentaire - pas une réponse. –

1

Je voudrais essayer quelques variations pour voir qui effectue le meilleur:

Version 1 en utilisant un simple Exists mais en utilisant date_add au lieu de la fonction de valeur ABS:

Update My_Table 
Set Married = 'T' 
Where Exists (
       Select 1 
       From My_Table As T2 
       Where T2.SurName = My_Table.SurName 
        And T2.Gender != My_Table.Gender 
        And T2.Address = My_Table.Address 
        And T2.City = My_Table.City 
        And (
         T2.BDate > Date_Add(My_Date.BDate, Interval 15 Year) 
         Or T2.BDate < Date_Add(My_Date.BDate, Interval -15 Year) 
         ) 
       ) 

Version 2 en utilisant un UNION ALL

Update My_Table 
Set Married = 'T' 
Where Exists (
       Select 1 
       From My_Table As T2 
       Where T2.SurName = My_Table.SurName 
        And T2.Gender != My_Table.Gender 
        And T2.Address = My_Table.Address 
        And T2.City = My_Table.City 
        And T2.BDate > Date_Add(My_Date.BDate, Interval 15 Year) 
       Union All 
       Select 1 
       From My_Table As T2 
       Where T2.SurName = My_Table.SurName 
        And T2.Gender != My_Table.Gender 
        And T2.Address = My_Table.Address 
        And T2.City = My_Table.City 
        And T2.BDate < Date_Add(My_Date.BDate, Interval -15 Year 
       ) 

Version 3 en utilisant une jointure interne et date_add

Update My_Table As T1 
    Join My_Table As T2 
      On T2.SurName = T1.SurName 
       And T2.Gender != T1.Gender 
       And T2.Address = T1.Address 
       And T2.City = T1.City 
Set Married = 'T' 
Where T1.BDate > Date_Add(T2.BDate, Interval 15 Year) 
     Or T1.BDate < Date_Add(T2.BDate, Interval -15 Year) 

Je pense que Reculant de SQL, qui essaie de déduire si deux personnes sont mariées en fonction de l'information fournie va être lourde de problèmes. Il ne tient pas compte des couples dont la variance d'âge est supérieure à 15 ans (Anna Nicole Smith, personne?) Et ne tient pas compte des frères et sœurs ni des deux personnes qui se marient mais ne changent pas de nom.