通过VBA脚本从EXCEL将多个值插入SQL数据库

时间:2013-11-27 15:13:22

标签: sql sql-server vba sql-server-2008 excel-vba

尝试通过VBA脚本将5个单元格中的数据插入到SQL Server 08数据库的列中。

所以基本上我有1个包含4列的表,我想一次将多组数据插入到列中,这会将数据插入到DB中,结果如下。

Server Name     Middleware  Version License 
TEST6           Testing     1       1
TEST6           Testing1    1       1
TEST6           Testing2    1       1
TEST6           Testing3    1       1

我知道下面的代码中的值不正确,但是当执行VBA脚本时,我收到错误消息(在vba代码下面)。

Dim val1 As String, val2 As String, val3 As String, val4 As String

val1 = Range("B126").Value
val2 = Range("C126").Value
val3 = Range("C127").Value
val4 = Range("D126").Value

conn.Open sConnString

Dim item As String
item4 = "INSERT INTO [IndustrialComp].[dbo].[Middleware]("
item4 = item4 & "  [server_name],[middleware],[middlware],[version]"

item4 = item4 & "  )Values("
item4 = item4 & "  '" & val1 & "', '" & val2 & "', '" & val3 & "','" & val4 & "')"

conn.Execute item4

End Sub

Msg 264,Level 16,State 1,Line 1 在SET子句中多次指定列名“middleware”。在同一SET子句中不能为列分配多个值。修改SET子句以确保列只更新一次。如果SET子句更新视图的列,则列名“middleware”可能在视图定义中出现两次。

3 个答案:

答案 0 :(得分:1)

我相信您在INSERT语句中指定的列是重复的,因此不正确。 尝试:

item4 = item4 & "  [server_name],[middleware],[version],[license]"

更新: 您的SQL语句应如下所示:

INSERT INTO [IndustrialComp].[dbo].[Middleware]([server_name],[middleware],[version],[license])
VALUES ('TEST6','Testing',1,1)
      ,('TEST6','Testing1',1,1)
      ,('TEST6','Testing2',1,1)
      ,('TEST6','Testing3',1,1)

因此,您必须在要插入的每一行的括号之间重复该块。

然而,您现在只有4个变量在您的解决方案中包含4个不同的值,因此您永远无法插入这4个不同的行,因为您只选择单元格B126,C126中的值, C127和D126。这可能是您要插入的第一行?或者您想自己添加1,2,3到Testing并重复其他值? 请相应地解释和更新您的答案。

答案 1 :(得分:0)

我假设数据在Excel中。如果是这样,只需循环遍历行。此外,您的val1,val2等似乎与该示例不匹配。也许你的意思是val3是D126而val4是E126。我会假设的。这是更正后的代码:

Dim sSQL as string
Dim i as long

i=0
while ActiveSheet.Range("B126").offset(i,0).value <> "" 'stop when there is a blank cell
    i=i+1
    conn.Open sConnString
    sSQL = "INSERT INTO [IndustrialComp].[dbo].[Middleware]("
    sSQL = sSQL & "  [server_name],[middleware],[version],[license]"
    sSQL = sSQL & "  )Values ("
    sSQL = sSQL & "  '" & ActiveSheet.Range("B126").offset(i,0).Value & "', "
    sSQL = sSQL & "  '" & ActiveSheet.Range("C126").offset(i,0).Value & "', "
    sSQL = sSQL & "  '" & ActiveSheet.Range("D126").offset(i,0).Value & "', "
    sSQL = sSQL & "  '" & ActiveSheet.Range("E126").offset(i,0).Value & "' "
    sSQL = sSQL & ")"

    conn.Execute sSQL
wend

代码未经测试但已编译。

答案 2 :(得分:0)

如果您有多行数据,则在一次调用存储过程中一次性插入所有行会更有效。为此,您将数据序列化为xml,然后调用存储过程,该过程将xml作为字符串参数。

AFAIK您无法序列化词典,但您可以序列化列表。

因此,我建议您循环填充具有适当值的List的Dictionary(词典列表)。我倾向于使用Structures,但简单的类也可以正常工作,例如:

Public Class DBData
    Public pKey As Integer
    Public pValue As Double
    Public Sub New(key As Integer, val As Double)
        pKey = key
        pValue = val
    End Sub
    Public Sub New()
        pKey = 0
        pValue = 0.0
    End Sub
End Class

创建您的列表并填写它,例如:

Dim myList As New List(Of DBData)
For Each kvPair In dict
    myList.Add(New DBData(kvPair.Key, kvPair.Value))
Next

现在有点神奇的序列化:

Dim sw As New StringWriter
Dim serializer As New XmlSerializer(GetType(List(Of DBData)))
Dim ns As New XmlSerializerNamespaces()
serializer.Serialize(sw, myList)

Dim xml As String
xml = sw.ToString
Dim pos As Integer

pos = xml.IndexOf("<Array")
xml = xml.Substring(pos)

请注意,我正在剥离刚刚创建的xml的标头。这是因为如果包含标题,我从来没有能够使SQL Server工作 - 你可能可以,我只是看起来不够努力。

我们像这样调用存储过程(显然将连接字符串更改为适合您的存储过程):

Using conn As New SqlConnection("Integrated Security=true; Initial Catalog=dbname; Data Source=servername")
    Using cmd As New SqlCommand("uspDBDataInsert", conn)
        cmd.CommandType = CommandType.StoredProcedure
        Dim param As SqlParameter
        param = cmd.CreateParameter
        param.ParameterName = "@dbdata"
        param.DbType = DbType.String
        param.Value = xml
        cmd.Parameters.Add(param)
        conn.Open()
        cmd.ExecuteNonQuery()
        conn.Close()
    End Using
End Using

对于SQL Server,所需的过程如下所示:

CREATE PROCEDURE [dbo].[uspDBDataInsert] 
    -- Add the parameters for the stored procedure here
    @dbdata varchar(MAX)

AS

BEGIN try
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;

    declare @idoc int

    --Create an internal representation of the XML document
    EXEC sp_xml_preparedocument @idoc OUTPUT, @dbdata

    insert into DBData
    (
        id, rowvalue    
    )
    select
        pKey,pValue
    FROM OPENXML (@idoc, '/ArrayOfDBData/DBData',2)
    WITH (
        pKey int,
        pValue float
    )
    select @@ROWCOUNT

end try

begin catch
    declare @msg nvarchar(200)
    SELECT @msg = ('Error during insert of DBData...')
end catch

显然,你需要将ArrayOf更改为你所谓的课程,当然还要使用自己的表格。

虽然我在这里只显示了一个简单的键/值对,但您可以用完全相同的方式填充多列表。

需要注意的一点是:您的类/结构在Public模块中必须是Public,否则serialize将不起作用。此外,如果您为类提供自己的参数构造函数(就像我一样),您还必须提供无参数构造函数,否则序列化程序将再次对象。

HTH

附录

我忘记了,为了让VB工作,你需要:

Imports System.IO
Imports System.Xml.Serialization
Imports System.Data.SqlClient