带有大型数据集的 SQL 插入
Posted
技术标签:
【中文标题】带有大型数据集的 SQL 插入【英文标题】:SQL Insert with large dataset 【发布时间】:2008-12-31 02:21:17 【问题描述】:当运行像“插入表”这样的查询时,我们如何处理提交大小? IE。是否将 anotherTable 中的所有记录都插入到单个事务中,或者有没有办法设置提交大小?
非常感谢~Sri
PS:我是第一次来这里,这个网站看起来很不错!
【问题讨论】:
对于新手,请尽量避免在问题中添加不必要的元素。使用相同的评论部分(节俭地)。 :) 一点一点组成一个字节。 【参考方案1】:在好的数据库中,这是一个原子语句,所以不,没有办法限制插入的记录数 - 这是一件好事!
【讨论】:
【参考方案2】:在原始发布者想要避免回滚空间问题的上下文中,答案非常简单。回滚段的大小应该适应事务的大小,而不是相反。当你的事务完成时你提交。
【讨论】:
【参考方案3】:我用各种语言(主要是 Java)编写了代码来执行您所描述的批量插入。每次我这样做时,主要是通过解析一些输入文件或类似的东西,我基本上只是准备一个数据子集以从总量中插入(通常是 4000 个左右的批次)并将该数据提供给我们的 DAO 层.所以它是以编程方式完成的。我们从未注意到这样做会对性能造成任何实际影响,并且我们正在处理几百万条记录。如果您要插入大型数据集,则无论您如何操作,该操作都将“花费一些时间”。
【讨论】:
【参考方案4】:除非您明确编码,否则您无法处理提交大小。例如,您可以使用 where 循环,并编写一种方法来限制您选择的数据量。
【讨论】:
但是对于大型插入,这通常不是最佳的。 不知道为什么这个答案是正确的。 我和尼克都不明白。【参考方案5】:David Aldridge 是对的,当您希望 INSERT 整体成功或失败时,根据最大事务调整回滚段的大小。
一些替代方案:
如果您不关心能否将其回滚(这就是该段的用途),您可以 ALTER TABLE 并添加 NOLOGGING 子句。但这不是明智之举,除非您正在加载一个报表,在该报表中删除所有旧行并加载新行,或者其他一些特殊情况。
如果您可以接受某些行被插入而其他行由于某种原因而失败,那么添加对处理失败的支持,使用 INSERT INTO LOG ERRORS INTO 语法.
【讨论】:
【参考方案6】:如果您需要限制数据集,请将该限制构建到查询中。
例如,用 Microsoft SQL Server 的说法,您可以使用“TOP
N
”来确保查询只返回有限数量的行。
INSERT INTO thisTable
SELECT TOP 100 * FROM anotherTable;
【讨论】:
【参考方案7】:我之所以要这样做是为了避免回滚段空间不足。另外,我希望看到结果定期填充到目标表中。
我不想使用 where 循环,因为它可能会增加性能开销。不是吗?
~斯里
【讨论】:
您应该用“oracle”标记您的问题,因为 IIRC 回滚段是 Oracle 功能。 我相信这个网站上的预期方法是您应该使用任何进一步的信息编辑您的原始问题,因为它可能会随着其他答案的投票而丢失。 你能把这个作为对原始问题的修改添加吗? @sri 这不是答案,这应该是对您原始问题的评论或编辑【参考方案8】:您是对的,您可能希望批量运行大型插入。附加的链接显示了在 SQL Server 中执行此操作的方法,如果您使用不同的后端,您将执行类似的操作,但确切的语法可能不同。这是可以接受循环的情况。
http://www.tek-tips.com/faqs.cfm?fid=3141
【讨论】:
【参考方案9】:“我这样做的原因是为了避免回滚段空间不足。另外,我希望看到结果定期填充到目标表中。”
首先是正确调整撤消表空间大小的问题。由于撤消是对现有行的删除,因此不需要大量空间。相反,删除通常需要更多空间,因为它必须拥有整个已删除行的副本才能重新插入以撤消它。
第二个,看看 v$sql 中的 v$session_longops 和/或 rows_processed
【讨论】:
【参考方案10】:INSERT INTO TableInserted
SELECT *
FROM (
SELECT *,
ROW_NUMBER() OVER (ORDER BY ID) AS RowNumber
FROM TableSelected
) X
WHERE RowNumber BETWEEN 101 AND 200
您可以很容易地将上面的内容包装到 while 循环中,将 101 和 200 替换为变量。这比一次做 1 条记录要好。
不知道什么版本的Oracle支持窗口函数。
【讨论】:
-1 因为这是一种非常缓慢的方法。对于 100,000 条记录的表,您访问“TableSelected”1000 次,每次访问都可能是全表扫描... 您假设它始终是表扫描。好的,将数据放入临时表中。你的更好的方法在哪里?在这里看不到。【参考方案11】:这是一个扩展注释,说明将索引设置为 NOLOGGING 将不会帮助减少 INSERT 的 UNDO 或 REDO。
该手册暗示 NOLOGGING 索引可以通过减少 UNDO 和 REDO 来帮助改进 DML。由于 NOLOGGING 有助于表 DML,因此它也有助于 INDEX 更改似乎是合乎逻辑的。但是这个测试用例表明,将索引更改为 NOLOGGING 对 INSERT 语句没有影响。
drop table table_no_index;
drop table table_w_log_index;
drop table table_w_nolog_index;
--#0: Before
select name, value from v$mystat natural join v$statname where display_name in ('undo change vector size', 'redo size') order by 1;
--#1: NOLOGGING table with no index. This is the best case scenario.
create table table_no_index(a number) nologging;
insert /*+ append */ into table_no_index select level from dual connect by level <= 100000;
commit;
select name, value from v$mystat natural join v$statname where display_name in ('undo change vector size', 'redo size') order by 1;
--#2: NOLOGGING table with LOGGING index. This should generate REDO and UNDO.
create table table_w_log_index(a number) nologging;
create index table_w_log_index_idx on table_w_log_index(a);
insert /*+ append */ into table_w_log_index select level from dual connect by level <= 100000;
commit;
select name, value from v$mystat natural join v$statname where display_name in ('undo change vector size', 'redo size') order by 1;
--#3: NOLOGGING table with NOLOGGING index. Does this generate as much REDO and UNDO as previous step?
create table table_w_nolog_index(a number) nologging;
create index table_w_nolog_index_idx on table_w_nolog_index(a) nologging;
insert /*+ append */ into table_w_nolog_index select level from dual connect by level <= 100000;
commit;
select name, value from v$mystat natural join v$statname where display_name in ('undo change vector size', 'redo size') order by 1;
以下是统计查询的结果。这些数字是会话累积的。测试用例 #2 和 #3 的 UNDO 和 REDO 增加相同。
--#0: BEFORE: Very little redo or undo since session just started.
redo size 35,436
undo change vector size 10,120
--#1: NOLOGGING table, no index: Very little redo or undo.
redo size 88,460
undo change vector size 21,772
--#2: NOLOGGING table, LOGGING index: Large amount of redo and undo.
redo size 6,895,100
undo change vector size 3,180,920
--#3: NOLOGGING table, NOLOGGING index: Large amount of redo and undo.
redo size 13,736,036
undo change vector size 6,354,032
【讨论】:
【参考方案12】:您可能只想将索引设为 NOLOGGING。这样表数据是可恢复的,但如果表恢复,则需要重建索引。索引维护可以创建很多撤消操作。
【讨论】:
不幸的是,索引 NOLOGGING 仅适用于 creating an index。 @jonearles,从您链接的文档中,“此设置还确定是否记录了针对索引的后续 ... 直接路径 INSERT 操作。”您可以使用附加提示来获取直接路径插入。 好点,我应该更仔细地阅读我的链接!但我仍然认为说 NOLOGGING 仅在创建或重建索引时适用是准确的。在this AskTom thread 中,汤姆多次说了同样的话。其他一些在线资源和我的简单测试也同意 NOLOGGING 在直接路径插入期间不会减少索引重做。我为文档提交了评论,但我不确定这是否会带来任何好处。 我说的是减少撤消。来自同一个 AskTom 线程:“撤消将对索引进行最小化,但必须生成 ...”asktom.oracle.com/pls/asktom/… 从该引用中不清楚的是 NOLOGGING 对索引的影响。 不幸的是,它似乎也对 UNDO 没有帮助。请参阅下面的我的回答/评论。我不确定为什么它没有帮助,如果它有效,它将是一个非常有用的功能。以上是关于带有大型数据集的 SQL 插入的主要内容,如果未能解决你的问题,请参考以下文章
在 phpMyAdmin SQL 表中存储大型数据集的有效方法
从 sql server 迁移到大型数据集的 sqlite 的最快方法