2012-04-26 12 views
5

Tengo una tabla de base de datos que representa una jerarquía de archivos y directorios, con la siguiente estructura (simplificada):enfoque eficiente para la actualización masiva de una tabla jerárquica

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

Actualmente la columna de la BackupTime sólo se utiliza para archivos , está configurado como nulo para directorios.

Ahora necesito completar esta columna también para los directorios: debe ser el mínimo BackupTime de todos los descendientes (archivos y directorios).

Esta consulta (ingenua e ineficiente) ilustra lo que quiero hacer:

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 

Mi problema es que me parece que no puede encontrar un enfoque eficiente. La consulta anterior lleva demasiado tiempo en grandes volúmenes de datos (esta tabla contiene a menudo más de 100K filas)

Probablemente sería más rápido para buscar las min(BackupTime) solamente en los niños directos:

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

Pero para esto Para trabajar, debo asegurarme de que los descendientes se actualizarán antes que sus antepasados, así que debo recorrer la jerarquía recursivamente desde abajo hacia arriba. El problema es que no tengo una manera fácil de saber qué elementos son los más profundos en la jerarquía. Estoy usando SQLite, así que no puedo usar consultas jerárquicas.

¿Alguna idea sobre cómo hacer esto de manera eficiente?

Idealmente, yo preferiría ser capaz de hacerlo de una sola consulta de actualización, pero si no es posible, estoy abierto a otras opciones, siempre y cuando sean eficientes

Respuesta

1

Esto es un tiro en la oscuridad, pero podría funcionar. Es un intento de manejar el problema de abajo hacia arriba manualmente. (No conozco las limitaciones de sqlite, pero probablemente este sea el estándar SQL-92 y espero que esté bien.)

Paso 1: Decida cómo quiere manejar los directorios vacíos. Creo que la solución aquí solo funciona si no hay directorios vacíos o si los directorios vacíos se actualizan inicialmente para que tengan un BackupTime no NULL artificial. (Lo que ese BackupTime artificial debería ser puede ser importante, dependiendo de cómo mantenga la columna BackupDate cuando haya cambios en sus datos. Usar la fecha actual o una futura artificial podría funcionar, pero debería pensar en ello)

Paso 2. Ejecutar la siguiente consulta varias veces hasta que no hay más filas se ven afectados:

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 
) 

En otras palabras, actualizar el BackUpTime de directorios cuando se necesita y también tienen toda la información: cuando su BackUpTime es nula y se no contiene subdirectorios cuyo valor de BackupTime también sea nulo.

Por lo tanto, la primera vez que ejecute esto, establecerá BackupTime para todos los directorios que no contengan subdirectorios, solo archivos. La segunda vez, establecerá BackupTime para directorios que contengan subdirectorios, pero no subdirectorios.

Es posible que pueda manejar el problema de directorio vacío configurando BackupTime para fusionarse ((seleccionar ...), current_timestamp).

+0

¡Gracias, lo intentaré! –

+0

OK, tomó 5 segundos procesar una base de datos con 100000 elementos ... eso es bastante bueno;).Intenté con una base de datos "ficticia", así que necesito verificar con una verdadera para estar seguro, pero estoy seguro de que tendrá un rendimiento similar. Por cierto, la última condición con 'no existe' no es necesaria: si hay un valor nulo,' min' simplemente devolverá nulo, por lo que eventualmente dará el mismo resultado, con menos iteraciones (14 en lugar de 27 en mi prueba) –

+0

MIN devolverá NULL si los valores * only * son NULL. MIN NO devolverá NULL si se agregan NULL y otros valores. NO EXISTE es necesario para garantizar que las iteraciones van de abajo hacia arriba. Si eliminas NO EXISTE, obtienes resultados incorrectos. Supongamos/dir1/contiene dos elementos: 1) un archivo con BackupTime 4/12 y 2) un directorio/dir2/que contiene 1 archivo con el tiempo de copia de seguridad 4/9. Sin NOT EXISTS,/dir1/obtendrá un BackupTime incorrecto de 4/12 durante la primera iteración. Con NOT EXISTS, esperará hasta la próxima iteración. Las pocas iteraciones que vea sugieren estas respuestas incorrectas. –

Cuestiones relacionadas