查询在循环内失败(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
当我有硬编码值时,我的查询在循环外工作正常。当我将查询放入我的循环内并使用变量来保存正确的值时,它 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