# Gestione ID nella Sincronizzazione Multi-Database

## Il Problema degli ID Auto-Increment

### Scenario Tipico

Considera 3 database indipendenti con le loro tabelle `tbl_editori`:

```
DB1 (Pordenone Legge):
  casaEditriceId | denominazione
  ----------------|---------------
  1              | Mondadori
  2              | Einaudi
  3              | Feltrinelli

DB2 (Geografie):
  casaEditriceId | denominazione
  ----------------|---------------
  1              | Adelphi
  2              | Mondadori      ← Stesso editore, ID diverso!
  3              | Sellerio

DB3 (Milano FVG):
  casaEditriceId | denominazione
  ----------------|---------------
  1              | Feltrinelli    ← Stesso editore, ID diverso!
  2              | Rizzoli
  3              | Mondadori      ← Stesso editore, ID diverso ancora!
```

### Problema 1: Conflitto ID Editori

Se copiassimo direttamente con gli ID originali:
```sql
-- ❌ SBAGLIATO
INSERT INTO db_target.tbl_editori 
  (casaEditriceId, denominazione)
VALUES 
  (1, 'Mondadori'),  -- Potrebbe sovrascrivere "Adelphi"!
  (3, 'Feltrinelli'); -- Potrebbe sovrascrivere "Sellerio"!
```

### Problema 2: Foreign Key Addetti

Gli addetti hanno `editoreId` che punta agli editori:

```
DB1:
  tbl_addetti:
    id | nome        | editoreId (→ Mondadori)
    ---|-------------|------------------------
    1  | Mario Rossi | 1

DB2:
  tbl_addetti:
    id | nome        | editoreId (→ Mondadori)
    ---|-------------|------------------------
    5  | Mario Rossi | 2                 ← Stesso editore, ID diverso!
```

Se copiassimo `editoreId=1` da DB1 a DB2, punterebbe ad "Adelphi" invece di "Mondadori"!

## La Soluzione: Chiavi Naturali + JOIN Dinamico

### Strategia per Editori

**Non usare mai casaEditriceId nella sincronizzazione**

```sql
-- ✅ CORRETTO - Omette casaEditriceId
INSERT IGNORE INTO pnlegge_common.tbl_editori 
  (denominazione, indirizzo, cap, ...) 
SELECT 
  denominazione, indirizzo, cap, ...
FROM source_db.tbl_editori;
```

**Risultato nel database comune:**
```
pnlegge_common.tbl_editori:
  casaEditriceId | denominazione | fonte
  ----------------|---------------|------------------
  1              | Adelphi       | (da DB2)
  2              | Einaudi       | (da DB1)
  3              | Feltrinelli   | (da DB1 o DB3, stesso editore)
  4              | Mondadori     | (da DB1, DB2 o DB3, stesso editore)
  5              | Rizzoli       | (da DB3)
  6              | Sellerio      | (da DB2)
```

**Quando si redistribuisce a DB1:**
```
DB1.tbl_editori (dopo sincronizzazione):
  casaEditriceId | denominazione | note
  ----------------|---------------|---------------------------
  1              | Mondadori     | (esisteva già)
  2              | Einaudi       | (esisteva già)
  3              | Feltrinelli   | (esisteva già)
  4              | Adelphi       | ← NUOVO (AUTO_INCREMENT=4)
  5              | Rizzoli       | ← NUOVO (AUTO_INCREMENT=5)
  6              | Sellerio      | ← NUOVO (AUTO_INCREMENT=6)
```

### Strategia per Addetti

**Usa JOIN per rimappare editoreId**

```sql
-- ✅ CORRETTO - JOIN su denominazione editore
INSERT INTO target_db.tbl_addetti 
  (cognome, nome, editoreId, ...)
SELECT 
  a.cognome,
  a.nome,
  e_target.casaEditriceId,  -- ID corretto nel DB target!
  ...
FROM pnlegge_common.tbl_addetti a
INNER JOIN pnlegge_common.tbl_editori e_common 
  ON a.editoreId = e_common.casaEditriceId
INNER JOIN target_db.tbl_editori e_target 
  ON e_common.denominazione = e_target.denominazione  -- Chiave naturale!
```

**Esempio pratico:**

Database Comune:
```
tbl_editori:
  casaEditriceId=47 | denominazione='Mondadori'

tbl_addetti:
  id=123 | nome='Mario Rossi' | editoreId=47
```

Quando si sincronizza verso DB1 (dove Mondadori ha ID=1):
```sql
-- Il JOIN traduce automaticamente:
-- editoreId: 47 (comune) → 1 (DB1)

Risultato in DB1.tbl_addetti:
  id=<auto> | nome='Mario Rossi' | editoreId=1  ← Punta correttamente a Mondadori!
```

Quando si sincronizza verso DB2 (dove Mondadori ha ID=2):
```sql
-- Il JOIN traduce automaticamente:
-- editoreId: 47 (comune) → 2 (DB2)

Risultato in DB2.tbl_addetti:
  id=<auto> | nome='Mario Rossi' | editoreId=2  ← Punta correttamente a Mondadori!
```

## Implementazione nella Procedura

### Fase 1A: Raccolta Editori
```sql
-- Non include casaEditriceId nella INSERT
INSERT IGNORE INTO pnlegge_common.tbl_editori 
  (denominazione, indirizzo, ...) 
SELECT denominazione, indirizzo, ...
FROM source_db.tbl_editori;
```

### Fase 1B: Raccolta Addetti
```sql
-- JOIN per mappare editoreId al valore nel DB comune
INSERT INTO pnlegge_common.tbl_addetti 
  (cognome, nome, editoreId, ...)
SELECT 
  a.cognome, a.nome,
  e_common.casaEditriceId,  -- ID nel DB comune
  ...
FROM source_db.tbl_addetti a
INNER JOIN source_db.tbl_editori e_source 
  ON a.editoreId = e_source.casaEditriceId
INNER JOIN pnlegge_common.tbl_editori e_common 
  ON e_source.denominazione = e_common.denominazione;
```

### Fase 2A: Distribuzione Editori
```sql
-- Non include casaEditriceId nella INSERT
INSERT IGNORE INTO target_db.tbl_editori 
  (denominazione, indirizzo, ...) 
SELECT denominazione, indirizzo, ...
FROM pnlegge_common.tbl_editori;
```

### Fase 2B: Distribuzione Addetti
```sql
-- JOIN per mappare editoreId al valore nel DB target
INSERT INTO target_db.tbl_addetti 
  (cognome, nome, editoreId, ...)
SELECT 
  a.cognome, a.nome,
  e_target.casaEditriceId,  -- ID nel DB target
  ...
FROM pnlegge_common.tbl_addetti a
INNER JOIN pnlegge_common.tbl_editori e_common 
  ON a.editoreId = e_common.casaEditriceId
INNER JOIN target_db.tbl_editori e_target 
  ON e_common.denominazione = e_target.denominazione;
```

## Vantaggi di questa Soluzione

### 1. **Indipendenza ID**
Ogni database mantiene i propri ID auto-increment senza conflitti.

### 2. **Integrità Referenziale**
Le foreign key puntano sempre all'editore corretto, indipendentemente dagli ID.

### 3. **Idempotenza**
Puoi eseguire la sincronizzazione più volte senza creare duplicati.

### 4. **Robustezza**
Se un database ha ID frammentati (1,2,5,7,12...), non ci sono problemi.

### 5. **Audit Trail**
Ogni database mantiene i suoi `createTime`/`createUserId` originali.

## Gestione Duplicati

### Editori
- **Chiave**: `denominazione` (UNIQUE KEY)
- **Metodo**: `INSERT IGNORE` - salta se esiste già
- **Criterio**: Due editori con denominazione identica = stesso editore

### Addetti
- **Chiave**: `cognome + nome + denominazione_editore`
- **Metodo**: `WHERE NOT EXISTS` con subquery
- **Criterio**: Stesso nome che lavora per stesso editore = stesso addetto

```sql
WHERE NOT EXISTS (
  SELECT 1 
  FROM target_db.tbl_addetti a2 
  INNER JOIN target_db.tbl_editori e2 
    ON a2.editoreId = e2.casaEditriceId 
  WHERE a2.cognome = a.cognome 
    AND a2.nome = a.nome 
    AND e2.denominazione = e_common.denominazione
)
```

## Casi Edge

### Omonimi in Editori Diversi
```
Mario Rossi che lavora per Mondadori
Mario Rossi che lavora per Einaudi
```
✅ Entrambi vengono sincronizzati (editori diversi)

### Stesso Editore, Grafia Diversa
```
DB1: "Mondadori"
DB2: "Mondadori "  (spazio finale)
DB3: "MONDADORI"
```
❌ Vengono considerati editori diversi

**Soluzione**: Normalizzare denominazioni prima della sincronizzazione:
```sql
UPDATE tbl_editori 
SET denominazione = TRIM(UPPER(denominazione));
```

### Editore Rinominato
```
DB1: "Casa Editrice XYZ"
DB2: "XYZ Editore"  (nuovo nome)
```
❌ Vengono considerati editori diversi

**Soluzione**: Standardizzare manualmente prima della sincronizzazione.

## Performance

### Ottimizzazioni
1. **Indici su denominazione**: 
   ```sql
   CREATE INDEX idx_denominazione ON tbl_editori(denominazione);
   ```

2. **Indici compositi su addetti**:
   ```sql
   CREATE INDEX idx_addetto_editore 
   ON tbl_addetti(cognome, nome, editoreId);
   ```

3. **Batch processing**: La procedura processa un DB alla volta

### Stima Tempi
- 100 editori x 8 DB = ~2-3 secondi
- 500 addetti x 8 DB = ~5-10 secondi
- Totale procedura completa = ~15-20 secondi

## Verifica Sincronizzazione

### Test di Integrità
```sql
-- Verifica che tutti gli addetti abbiano editori validi
SELECT a.*, e.denominazione
FROM tbl_addetti a
LEFT JOIN tbl_editori e ON a.editoreId = e.casaEditriceId
WHERE e.casaEditriceId IS NULL;
-- Se vuoto → OK, altrimenti ci sono orphan records

-- Conta editori comuni a tutti i DB
SELECT denominazione, COUNT(*) as occorrenze
FROM (
  SELECT DISTINCT denominazione FROM db1.tbl_editori
  UNION ALL
  SELECT DISTINCT denominazione FROM db2.tbl_editori
  UNION ALL
  SELECT DISTINCT denominazione FROM db3.tbl_editori
) AS unione
GROUP BY denominazione
HAVING occorrenze >= 3
ORDER BY occorrenze DESC;
```

## Rollback e Recovery

### Backup Pre-Sincronizzazione
```bash
# Prima di eseguire, backup di sicurezza
for db in $(mysql -u root -p -e "SHOW DATABASES LIKE 'pordenonelegge_it_2026%'" -s); do
  mysqldump -u root -p $db tbl_editori tbl_addetti > backup_${db}_$(date +%Y%m%d).sql
done
```

### Restore Selettivo
```bash
# Ripristina solo un database
mysql -u root -p pordenonelegge_it_2026_fuoricitta < backup_pordenonelegge_it_2026_fuoricitta_20260120.sql
```

## Conclusione

Questa strategia garantisce:
- ✅ Nessun conflitto di ID
- ✅ Integrità referenziale preservata
- ✅ Sicurezza da duplicati
- ✅ Scalabilità multi-database
- ✅ Manutenibilità a lungo termine

La chiave è: **usare sempre la denominazione come chiave naturale, mai gli ID tecnici**.
