sql表插入行问题,部分列插入NULL值
Posted
技术标签:
【中文标题】sql表插入行问题,部分列插入NULL值【英文标题】:Inserting rows in sql table problem, some columns inserted NULL value 【发布时间】:2020-09-04 13:33:58 【问题描述】:我有 2 个带有多个表的数据库。所有表都具有相同的语法。在这里,我有一个将表名作为参数的方法。我尝试插入的表有 3 列(int、varchar、int)。问题是,只插入了第一行,而第2行和第3行都是NULL,不知道是什么问题。有什么建议吗?
public void getAndInsertData(String nameOfTable)
try
Class.forName("com.mysql.jdbc.Driver");
Connection con1 = DriverManager.getConnection(urlDB1, user1, password1);
Statement s1 = con1.createStatement();
Connection con2 = DriverManager.getConnection(urlDB2, user2, password2);
Statement s2 = con2.createStatement();
ResultSet rs1 = s1.executeQuery("SELECT * FROM " + nameOfTable);
ResultSetMetaData rsmd1 = rs1.getMetaData();
int columnCount = rsmd1.getColumnCount();
for (int column = 1; column <= columnCount; column++)
String columnName = rsmd1.getColumnName(column);
int columnType = rsmd1.getColumnType(column);
while (rs1.next())
switch (columnType)
case Types.INTEGER:
case Types.SMALLINT:
case Types.BIGINT:
case Types.TINYINT:
int integerValue = rs1.getInt(column);
String integerQuery = "insert into " + nameOfTable + " (" + columnName + ") VALUES("
+ integerValue + ");";
s2.executeUpdate(integerQuery);
break;
case Types.VARCHAR:
case Types.NVARCHAR:
case Types.LONGNVARCHAR:
case Types.LONGVARCHAR:
String varcharValue = rs1.getString(column);
String varcharQuery = "insert into " + nameOfTable + " (" + columnName + ") VALUES("
+ varcharValue + ");";
s2.executeUpdate(varcharQuery);
default:
System.out.println("Default");
break;
catch (Exception e)
e.printStackTrace();
【问题讨论】:
如果您没有明确地将值放入列中,则使用默认值。如果未指定默认值,则值为NULL
。
你的循环是由内而外的。 while
应包含 for
。
我将 for 循环放在了 while 的下方,但还是没有用,您对代码有什么建议吗?我该怎么做?
【参考方案1】:
您的integerQuery
和varcharQuery
都向数据库表中插入一条记录,其中一列已填充,其他列为空白。因为您只为一列提供值。
【讨论】:
我看到了,但我不明白如何放置所有值,有什么建议吗? 使用 StringBuilder 形成列名列表和值列表。 我做了一个列名列表,但我不知道如何在那里制作一个值列表,因为我需要获取该过滤器中的所有列...【参考方案2】:几个问题:
使用 try-with-resources 确保正确清理 JDBC 资源。
不需要switch
语句,因为我们实际上不需要知道列的类型。如果您使用 getObject()
和 setObject()
,JDBC 驱动程序将处理该问题。
源表中的每一行只执行一个INSERT
。
在插入大量记录时使用批处理,以获得更好的性能。
这是怎么做的:
try (
Connection conSource = DriverManager.getConnection(urlDB1, user1, password1);
Connection conTarget = DriverManager.getConnection(urlDB2, user2, password2);
Statement stmtSource = conSource.createStatement();
ResultSet rsSource = stmtSource.executeQuery("SELECT * FROM " + nameOfTable);
)
// Build insert statement
ResultSetMetaData metaData = rsSource.getMetaData();
int columnCount = metaData.getColumnCount();
StringBuilder sql = new StringBuilder("INSERT INTO " + nameOfTable + " (");
for (int column = 1; column <= columnCount; column++)
if (column != 1)
sql.append(", ");
sql.append(metaData.getColumnName(column));
sql.append(") VALUES (");
for (int column = 1; column <= columnCount; column++)
if (column != 1)
sql.append(", ");
sql.append("?");
sql.append(")");
// Copy data
conTarget.setAutoCommit(false);
try (PreparedStatement stmtTarget = conTarget.prepareStatement(sql.toString()))
int batchSize = 0;
while (rsSource.next())
for (int column = 1; column <= columnCount; column++)
// Copy row here. Use switch statement to control the mapping
// if source and target table columns don't have compatible types.
// The following statement should work for most types, so switch
// statement only needs to cover the exceptions.
stmtTarget.setObject(column, rsSource.getObject(column), metaData.getColumnType(column));
stmtTarget.addBatch();
if (++batchSize == 1000) // Flush every 1000 rows to prevent memory overflow
stmtTarget.executeBatch();
batchSize = 0;
if (batchSize != 0)
stmtTarget.executeBatch();
conTarget.commit();
【讨论】:
这将用于将表内容从 Hana DB 传输到一个 MySQL DB,两者都使用 JDBC,问题是,我知道某些数据类型存在一些差异,这就是我尝试使用 switch 的原因跨度> @RobertVasile Hana DB 需要与 MySQL 不同的 JDBC 驱动程序,不是吗?您应该edit您的问题并添加该信息,即目标数据库是 Hana DB,源数据库是 MySQL。 @Abra 这是另一种方式,from Hana to MySQL。 --- 无论如何,由于我们不知道urlDB1
和urlDB2
的内容,我们 不应该假设它们是相同的数据库风格,即使用相同的 JDBC 驱动程序。
我还不知道,我的导师告诉我们,我们有 2 个 DB,一个是 Hana DB,一个是 MySql,都使用 JDBC,他需要一些东西来将表内容从 Hana 传输到 MySql .他建议切换,因为他说可以是一些不同的数据,例如可以将一些数据存储到String字段中,例如,由于精度,长度等原因,在传输时不会丢失数据。【参考方案3】:
正如 The Impaler 已经提到的,您的循环位于错误的位置。 对于 rs1 的每条记录,您想使用 s2 插入一条记录。
您可以先使用元数据构建准备好的语句,然后注入值: ResultSetMetaData rsmd1 = rs1.getMetaData(); int columnCount = rsmd1.getColumnCount();
StringBuffer sql=new StringBuffer("insert into "+nameOfTable+" (");
for (int column = 1; column <= columnCount; column++)
String columnName = rsmd1.getColumnName(column);
if(column>1)
sql.append(",");
sql.append(columnName);
sql.append(") values (");
for(int i=1;i<=columnCount;i++)
sql.append((i==1?"":",")+ "?");
sql.append(")");
System.out.println("Prepared SQL:"+sql.toString());
// sql = insert into nameOfTable (col1,col2,col3) values (?,?,?)
PreparedStatement s2= con2.prepareStatement(sql.toString());
while (rs1.next())
s2.clearParameters();
for (int column = 1; column <= columnCount; column++)
int columnType = rsmd1.getColumnType(column);
switch (columnType)
case Types.INTEGER:
case Types.SMALLINT:
case Types.BIGINT:
case Types.TINYINT:
s2.setInt(column, rs1.getInt(column));
break;
case Types.VARCHAR:
case Types.NVARCHAR:
case Types.LONGNVARCHAR:
case Types.LONGVARCHAR:
s2.setString(column, rs1.getString(column));
break;
default:
System.err.println("Not supported type for column "+column+" with type:"+columnType);
s2.setNull(column, columnType);
break;
// end of for loop
// execute statement once per record in rs1
s2.executeUpdate();
// end of while
【讨论】:
我试了一下,他给了我这个异常:java.sql.SQLException: Parameter index out of range (3 > number of parameters, which is 2)。 你是对的,第一个for循环的结束条件一定是column <= columnCount
。 =
不见了。
现在我有另一个异常说: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 'numar' in 'field list' "numar" is the last column name in the table
@RobertVasile 似乎您的目标表与源表的列不同。检查表以确保它们具有相同的列、相同的名称和类型。
我添加了一个 System.out.println 来打印准备好的语句(未经测试,希望它打印一些人类可读的 SQL)。如果您希望此代码更具弹性,则应比较两个表的元数据以验证它们具有相同的列定义,并使用差异的详细信息引发异常。以上是关于sql表插入行问题,部分列插入NULL值的主要内容,如果未能解决你的问题,请参考以下文章
SQL语法错误:无法将 NULL值插入列'',该列不允许空值。INSERT失败。怎么解决啊
SQLAlchemy 将值插入到反射表中会导致所有的 NULL 条目