用于批量更新分层表的有效方法

时间:2012-04-26 15:29:51

标签: sql performance optimization hierarchy hierarchical-data

我有一个数据库表,表示文件和目录的层次结构,具有以下结构(简化):

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

目前BackupTime列仅用于文件,目录设置为null。

现在我需要在列中填写目录:它必须是所有后代(文件和目录)的最小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

我的问题是我似乎找不到有效的方法。上面的查询在大量数据上花费太长时间(此表通常包含超过100K行)

仅在直接孩子上搜索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 个答案:

答案 0 :(得分:1)

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

步骤1:确定如何处理空目录。我认为这里的解决方案只有在没有空目录或者最初更新空目录时才有效,因此它们具有人为的非NULL BackupTime。 (人工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。第二次,它将为包含子目录但没有子子目录的目录设置BackupTime。

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