【问题标题】:Error converting data type varchar to numeric VB.Net(Windows Service)?将数据类型 varchar 转换为数字 VB.Net(Windows 服务)时出错?
【发布时间】:2021-06-09 20:49:03
【问题描述】:

我有一个 Windows 服务 (VB.NET) 可以将数据从 SQL 表复制到另一个 SQL 表(在不同的数据库和服务器中)。当我启动服务时,它只会给我这个错误:

将数据类型 varchar 转换为数值时出错。

PS:我对这个错误感到惊讶,因为我在源表中没有看到任何 varchar 数据类型。

Source Table(NOR_LABOR) columns and data types 样本源表:http://www.sqlfiddle.com/#!18/bd4fb/1

Destination Table(ALL_LABOR_DETAILS) columns and data types 示例目标表:http://www.sqlfiddle.com/#!18/7eb72/1

Imports System.Configuration
Imports System.Data.SqlClient

Public Class DataCollector

    Dim con1, con2 As New SqlConnection
    Dim timer1 As Timers.Timer
    Dim p_oConn As New Wisys.AllSystem.ConnectionInfo
    Protected Overrides Sub OnStart(ByVal args() As String)

        con1 = New SqlConnection("Data Source=NORMAC-CTMS\SQLEXPRESS;Database=Normac Data;Integrated Security=true")

        Try
            con1.Open()
        Catch ex As Exception
            FileIO.WriteLog(ex.Message)
        End Try

        con2 = New SqlConnection("Data Source=STLEDGSQL01;Database=MES_DEV;Integrated Security=true")

        Try
            con2.Open()
        Catch ex As Exception
            FileIO.WriteLog(ex.Message)
        End Try

        timer1 = New Timers.Timer()
        timer1.Interval = 5000
        AddHandler timer1.Elapsed, AddressOf OnTimedEvent
        timer1.Enabled = True
        FileIO.WriteLog("Service has started")

    End Sub

    Protected Overrides Sub OnStop()
        timer1.Enabled = False
        FileIO.WriteLog("Service has stopped")
        con1.Close()
        con2.Close()
    End Sub

    Private Sub OnTimedEvent(obj As Object, e As EventArgs)

        Dim cmd1, cmd2, cmd3 As SqlCommand

        'Connecting the Normac Data table
        Dim da1 As SqlDataAdapter = New SqlDataAdapter("select ID, RTRIM(trx_date), RTRIM(work_order), RTRIM(department), RTRIM(work_center), RTRIM(operation_no), RTRIM(operator), RTRIM(total_labor_hours), RTRIM(feet_produced), RTRIM(item_no), RTRIM(posted), RTRIM(lot_no), RTRIM(default_bin) from NOR_LABOR where ID > 46006 order by ID", con1)
        Dim cb1 As SqlCommandBuilder = New SqlCommandBuilder(da1)
        Dim dt1 As DataTable = New DataTable()
        da1.Fill(dt1)

        Dim i As Integer

        'Inserting Normac Data  into ALL_LABOR_DETAILS table
        For Each dr As DataRow In dt1.Rows
            Try
                cmd1 = New SqlCommand("Insert into ALL_LABOR_DETAILS values('" & dr(0) & "','" & dr(1) & "','" & dr(2) & "','" & dr(3) & "','" & dr(4) & "','" & dr(5) & "','" & dr(6) & "','" & dr(7) & "','" & dr(8) & "','" & dr(9) & "','" & dr(10) & "','','','','','" & dr(11) & "','" & dr(12) & "','','','','','','','')", con2)
                i = cmd1.ExecuteNonQuery()
                FileIO.WriteLog("Most Recent Normac ID " & mostRecentNormacID)
            Catch ex As Exception
                FileIO.WriteLog(ex.Message)
            End Try
        Next
        da1.Update(dt1)
        cmd1.Dispose()
        dt1.Dispose()
        da1.Dispose()

    End Sub
End Class

【问题讨论】:

  • 请从这篇文章中删除 VB6 标签,因为它显然不适用于此代码。
  • 一堆问题。第一个问题。确切地知道异常发生的位置将非常有帮助。我建议至少将其作为控制台应用程序进行开发,这比将调试器附加到服务要容易得多。
  • 很抱歉先生没有注意到这一点,这只是一个包含 VB6 的大项目,而这只是其中的一部分。刚刚删除了 VB6 标签。
  • 下一个问题,为什么所有的字符串连接。一般如果有sql语法问题,查看生成的sql即可轻松解决。更好的使用参数
  • 下一个问题。为什么要在初始查询中修剪所有内容。如果你想要可变长度的字符串。使用 varchar 或 nvarchar

标签: sql-server vb.net


【解决方案1】:

我对这个错误感到惊讶

你不应该;您连接到 INSERT 语句中的每一个 SQL 注入黑客倾向值都是一个 varchar,因为它们被 '' 包围。

不要只用'' 包围您编写的任何 SQL 中的每个值

--no
SELECT * FROM Person WHERE Age = '32'

--yes
SELECT * FROM Person WHERE Age = 32

至于您的实际问题,您应该正确参数化您的插入 SQL 并准确设置参数类型。设置一次命令:

cmd1 = New SqlCommand("Insert into ALL_LABOR_DETAILS (ID, trx_date, work_order ...) values(@p0, @p1, @p2 ...)")

cmd1.Parameters.Add("@p0", SqlDbType.Int)
cmd1.Parameters.Add("@p1", SqlDbType.DateTime) 'if it's a datetime2 with a scale, use the overload that accepts a SqlParameter, and do a New With to set the scale
cmd1.Parameters.Add("@p2", SqlDbType.VarChar)
...

然后在循环内反复设置新值并执行命令:

cmd1.Parameters("@p0").Value = dr(0) 'or whatever dr index you want ID to be
cmd1.Parameters("@p1").Value = dr(1) 'or whatever dr index you want trx_date to be
cmd1.Parameters("@p2").Value = dr(2) 'or whatever dr index you want work_order to be
...

在 INSERT 中的表名之后命名您要插入的所有列,这样您就不必插入大量虚拟值


.. 或者考虑使用 SqlCommandBuilder 从表定义中为您创建 INSERT

【讨论】:

    【解决方案2】:

    应该这样做,包括修复一些不良做法,尤其是 SQL 注入问题!不要忘记在此处的适当位置为您的实际数据库表设置正确的类型和长度代码(有注释指出)。

    Imports System.Configuration
    Imports System.Data.SqlClient
    
    Public Class DataCollector
    
        Dim conString1 As String = "Data Source=NORMAC-CTMS\SQLEXPRESS;Database=Normac Data;Integrated Security=true"
        Dim conString2 As String = "Data Source=STLEDGSQL01;Database=MES_DEV;Integrated Security=true"
        Dim timer1 As Timers.Timer
    
        Protected Overrides Sub OnStart(ByVal args() As String)
    
            timer1 = New Timers.Timer()
            timer1.Interval = 5000
            AddHandler timer1.Elapsed, AddressOf OnTimedEvent
            timer1.Enabled = True
            FileIO.WriteLog("Service has started")
    
        End Sub
    
        Protected Overrides Sub OnStop()
            timer1.Enabled = False
            FileIO.WriteLog("Service has stopped")
        End Sub
    
        Private Sub OnTimedEvent(obj As Object, e As EventArgs)
    
            Dim sql1 As String = "
    SELECT ID, RTRIM(trx_date), RTRIM(work_order), RTRIM(department),
        RTRIM(work_center), RTRIM(operation_no), RTRIM(operator),
        RTRIM(total_labor_hours), RTRIM(feet_produced), RTRIM(item_no),
        RTRIM(posted), RTRIM(lot_no), RTRIM(default_bin) 
    FROM NOR_LABOR 
    WHERE ID > 46006
    ORDER BY ID ;
    "
    
            Dim sql2 As String = "
    INSERT INTO ALL_LABOR_DETAILS 
    VALUES 
    (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7, @p8, @p9, @p10, @p11, @p12);
    "
    
            Dim dt As new DataTable
    
            Try
                Using cn  As New SqlConnection(conString1), _
                      cmd As New SqlCommand(sql1, cn), _
                      da  As  New SqlDataAdapter(cmd)
    
                    da.Fill(dt)
                End Using
    
                Using cn  As New SqlConnection(conString2), _
                      cmd As New SqlCommand(sql2, cn)
               
                    'Use actual types and lengths from the DB here!
                    cmd.Parameters.Add("@p0", SqlDbType.Int)
                    cmd.Parameters.Add("@p1", SqlDbType.Int)
                    cmd.Parameters.Add("@p2", SqlDbType.Int)
                    cmd.Parameters.Add("@p3", SqlDbType.Int)
                    cmd.Parameters.Add("@p4", SqlDbType.Int)
                    cmd.Parameters.Add("@p5", SqlDbType.Int)
                    cmd.Parameters.Add("@p6", SqlDbType.Int)
                    cmd.Parameters.Add("@p7", SqlDbType.Int)
                    cmd.Parameters.Add("@p8", SqlDbType.Int)
                    cmd.Parameters.Add("@p9", SqlDbType.Int)
                    cmd.Parameters.Add("@p10", SqlDbType.Int)
                    cmd.Parameters.Add("@p11", SqlDbType.Int)
                    cmd.Parameters.Add("@p12", SqlDbType.Int)
    
                    cn.Open()
                    For Each row As DataRow In dt.Rows
                        For i As Integer = 0 To 12
                            cmd.Parameters($"@p{i}").Value = row(i)
                        Next i
    
                        cmd.ExecuteNonQuery()
                        FileIO.WriteLog($"Most Recent Normac ID {row(0)}")            
                    Next row
                End Using
            Catch ex As Exception
                FileIO.WriteLog(ex.Message)
            End Try
        End Sub
    End Class
    

    【讨论】:

    • 我真的很感谢你的帮助,在运行你的代码后它给了我这个错误“列名或提供的值的数量与表定义不匹配。”我相信这是因为我在目标表中有一些额外的列,这些列是用于其他目的的。我应该如何跳过那些不需要的列并仅添加我需要的列?
    • 我发现了错误,为不传递任何值的参数添加了 DBNull.Value。再次非常感谢您的帮助:)
    • 或者,就像我在回答中建议的那样,在 INSERT 中指定要插入到的所有列:INSERT INTO ALL_LABOR_DETAILS (ID, trx_date, work_order ...) VALUES (@p0,@p1,@p2...) - 查看 ALL_LABOR_DETAILS 之后的列名..
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-27
    • 2014-10-08
    相关资源
    最近更新 更多