Oracle 需要很长时间来插入行

问题描述 投票:0回答:1

我已在 Docker 映像中安装了 Oracle 23c Free。新功能之一是您可以在单个语句中插入多个值,就像所有其他 DBMS 一样。

我通过添加大约 13,692 行对此进行了测试。在其他 DBMS 中,例如 PostgreSQL、MySQL 和 MSSQL(有解决方法),实际上只需要几秒钟。对于 Oracle,花了五分钟多的时间。

在以前的版本中,我会将这些行分成大约十组,然后从多个大量

UNION ALL
语句中插入。大约需要半分钟。

我知道这是免费版本,但它真的需要花费一百倍的时间吗?我应该做些什么来让事情进行得更顺利吗?

这是 SQL 的简要版本:

CREATE TABLE saleitems (
    id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    saleid INT REFERENCES sales(id) ON DELETE CASCADE NOT NULL,
    bookid INT REFERENCES books(id) NOT NULL,
    quantity INT,
    price NUMERIC(6,2)
);

INSERT INTO saleitems(id, saleid, bookid, quantity, price)
VALUES
    (15030, 6036, 1896, 2, 25.50), 
    (13753, 5531, 1026, 1, 10.50), 
    (8344, 3359, 1855, 1, 14.00),
    …
;
oracle sql-insert
1个回答
0
投票

Oracle 23c 需要很长时间才能在单个 SQL 语句中插入 超过 1,000 行硬编码行。 如果您一次将插入分成 1,000 行的块,则总体时间将显着缩短。

Oracle 可以轻松处理大型 SQL 语句,但解析器会遇到巨大 SQL 语句的问题。此问题适用于

INSERT ALL
UNION ALL
以及新的 23 表值构造函数。在旧版本的 Oracle 中,问题发生在数百个串联 SQL 语句或行左右,而在 23c 中,问题仅在大约 1000 行后开始。

解析时间呈指数增长,如下图所示。

enter image description here

通过跟踪代码,我们可以知道额外的时间花在了解析上,而不是执行或获取上。这个细节很重要,因为它解释了为什么测试这些问题如此困难。由于解释计划可能会被缓存,因此使用完全相同的值运行测试两次可能不会显示任何问题。但在实践中,我假设您的插入语句将始终有至少一个不同的字节,这将强制硬解析该语句并重新创建此问题。


用于测试的代码:

-- Create a table without any foreign key references, to simply the schema. CREATE TABLE saleitems ( id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, saleid INT NOT NULL, bookid INT NOT NULL, quantity INT, price NUMERIC(6,2) ); -- Run INSERT statements of varying number of rows upt to 10K, in increments of 50. -- Time and display how long it takes to execute each INSERT statement. declare v_before_time number; v_after_time number; v_sql clob := 'INSERT INTO saleitems(id, saleid, bookid, quantity, price) VALUES '; begin for i in 50 .. 10000 by 50 loop for j in 1 .. 50 loop if i = 50 and j = 1 then v_sql := v_sql || '('||to_char(i*50+j)||','||to_char(i*50+j)||','||to_char(i*50+j)||','||to_char(i*50+j)||',9.99)'; else v_sql := v_sql || ',('||to_char(i*50+j)||','||to_char(i*50+j)||','||to_char(i*50+j)||','||to_char(i*50+j)||',9.99)'; end if; end loop; if i = 1 then dbms_output.put_line(v_sql); end if; v_before_time := dbms_utility.get_time; execute immediate v_sql; v_after_time := dbms_utility.get_time; rollback; dbms_output.put_line(i || ',' || to_char(v_after_time - v_before_time)); end loop; end; /
    
© www.soinside.com 2019 - 2024. All rights reserved.