SQLite查询性能:UPDATE vs JOIN

时间:2015-03-25 00:12:08

标签: sql sqlite

我已经与你斗争了几个星期了 - 你可以看到herehere。虽然我得到的答案是正确的,但由于性能问题,它们不适合我。更多信息如下。

我正在使用SQLite。我有一个表格,行表示某个时间点的个人。我正在尝试使用另一列(statusLag)的滞后值创建一个列(status),如下所示:

workerID    timeVar     status      statusLag
---------------------------------------------
1           1           0           NULL
1           2           1           0
1           3           1           1
1           4           1           1
----------------------------------------------
2           3           1           NULL
2           4           0           1
2           5           1           0
2           6           0           1

我尝试了什么

方法1:更新

UPDATE myTable
    SET statusLag = (SELECT t2.status
                     FROM myTable t2
                     WHERE t2.workerID = myTable.workerID AND
                           t2.timeVar < myTable.timeVar
                     ORDER BY t2.timeVar desc
                     LIMIT 1
                    );

这是我运行的测试中的工作。但是,我有一个非常大的表--32亿行。我现在可以用一个3亿行的表来工作,这个查询使用的RAM太多,比我的机器(12GB)可以处理的多。因此,对于所有(我的)实际目的,这不起作用。

方法2:加入

在我看来,我可以加入。

的内容
SELECT t1.*, t2.status as statusLag FROM myTable AS t1
        LEFT JOIN myTable  AS t2
        ON t1.workerID=t2.workerID AND t1.timeVar=t2.timeVar+1
        ORDER BY t1.workerID, t1.timeVar ;

我不清楚这是否会起作用。此外,这是我第一次加入时,我的印象是这个查询单独不会在myTable上插入或更新任何内容。我是否需要补充此查询以完成我在帖子开头所解释的内容?

任何想法,任何帮助,非常感谢。我已经在这两个星期里苦苦挣扎了,我需要完成这件事。

3 个答案:

答案 0 :(得分:1)

您可以尝试的一种策略是将其分解为许多较小的更新。

也就是说,您不要尝试一次更新32亿行。找到一种方法可以将它分成30组,每组1亿组,并一次完成一组。

这有一些缺点:

  • 有一段时间你会更新一些行,而有些行没有更新。
  • 你必须找到一种方法将它们分解成可用的块。

但最大的好处是它可能会起作用(最终)。

类似的东西:

UPDATE myTable
    SET statusLag = (SELECT t2.status
                     FROM myTable t2
                     WHERE t2.workerID = myTable.workerID AND
                           t2.timeVar < myTable.timeVar
                     ORDER BY t2.timeVar desc
                     LIMIT 1
                    )
     WHERE companyID = 1;

为数据库中的每个companyID运行它。

或者

     WHERE companyID =>  0 AND companyID < 1000;

重要的是让数据库执行更新并完成事务,然后再移动到下一组要更新的记录。如果您尝试将其全部包装在一个事务中,那么您就会遇到同样的问题,即必须在动态更新中管理32亿行的更新。

您可能希望自动执行更新(例如,编写一些java或某些东西来循环遍历companyID),或者您可以使用体面的文本编辑器创建30或40个SQL语句,然后手动运行它们。

如果您需要维护此数据,那么我建议您在创建行时包含滞后数据 - 一次一个比这样的大批量容易得多。

答案 1 :(得分:0)

如果您想更改表格中的值,则需要使用update(或insertdelete)。

如果这是您的查询:

UPDATE myTable
    SET statusLag = (SELECT t2.status
                     FROM myTable t2
                     WHERE t2.workerID = myTable.workerID AND
                           t2.timeVar < myTable.timeVar
                     ORDER BY t2.timeVar desc
                     LIMIT 1
                    );

然后你可能有myTable(workerId, timeVar, status)的索引。但是,说实话,有32亿行和16 GB的RAM,这仍然可能无济于事。

几乎任何其他数据库都有可以帮助(MySQL lag()变量的构造)。是否有可能使用其他数据库?

答案 2 :(得分:0)

更新是一个缓慢的操作,因为数据库引擎必须获取行,运行子查询以获取新值,然后将行存储到位。有时它不适合它所在的地方,它必须到存储器的其他地方。

为了获得更好的表现,我会这样做:

  • 使用列myLagTable
  • 创建一个表statusLag
  • 运行INSERT(见下文)
  • myTable重命名为myOldTable
  • myLagTable重命名为myTable

执行此操作时,myTable在重命名操作期间不可用。

要填充myLagTable,您可以使用JOIN

INSERT INTO myLagTable(workerID, timeVar, status, statusLag)
SELECT t1.*, t2.status as statusLag 
FROM myTable AS t1
LEFT JOIN myTable AS t2
ON t1.workerID=t2.workerID AND t1.timeVar=t2.timeVar+1;

请注意,我删除了ORDER BY,因为INSERT中没有必要,它会使用资源(CPU,内存,磁盘)对行进行排序。

对结果感到满意后,您可以放弃myOldTable