2012-04-26 27 views
5

ho una tabella di database che rappresenta una gerarchia di file e directory, con la seguente struttura (semplificato):approccio efficiente per l'aggiornamento di massa di una tabella gerarchica

 
ItemId  int 
Path   text 
Type   int  (0 for files, 1 for directories) 
ParentId  int 
BackupTime datetime 

Attualmente la colonna BackupTime viene utilizzato solo per i file , è impostato su null per le directory.

Ora devo compilare questa colonna anche per le directory: deve essere il minimo BackupTime di tutti i discendenti (file e directory).

Questo (ingenuo e inefficiente) domanda illustra quello che voglio fare:

update Items i 
set BackupTime = (select min(BackupTime) 
        from Items d 
        where d.Path like i.Path || '%' 
        and d.Type = 0) 
where i.Type = 1 

Il mio problema è che io non riesco a trovare un approccio efficiente. La query sopra prende troppo a lungo su grandi volumi di dati (questa tabella contiene spesso più di 100K righe)

Probabilmente sarebbe più veloce per cercare le min(BackupTime) solo sui figli diretti:

update Items i 
set BackupTime = (select min(BackupTime) 
        from Items d 
        where d.ParentId = i.ItemId) 
where i.Type = 1 

Ma per questo per funzionare, devo assicurarmi che i discendenti vengano aggiornati prima dei loro antenati, quindi devo percorrere la gerarchia in modo ricorsivo dal basso verso l'alto. Il problema è che non ho un modo semplice per sapere quali oggetti sono i più profondi nella gerarchia. Sto usando SQLite, quindi non posso usare le query gerarchiche.

Qualche idea su come farlo in modo efficiente?

Idealmente, preferirei essere in grado di farlo in una singola query UPDATE, ma se non è possibile io sono aperto ad altre opzioni, purché siano efficienti

risposta

1

Questo è un colpo nel buio, ma potrebbe funzionare. È un tentativo di gestire manualmente il problema dal basso. (Non conosco i limiti di sqlite, ma probabilmente è SQL-92 standard e, si spera, ok).

Passaggio 1: decidere come gestire le directory vuote. Penso che la soluzione qui funzioni solo se non ci sono directory vuote o se le directory vuote vengono inizialmente aggiornate in modo che abbiano un BackupTime non Null artificiale. (Quello che dovrebbe essere BackupTime artificiale potrebbe essere importante, a seconda di come si mantiene la colonna BackupDate quando ci sono modifiche ai dati. L'uso della data corrente o di una data futura artificiale potrebbe funzionare, ma dovresti pensarci.)

Fase 2. Eseguire la seguente query più volte fino a quando non più righe sono interessate:

update Items i set 
    BackupTime = (
     select min(BackupTime) 
     from Items d 
     where d.ParentId = i.ItemId 
    ) 
    where i.Type = 1 
    and i.BackupTime is null 
    and not exists (
    select * 
    from Items d 
    where d.ParentId = i.ItemId 
    and d.Type = 1 
    and d.BackupTime is null 
) 

In altre parole, aggiornare il BackUpTime per le directory in cui è necessario e anche avere tutte le informazioni: quando il loro BackUpTime è nullo e non contiene sottodirectory il cui valore BackupTime è nullo.

Quindi la prima volta che si esegue questa operazione, verrà impostato BackupTime per tutte le directory che non contengono sottodirectory, solo file. La seconda volta, imposterà BackupTime per le directory che contengono sottodirectory, ma senza sottodirectory secondarie.

Potrebbe essere possibile gestire il problema della directory vuota impostando BackupTime in coalesce ((selezionare ...), current_timestamp).

+0

Grazie, ci provo! –

+0

OK, ci sono voluti 5 secondi per elaborare un DB con 100000 elementi ... è abbastanza buono;).Ho provato con un database "fittizio", quindi è necessario verificarne uno reale, ma sono certo che avrà prestazioni simili. BTW, l'ultima condizione con 'not exists' non è necessaria: se c'è un null,' min' restituirà null, quindi alla fine darà lo stesso risultato, con meno iterazioni (14 invece di 27 nel mio test) –

+0

MIN restituirà NULL se i valori * only * sono NULL. MIN NON restituirà NULL se NULL e altri valori sono aggregati. NON ESISTE è necessario per garantire che le iterazioni vadano dal basso verso l'alto. Se rimuovi il NON ESISTE, ottieni risultati sbagliati! Supponiamo che/dir1/contenga due elementi: 1) un file con BackupTime 4/12 e 2) una directory/dir2/che contenga 1 file con tempo di backup 4/9. Senza NOT EXISTS,/dir1/otterrà un BackupTime non corretto di 4/12 durante la prima iterazione. Con NOT EXISTS, aspetterà fino alla prossima iterazione. Le poche iterazioni che vedi suggeriscono queste risposte sbagliate. –