生成 ANSI SQL INSERT INTO 语句

21

我有一个包含10个表的Oracle数据库。其中一些表中存在CLOB文本数据。我需要使用Java编程从这些表中以编程方式导出数据。导出的数据应该采用ANSI INSERT INTO SQL格式,例如:

INSERT INTO table_name (column1, column2, column3, ...)
VALUES (value1, value2, value3, ...);

主要思路是我需要将这些数据导入三个不同的数据库:ORACLEMSSQLMySQL。据我所知,所有这些数据库都支持ANSI INSERT INTO。但我没有找到任何用于生成数据SQL脚本的Java API/框架。我也不知道如何处理CLOB数据,如何导出它。
以Java最佳的方式从数据库中导出数据是什么?

更新:(01.07.2018)
根据这个答案,我猜插入文本数据超过4000字节是不可能的。如何使用Java编程生成PL\SQL脚本?或者是否有其他支持ORACLE、MSSQL等的导出格式?


1
可能可以在Java中完成整个操作,例如打开到Oracle(源)和SQL Server(目标)的连接,并迭代来自Oracle的表,将每个记录写入SQL Server。 - Tim Biegeleisen
连接具有数据库元数据,您可以查询其中的表格。 - Joop Eggen
2
你是否一定需要一组 ANSI SQL 文件,还是可以写入分隔符文本文件并从中加载?如果您转储到文本,然后导入,将会更加容易和快速,而不是执行 SQL 加载。 - Serg M Ten
1
不,我绝对不需要 ANSI SQL 文件(但如果有的话会完美)。目前我决定将所有数据导入 XML 文件中,并使用 Hibernate 编写一个小型 Java 工具,将该文件中的所有数据插入数据库。然后,我可以更改驱动程序,并将其应用于不同的 SQL 供应商。 - Maksym
1
我认为使用DB-to-file-to-DB管道比使用SQL脚本要好得多,即使需要定制转储/加载,因为你几乎不可能得到任何易于适用的通用SQL脚本集。 - Serg M Ten
1
最佳实践是将Clob作为外部文件导出,并通过文件加载它。我建议您阅读http://www.dba-oracle.com/t_insert_clob_table_column.htm。 - logger
7个回答

6

您是否曾考虑过一个合适的ORM-Api?我首先想到的是Hibernate或更抽象的JPA / JPQL。该框架了解所有主要的SQL方言。您需要做的就是定义您的方言连接,然后从数据库中检索数据并将其映射到POJO中,然后将数据推送(插入)到不同的(其他方言)连接中。我认为这应该很好用,即使我从未尝试过。但是我知道JPA并不新颖,并且在软件已经投入生产时仍广泛使用以更改数据库。由于每行都被转换为POJO,并且没有批量插入可用,因此这种方法效率较低。


同意你的观点。现在我正在使用Hibernate实现这样的导出工具。 - Maksym

4
如果您需要从Oracle数据库导出表并将其插入到不同类型的数据库中,我建议采用不同的方法。
这是使用JPA(Java Persistence API)的完美案例,它允许您创建代表数据库结构的模型。这是管理不同类型数据库的当前Java解决方案。
通过您的模型,您将能够生成与所有流行数据库兼容的请求。
因此,我的建议是使用Spring Boot + Spring Data + Spring Batch:
1. 创建一个应用程序,根据您的模型将表的内容导出为CSV格式。 2. 创建另一个应用程序,根据相同的模型导入您的CSV文件。根据您的jdbc url,Spring Boot将自动触发适用于目标数据库的适当方言,并生成正确的查询(导出也是如此)。
这可以在合理的时间内完成,并具有良好的性能。

4

10
这不是一个完整的答案。 - Tim Biegeleisen

2

对于MSSQLORACLE,您可以使用MERGE工具语法(以及数据的USING子句)符合ANSI标准:

MERGE INTO tablename USING table_reference ON (condition)
   WHEN MATCHED THEN
   UPDATE SET column1 = value1 [, column2 = value2 ...]
   WHEN NOT MATCHED THEN
   INSERT (column1 [, column2 ...]) VALUES (value1 [, value2 ...]);

对于MySQL,有一种不同的语法(On Duplicate Key Update Statement)

-- Insert new or merge into existing row.
MERGE INTO system_user target
USING (SELECT   1 AS system_user_id
       ,       'SYSADMIN' AS system_user_name
       ,        1 AS system_user_group_id
       ,        1 AS system_user_type
       ,       'Samuel' AS first_name
       ,        'the' AS middle_name
       ,       'Lamanite' AS last_name
       ,        1 AS created_by
       ,        SYSDATE AS creation_date
       ,        1 AS last_updated_by
       ,        SYSDATE AS last_update_date
       FROM     dual) SOURCE
ON (target.system_user_id = SOURCE.system_user_id)
WHEN MATCHED THEN
  UPDATE SET first_name = 'Samuel'
  ,          middle_name = 'the'
  ,          last_name = 'Lamanite'
  ,          last_updated_by = 1
  ,          last_update_date = SYSDATE
WHEN NOT MATCHED THEN
  INSERT
  ( target.system_user_id
  , target.system_user_name
  , target.system_user_group_id
  , target.system_user_type
  , target.first_name
  , target.middle_name
  , target.last_name
  , target.created_by
  , target.creation_date
  , target.last_updated_by
  , target.last_update_date )  
  VALUES
  ( SOURCE.system_user_id
  , SOURCE.system_user_name
  , SOURCE.system_user_group_id
  , SOURCE.system_user_type
  , SOURCE.first_name
  , SOURCE.middle_name
  , SOURCE.last_name
  , SOURCE.created_by
  , SOURCE.creation_date
  , SOURCE.last_updated_by
  , SOURCE.last_update_date );

AND:

-- Insert new or merge into existing row.
INSERT INTO system_user
( system_user_name
, system_user_group_id
, system_user_type
, first_name
, middle_name
, last_name
, created_by
, creation_date
, last_updated_by
, last_update_date )
VALUES
('SYSADMIN'
, 1
, 1
,'Samuel'
,'the'
,'Lamanite'
, 1
, NOW()
, 1
, NOW())
ON DUPLICATE KEY 
UPDATE first_name = 'Samuel'
,      middle_name = 'the'
,      last_name = 'Lamanite'
,      last_updated_by = 1
,      last_update_date = UTC_DATE();

2

我非常喜欢“以编程方式”这个词。 :)

导出数据的最佳方法是遍历表,然后查询每个表并使用insert into语句输出纯文本。如果您有二进制数据,可能会有问题,因为不同的RDBS可能会以稍微不同的方式处理它。

在Java端读取blob/clob意味着读取流。它可以是二进制流或字符流。对于Oracle,在文档中,您可以执行以下操作:

        ResultSet rs = s.executeQuery(
            "SELECT text FROM documents WHERE id = 1477");
        while (rs.next()) {
            java.sql.Clob aclob = rs.getClob(1);
            java.io.InputStream ip = rs.getAsciiStream(1);
            int c = ip.read();
            while (c > 0) {
                System.out.print((char)c);
                c = ip.read();
            }
            System.out.print("\n");
        }

这个答案中,您可以将其缩短:

Clob clob = resultSet.getClob("CLOB_COLUMN")
String clob_content = clob.getSubString(1, (int) clob.length());

写输出可能需要处理:\t\n\r。这取决于您的需求和内容。 文档有完整的示例-读取,写入。他们使用预准备语句,因此在两端都需要流。如果您的不大-比如32k / 64k-可能会有其他限制。如果您有任何示例-例如带有2-3行的create table,那么任何人编写代码并提供有效的东西都会更容易...


2
我需要使用Java程序从这些表中导出数据。
拜托了,Java是一种用于操作数据的工具,而不是迁移数据的工具。如果涉及ETL,请使用目标DBMS的ETL环境或自己编写ETL代码。

2
我会尝试使用Scriptella。它是一个开源的ETL和脚本执行工具,用Java编写,在其中你可以在xml文件中定义源和目标连接以及必要的转换。连接可以是JDBC甚至是文本文件,并且有批处理支持功能。生成的xml文件可以通过Java、Ant或命令行进行程序化处理。
在他们的两分钟教程中,有将表复制到另一个数据库以及处理BLOBs的示例。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接