2012-04-26 22 views
5

我有一个代表的文件和目录层次结构的数据库表的大规模更新,具有以下结构(简化)的有效算法,它被设置为空目录。的为层次表

现在我需要为目录填充此列:它必须是所有后代(文件和目录)的最小BackupTime

这(幼稚和低效)查询说明了什么,我想做的事:

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 

我的问题是,我似乎无法找到一个有效的方法。上面的查询时间太长对大量数据(此表通常包含超过10万行)

它可能会更快搜索仅在min(BackupTime)直接孩子:

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

但对于这为了工作,我必须确保后代会在他们的祖先之前更新,所以我必须从下往上递归地进行分级。问题是我没有简单的方法来知道哪些项目是最深层次的。我正在使用SQLite,所以我不能使用分层查询。

有关如何有效地做到这一点的任何想法?

理想情况下,我宁愿能做到在一个UPDATE查询,但如果这是不可能的,我开放给其他的选项,只要它们是有效的

回答

1

这是一个镜头在黑暗中,但它可能工作。这是一个尝试手动处理自下而上的问题。 (我不知道sqlite的限制,但这可能是标准的SQL-92,希望可以。)

步骤1:决定如何处理空目录。我认为这里的解决方案只适用于没有空目录或空目录最初更新的情况,因此它们具有人为的非NULL备份时间。 (BackupTime应该是什么样的东西可能很重要,这取决于在数据发生变化时如何维护BackupDate列。使用当前日期或假的未来日期可能会有效,但您应该考虑一下。)

第2步:重复执行下面的查询,直到没有更多的行会受到影响:

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 
) 

换句话说,更新BACKUPTIME的目录时,你需要,也有所有的信息:当他们的BACKUPTIME为空,他们不包含BackupTime值也为空的子目录。

因此,您第一次运行此操作时,它将为所有不包含子目录的目录(仅包含文件)设置备份时间。第二次,它将为包含子目录但没有子子目录的目录设置备份时间。

您可以通过将BackupTime设置为合并((select ...),current_timestamp)来处理空目录问题。

+0

谢谢,我会试一试! – 2012-04-26 22:47:32

+0

好吧,花了5秒钟处理一个有100000个项目的数据库......这非常好;)。我尝试了一个“虚拟”数据库,所以我需要确定一个真实的数据库,但我相信它会有类似的性能。顺便说一下,'not exists'的最后一个条件是没有必要的:如果有null,'min'将返回null,所以它最终会得到相同的结果,迭代次数更少(14次而不是27次) – 2012-04-26 23:38:58

+0

如果* only *值为NULL,MIN将返回NULL。如果NULL和其他值汇总,MIN不会返回NULL。 NOT EXISTS是需要保证迭代从下到上的。如果你删除NOT EXISTS,你会得到错误的结果!假设/ dir1 /包含两个项目 - 1)具有BackupTime 4/12的文件和2)包含具有备份时间4/9的1个文件的目录/ dir2 /。如果没有NOT EXISTS,在第一次迭代期间/ dir1 /将得到不正确的4/12的备份时间。不存在,它会等到下一次迭代。您看到的迭代次数越少,这些错误答案就越多。 – 2012-04-27 00:49:17