为什么在相同 type/width 的列之间复制数据时会出现 'ORA-12899: value too large for column' 错误?
Why do I get 'ORA-12899: value too large for column' error when copying data between columns of the same type/width?
我有一个 java 小程序,可以将数据从一个数据库复制到另一个数据库。两个数据库都有相同的列,副本是使用 sql 开发人员的“复制数据库”选项创建的,以创建所有对象。这两个数据库都是 oracle 12.2.0 企业版,但目标位于 docker 映像中,将用于开发。仅复制一个 table:
时出现奇怪的错误
ORA-12899: 列“MY_SCHEMA”的值太大。“MY_TABLE”。“MY_COLUMN”(实际:101,最大值:100)
两个数据库在此列上的宽度相同,所以不确定为什么会抱怨。两者都是 VARCHAR2(100)。也许是我的 java 代码有问题?本质上是这样做的:
List<Column> cols = getCols("MY_SCHEMA", "MY_TABLE",sourceConn.getMetaData());
String sourceTableQuery = ...; //select * from my_table
String destinationTableInsertQuery = ...;//insert into my_table(...) values(...)
PreparedStatement queryStmt = sourceConn.prepareStatement(sourceTableQuery);
ResultSet data = queryStmt.executeQuery();
PreparedStatement insertStmt = destionationConn.prepareStatement(destinationTableInsertQuery);
while (data.next()) {
for(int i = 0; i < cols.size(); i++) {
insertStmt.setObject(i+1, data.getObject(cols.get(i).getName()));
}
insertStmt.addBatch();
}
int [] results = insertStmt.executeBatch();
编辑:两列的确切数据类型都是 VARCHAR2(100 BYTE)。抱歉,如果造成任何混淆。
默认情况下,如果您将列声明为 varchar2(100)
,您将分配 100 字节的存储空间。根据数据和数据库字符集,1 个字符可能占用 1 个字节的存储空间、2 个字节、3 个字节或 4 个字节。因此 varchar2(100)
列可以存储 25 到 100 个字符,具体取决于数据和数据库字符集。
我敢打赌这两个数据库的数据库字符集(nls_characterset
in v$nls_parameters
)是不同的。我的猜测是目标数据库使用可变长度的 UTF-8 字符集,而源数据库使用固定宽度的字符集。在失败的行中,至少有一个字符在 UTF-8 字符集中需要 2 个字节的存储,而在源数据库的字符集中只需要 1 个字节的存储。 (请注意,UTF-8 字符集支持的字符范围比源数据库的字符集多。)
您可以在两个数据库中或至少在目标数据库中将列声明为 varchar2(100 CHAR)
(使用字符长度语义而不是字节长度语义)。这将为 100 个字符分配 space,而不管数据需要多少字节(请注意,varchar2
列的长度限制仍然以字节表示,因此这不适用于 varchar2(4000)
列,除非您启用了扩展字符串大小)。
如果您将所有 DDL 放在一个地方并且该 DDL 未指定字符或字节语义,您可以执行
alter session set nls_length_semantics = CHAR
在 运行 DDL 之前,如果您不想通过和编辑 DDL,则可以在源数据库中创建您的对象。
更新数据库字符集的步骤。请不要在没有阅读的情况下执行此操作,因为它可能会损坏您的数据库。我不得不使用 INTERNAL_USE 关键字,因为 WE8MSWIN1252 不是 AL32UTF8 的超集。我不在乎,因为我可以在我的本地数据库中重新加载任何损坏的数据。
立即关机
启动限制
改变数据库字符集INTERNAL_USE WE8MSWIN1252;
立即关机
启动
我有一个 java 小程序,可以将数据从一个数据库复制到另一个数据库。两个数据库都有相同的列,副本是使用 sql 开发人员的“复制数据库”选项创建的,以创建所有对象。这两个数据库都是 oracle 12.2.0 企业版,但目标位于 docker 映像中,将用于开发。仅复制一个 table:
时出现奇怪的错误ORA-12899: 列“MY_SCHEMA”的值太大。“MY_TABLE”。“MY_COLUMN”(实际:101,最大值:100)
两个数据库在此列上的宽度相同,所以不确定为什么会抱怨。两者都是 VARCHAR2(100)。也许是我的 java 代码有问题?本质上是这样做的:
List<Column> cols = getCols("MY_SCHEMA", "MY_TABLE",sourceConn.getMetaData());
String sourceTableQuery = ...; //select * from my_table
String destinationTableInsertQuery = ...;//insert into my_table(...) values(...)
PreparedStatement queryStmt = sourceConn.prepareStatement(sourceTableQuery);
ResultSet data = queryStmt.executeQuery();
PreparedStatement insertStmt = destionationConn.prepareStatement(destinationTableInsertQuery);
while (data.next()) {
for(int i = 0; i < cols.size(); i++) {
insertStmt.setObject(i+1, data.getObject(cols.get(i).getName()));
}
insertStmt.addBatch();
}
int [] results = insertStmt.executeBatch();
编辑:两列的确切数据类型都是 VARCHAR2(100 BYTE)。抱歉,如果造成任何混淆。
默认情况下,如果您将列声明为 varchar2(100)
,您将分配 100 字节的存储空间。根据数据和数据库字符集,1 个字符可能占用 1 个字节的存储空间、2 个字节、3 个字节或 4 个字节。因此 varchar2(100)
列可以存储 25 到 100 个字符,具体取决于数据和数据库字符集。
我敢打赌这两个数据库的数据库字符集(nls_characterset
in v$nls_parameters
)是不同的。我的猜测是目标数据库使用可变长度的 UTF-8 字符集,而源数据库使用固定宽度的字符集。在失败的行中,至少有一个字符在 UTF-8 字符集中需要 2 个字节的存储,而在源数据库的字符集中只需要 1 个字节的存储。 (请注意,UTF-8 字符集支持的字符范围比源数据库的字符集多。)
您可以在两个数据库中或至少在目标数据库中将列声明为 varchar2(100 CHAR)
(使用字符长度语义而不是字节长度语义)。这将为 100 个字符分配 space,而不管数据需要多少字节(请注意,varchar2
列的长度限制仍然以字节表示,因此这不适用于 varchar2(4000)
列,除非您启用了扩展字符串大小)。
如果您将所有 DDL 放在一个地方并且该 DDL 未指定字符或字节语义,您可以执行
alter session set nls_length_semantics = CHAR
在 运行 DDL 之前,如果您不想通过和编辑 DDL,则可以在源数据库中创建您的对象。
更新数据库字符集的步骤。请不要在没有阅读的情况下执行此操作,因为它可能会损坏您的数据库。我不得不使用 INTERNAL_USE 关键字,因为 WE8MSWIN1252 不是 AL32UTF8 的超集。我不在乎,因为我可以在我的本地数据库中重新加载任何损坏的数据。
立即关机
启动限制
改变数据库字符集INTERNAL_USE WE8MSWIN1252;
立即关机
启动