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】:

您的integerQueryvarcharQuery 都向数据库表中插入一条记录,其中一列已填充,其他列为空白。因为您只为一列提供值。

【讨论】:

我看到了,但我不明白如何放置所有值,有什么建议吗? 使用 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。 --- 无论如何,由于我们不知道urlDB1urlDB2 的内容,我们 不应该假设它们是相同的数据库风格,即使用相同的 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 &lt;= 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值不会插入到sql表中。

SQL语法错误:无法将 NULL值插入列'',该列不允许空值。INSERT失败。怎么解决啊

SQLAlchemy 将值插入到反射表中会导致所有的 NULL 条目

sqlserver decimal 默认值是0.0 插入null 的时候会不会报错

怎么样把NULL值插入到SQL数据库的INT字段(int可为空)