查询在循环内失败(returns EOF)

Query fails inside loop (returns EOF)

当我有硬编码值时,我的查询在循环外工作正常。当我将查询放入我的循环内并使用变量来保存正确的值时,它 returns EOF。我已经打印出查询并 运行 直接在 SQL 服务器中,它 return 是正确的结果。这让我觉得我的 SQL 语法没问题,但我不明白为什么它在循环中没有 return 任何东西。有什么想法吗?

Public Function getPOs()
    Dim TotalPos, Curpo, Query, ClaimNum, Color, DCloc As String
    Dim i As Integer
    Dim Row, Style, LastRow As LongPtr
    Dim ws As Worksheet

    Set ws = Worksheets("test")


    ' Set up database connection
    Dim cnn As ADODB.Connection
    Dim rs As New ADODB.Recordset

    Set cnn = New ADODB.Connection
    cnn.ConnectionString = SQL_SERVER_CONNECTION
    cnn.ConnectionTimeout = 0
    cnn.CommandTimeout = 0
    cnn.Open

    'This query works fine, it returns results that I can iterate through.
    rs.Open "SELECT PO " & _
    "FROM [catalog].[dbo].[table] " & _
    "WHERE CLAIM_NUMBER = '1337' AND STYLE = '293493' and COLOR = '03' AND DC_LOCATION = 'PFC'", cnn, adOpenDynamic, adLockOptimistic


        ' Itereate through the results
        i = 0
        Do While Not rs.EOF
            If rs![PO] = "" Then
                Exit Do
            End If
            If i = 0 Then
                Curpo = rs![PO]
                TotalPos = Curpo
            Else
                Curpo = rs![PO]
                TotalPos = TotalPos & ", " & Curpo
            End If

            i = i + 1
            rs.MoveNext
        Loop
    MsgBox TotalPos ' Works fine!




    ' For some reason adding the query inside this loop messes it up.
    Row = 11
    LastRow = ws.Cells(ws.Rows.Count, "D").End(xlUp).Row
    rs.Close
    While Row < 12 ' will change back to LastRow once working

        'Parse the claim number
        ClaimNum = Replace(ws.Cells(Row, 10), "IC - ", "")
        MsgBox ClaimNum

        'Style
        Style = Left(ws.Cells(Row, 11), Len(ws.Cells(Row, 11)) - 2)
        MsgBox Style

        'Color
        Color = ws.Cells(Row, 12)
        MsgBox Color

        'DCloc
        DCloc = ws.Cells(Row, 13)
        MsgBox DCloc

        ' When I add the query here it returns nothing...
        rs.Open "SELECT PO " & _
        "FROM [catalog].[dbo].[table] " & _
        "WHERE CLAIM_NUMBER = " & ClaimNum & " AND STYLE = " & Style & " and COLOR = '" & Color & "' AND DC_LOCATION = ' " & DCloc & "'", cnn, adOpenDynamic, adLockOptimistic

        'add the entire sql statement to the Query var so I can print it out and run it in SQL Server
        Query = "SELECT PO " & _
        "FROM [catalog].[dbo].[table] " & _
        "WHERE CLAIM_NUMBER = " & ClaimNum & " AND STYLE = " & Style & " and COLOR = '" & Color & "' AND DC_LOCATION = '" & DCloc & "'"

        ' print the query... when I run this exact thing in SQL server it returns results just fine'
        MsgBox Query

        ' iterate through results
        i = 0

        'rs.EOF now that it's in the loop... but why? I know the syntax of the query is correct, it returns results when I run it directly in SQL server
        If rs.EOF Then
            MsgBox "why???"
        End If

        Do While Not rs.EOF
            If rs![PO] = "" Then

                Exit Do
            End If
            If i = 0 Then
                Curpo = rs![PO]
                TotalPos = Curpo
            Else
                Curpo = rs![PO]
                TotalPos = TotalPos & ", " & Curpo
            End If
            MsgBox TotalPos
            i = i + 1
            rs.MoveNext
        Loop

    rs.Close
    Row = Row + 1
    Wend
    cnn.Close

End Function

您需要将变量用引号引起来才能正常工作,字符串类型是不够的。

"WHERE CLAIM_NUMBER = " & ClaimNum & " ... 

需要成为:

"WHERE CLAIM_NUMBER = " & "'" &  ClaimNum & "'" & " ...

除了您要连接到 SQL 语句中的所有其他变量之外

顺便说一句

Dim TotalPos, Curpo, Query, ClaimNum, Color, DCloc As String

仅将 DCloc 声明为字符串,其他所有都是变体。

要使它们全部成为字符串,您需要向所有这些添加 as string

Dim TotalPos as string, Curpo as string, Query as string, ClaimNum as string, Color as string, DCloc As String
rs.Open "SELECT PO " & _
"FROM [catalog].[dbo].[table] " & _
"WHERE CLAIM_NUMBER = " & ClaimNum & " AND STYLE = " & Style & " and COLOR = '" & Color & "' AND DC_LOCATION = ' " & DCloc & "'"

不要将参数值连接到您的 SQL 字符串中 - 这样您就不需要关心引用字符串和担心字符串是否包含撇号,或者更糟的是 - 广为人知的故事Little Bobby Tables 捕捉到了这种粗心大意的值串联做法的影响力,如果你愿意的话。

相反,将您的查询定义一次,然后让服务器处理参数(这是它的工作)。

Const sql As String = _
    "SELECT PO " & _
    "FROM [catalog].[dbo].[table] " & _
    "WHERE CLAIM_NUMBER = ? AND STYLE = ? AND COLOR = ? AND DC_LOCATION = ?"

每个 ? 都被 ADODB 理解为位置参数:您现在需要做的就是执行带有 4 个参数的 ADODB.Command,并按指定的顺序附加。

现在您可以编写一个 Function 来获取您需要的 4 个参数的值,并且该函数可以 return 一个包含结果的 ADODB.Recordset - 无需重新定义每次需要时 SQL 字符串!

Private Function GetPO(ByVal cnn As ADODB.Connection, ByVal ClaimNum As String, ByVal Style As String, ByVal Color As String, ByVal DCloc As String) As ADODB.Recordset

    Const sql As String = _
        "SELECT PO " & _
        "FROM [catalog].[dbo].[table] " & _
        "WHERE CLAIM_NUMBER = ? AND STYLE = ? AND COLOR = ? AND DC_LOCATION = ?"

    Dim cmd As ADODB.Command
    Set cmd = New ADODB.Command

    Set cmd.ActiveConnection = cnn
    cmd.CommandType = adCmdText
    cmd.CommandText = sql

    'TODO: verify parameter types & sizes - here NVARCHAR(200).
    'NOTE: parameters must be added in the order they are specified in the SQL.
    cmd.Parameters.Append cmd.CreateParameter(Type:=adVarWChar, Size:=200, Value:=ClaimNum)
    cmd.Parameters.Append cmd.CreateParameter(Type:=adVarWChar, Size:=200, Value:=Style)
    cmd.Parameters.Append cmd.CreateParameter(Type:=adVarWChar, Size:=200, Value:=Color)
    cmd.Parameters.Append cmd.CreateParameter(Type:=adVarWChar, Size:=200, Value:=DCloc)

    Set GetPO = cmd.Execute

End Function

您可以在任何有 ADODB.Connection 的地方使用它:

Dim rs As ADODB.Recordset
Set rs = GetPO(cnn, ClaimNum, Style, Color, DCloc)

Do While Not rs.EOF
    '...
Loop