我已经与你斗争了几个星期了 - 你可以看到here和here。虽然我得到的答案是正确的,但由于性能问题,它们不适合我。更多信息如下。
我正在使用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
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)可以处理的多。因此,对于所有(我的)实际目的,这不起作用。
在我看来,我可以加入。
的内容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上插入或更新任何内容。我是否需要补充此查询以完成我在帖子开头所解释的内容?
任何想法,任何帮助,非常感谢。我已经在这两个星期里苦苦挣扎了,我需要完成这件事。
答案 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
(或insert
或delete
)。
如果这是您的查询:
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