@@ -69,29 +69,31 @@ def _sql_candidats(db) -> str:
6969 FROM entities e
7070 WHERE NOT ({ ref } )
7171),
72- -- Pour chaque nom, le nombre de libellés que porte la version rattachée
73- -- à une source : c'est lui qui détermine le pouvoir de rapprochement.
72+ -- Libellés portés par chaque entité, sous forme d'ensemble normalisé.
73+ -- Compter ne suffit pas : deux copies peuvent porter autant de libellés tout
74+ -- en couvrant des graphies différentes. Seule l'INCLUSION garantit qu'aucune
75+ -- graphie ne disparaît.
76+ libelles AS (
77+ SELECT entity_id, ARRAY_AGG(DISTINCT name_normalized) AS jeu
78+ FROM entity_names GROUP BY entity_id
79+ ),
7480officielles AS (
75- SELECT UPPER(TRIM(e.primary_name)) AS cle, MAX(k.n) AS n_libelles
81+ SELECT UPPER(TRIM(e.primary_name)) AS cle,
82+ ARRAY_AGG(DISTINCT g) AS jeu
7683 FROM entities e
7784 JOIN source_records sr ON sr.entity_id = e.id
78- JOIN (SELECT entity_id, COUNT(*) n FROM entity_names GROUP BY entity_id) k
79- ON k.entity_id = e.id
85+ JOIN libelles l ON l. entity_id = e.id
86+ CROSS JOIN LATERAL UNNEST(l.jeu) AS g
8087 GROUP BY UPPER(TRIM(e.primary_name))
81- ),
82- richesse AS (
83- SELECT o.id, COUNT(en.id) AS n_libelles
84- FROM orphelines o LEFT JOIN entity_names en ON en.entity_id = o.id
85- GROUP BY o.id
8688)
8789SELECT o.id, o.primary_name, o.entity_type
8890 FROM orphelines o
8991 JOIN officielles f ON f.cle = o.cle
90- JOIN richesse r ON r.id = o.id
91- -- Une copie qui porte PLUS de libellés que sa jumelle est conservée :
92- -- la supprimer ferait cesser de reconnaître des graphies aujourd'hui
93- -- couvertes. Un alias perdu, c'est une personne qu'on ne détecte plus.
94- WHERE r.n_libelles <= f.n_libelles
92+ LEFT JOIN libelles lo ON lo.entity_id = o.id
93+ -- La copie n'est supprimée que si TOUTES ses graphies sont déjà couvertes
94+ -- par la version rattachée à une source. Une seule graphie manquante suffit
95+ -- à la conserver : un alias perdu, c'est une personne qu'on ne détecte plus.
96+ WHERE COALESCE(lo.jeu, ARRAY[]::text[]) <@ f.jeu
9597"""
9698
9799
@@ -113,30 +115,30 @@ def analyser(db) -> dict:
113115 # Contrôle de non-régression du filtrage : supprimer une copie qui porte
114116 # PLUS de libellés que sa jumelle ferait perdre du pouvoir de rapprochement.
115117 # Un nom d'alias en moins, c'est une personne qu'on cesse de reconnaître.
116- # Copies plus riches que leur jumelle : exclues de la suppression par la
117- # requête ci-dessus, comptées ici pour être signalées.
118- ref2 = ref
118+ # Copies conservées parce qu'elles couvrent des graphies absentes de la
119+ # version rattachée à une source.
119120 plus_riches = db .execute (text (f"""
120- WITH orph AS (
121- SELECT e.id, UPPER(TRIM(e.primary_name)) AS cle
122- FROM entities e WHERE NOT ( { ref2 } )
121+ WITH libelles AS (
122+ SELECT entity_id, ARRAY_AGG(DISTINCT name_normalized) AS jeu
123+ FROM entity_names GROUP BY entity_id
123124 ),
124- n_orph AS (
125- SELECT o.id, o.cle, COUNT(en.id) AS n
126- FROM orph o LEFT JOIN entity_names en ON en.entity_id = o.id
127- GROUP BY o.id, o.cle
125+ orph AS (
126+ SELECT e.id, UPPER(TRIM(e.primary_name)) AS cle
127+ FROM entities e WHERE NOT ({ ref } )
128128 ),
129- n_off AS (
130- SELECT UPPER(TRIM(e.primary_name)) AS cle, MAX(k.n ) AS n
129+ off AS (
130+ SELECT UPPER(TRIM(e.primary_name)) AS cle, ARRAY_AGG(DISTINCT g ) AS jeu
131131 FROM entities e
132132 JOIN source_records sr ON sr.entity_id = e.id
133- JOIN (SELECT entity_id, COUNT(*) n FROM entity_names GROUP BY entity_id) k
134- ON k.entity_id = e.id
133+ JOIN libelles l ON l. entity_id = e.id
134+ CROSS JOIN LATERAL UNNEST(l.jeu) AS g
135135 GROUP BY UPPER(TRIM(e.primary_name))
136136 )
137- SELECT COUNT(*) FROM n_orph o
138- JOIN n_off f ON f.cle = o.cle
139- WHERE o.n > f.n
137+ SELECT COUNT(*)
138+ FROM orph o
139+ JOIN off f ON f.cle = o.cle
140+ LEFT JOIN libelles lo ON lo.entity_id = o.id
141+ WHERE NOT (COALESCE(lo.jeu, ARRAY[]::text[]) <@ f.jeu)
140142 """ )).scalar ()
141143 return {"total" : total , "sans_source" : sans_source , "protegees" : protegees ,
142144 "candidats" : candidats , "plus_riches" : plus_riches ,
@@ -174,7 +176,7 @@ def main(argv: list[str]) -> int:
174176 print (f" sans enregistrement source: { a ['sans_source' ]:>7} " )
175177 print (f" dont référencées ailleurs : { a ['protegees' ]:>7} (conservées)" )
176178 print (f" doublons supprimables : { a ['candidats' ]:>7} " )
177- print (f" dont plus riches en alias : { a ['plus_riches' ]:>7} "
179+ print (f" dont graphies non couvertes : { a ['plus_riches' ]:>7} "
178180 + (" (conservées : elles couvrent des graphies que la version"
179181 " rattachée à une source ignore)" if a ["plus_riches" ] else "" ))
180182 if a ["exemples" ]:
0 commit comments