Excel VBA Recordset is empty - sql-server

i have an ADODB recordset to load data from a view in an Excel sheet. The view is located in a MS SQL database. It runs perfectly for months, but since a few days the recordset is always empty, so i did not get any results. After a long day searching the www for any reasons i found, that it can happen because i use a x32 Excel and there is so much data in the view. So i separated the procedure in two queries. This helped a lot and the macro run perfectly again. Yesterday the same error appears again, so i began to split the procedure again. But now the recordset is still empty. I don't know any reason for this. I tested it with a selection of only ten rows of the view and this runs but if i want to get 1000 rows the recordset is empty again. Does anybody know a reason for this problem? All queries give the result i want to have in the database so they are ok.
Here is my code:
Sub doStuff()
Dim sqlStatement(9) As String
Dim lrow As Long
'... other variables
sqlStatement(1) = "Select * from db.View1 where location like 'forest'"
'... other sqlStatements
For i = 0 To UBound(sqlStatement)
Call loadData(sqlStatement, lrow)
Next i
End Sub
Sub loadData(sqlStatement As String, lastrow As Long)
Dim sqlServer As String
Dim dbName As String
sqlServer = "MSSQLSERVER"
dbName = "database"
On Error Resume Next
Dim con As Object
Set con = CreateObject("ADODB.Connection")
con.Open _
"Provider = sqloledb;" & _
"Data Source=" & sqlServer & ";" & _
"Initial Catalog=" & dbName & ";" & _
"User ID=user1;" & _
"Password=abcde;"
Dim rst As Object
Set rst = CreateObject("ADODB.Recordset")
With rst
.ActiveConnection = con
.Open sqlStatement , con, adLockReadOnly
ws.Activate
If lastrow = 1 Then
For col = 0 To .Fields.Count - 1
ws.Cells(1, col + 1).Value = .Fields(col).Name
Next
End If
ws.Activate
ws.Cells(lastrow+1,1).CopyFromRecordset rst
.Close
End With
con.Close
Set rst = Nothing
Set con = Nothing
End Sub

Related

How can I solve the error for connecting to SQL Server

I am getting an error for connecting to the sql error is named pipes provider could not open a connection to SQL Server 1265. Here is the code and it worked yesterday and when I check it today it is not working and I get the error.
Here is the vb code:
'Require all variables to be defined
'to prevent rogue variables and limit
'debugging time
Option Explicit
'====================================================================================
' GLOBAL VARIABLES
'====================================================================================
Private Const g_sqlServer = "EWNVM-2017U3"
Private g_lStartDate As Long
Private g_nDaysInMonth As Integer
Public Enum mrReportType
mrDailyReport
mrMonthlyReport
mrYearlyReport
End Enum
'====================================================================================
' GetData(nYear, nMonth)
'====================================================================================
Public Sub GetData(ByVal eReportType As mrReportType, ByVal nYear As Integer, Optional ByVal nMonth As Integer = 1, Optional ByVal nDay As Integer)
' On Error GoTo ErrorHandler
Dim cMRReport As New MRReport
Dim adoConn As New ADODB.Connection
Dim adoRS As New ADODB.Recordset
Dim sSqlQuery As String
Dim sStartDateFmt As String
Dim i, k As Integer
Dim sLink As String
'Get Start Date
g_lStartDate = cMRReport.GetStartDate(nYear, nMonth, nDay)
'Write report Date to RawData sheet to use on other sheets
RawData.Range("A1") = Format(g_lStartDate, "mm/yyyy")
'Show Progress Bar Form
cMRReport.ShowProgressBar
'===========================================================================================================================================
'Historian Database Queries
'===========================================================================================================================================
adoConn.ConnectionString = "Provider='SQLNCLI11';Data Source='" & g_sqlServer & "';Initial Catalog='MR_Carrolton_DB';User ID='mrsystems';Password='Reggie#123';"
adoConn.CursorLocation = adUseClient
adoConn.Open
'Daily Report Type
RawData.Range("B4", "AZ39").ClearContents
cMRReport.SetHeader Sheet2, Positioncenter, "Monthly WAS Tank Blower Runtimes Report" & vbCr & Format(g_lStartDate, "mmmm yyyy")
cMRReport.SetHeader Sheet2, PositionRight, "Pee Dee River WWTP" & vbCr & "City of Florence, SC"
QueryRuntimesDaily adoConn, adoRS, cMRReport
'Close Historian DB Connection
adoConn.Close
'-------------------------------------------------------------------------------------------------------------------------------------------
'Cleanup memory by closing
'classes we initialized
Set adoRS = Nothing
Set adoConn = Nothing
Set cMRReport = Nothing
Exit Sub
ErrorHandler:
'Clean Up
If Not adoConn Is Nothing Then
If adoConn.State = adStateOpen Then adoConn.Close
End If
Set adoConn = Nothing
cMRReport.HandleError err, "Report", "GetData"
End Sub
'===========================================================================================================================================
'Historian Database Queries Functions
'===========================================================================================================================================
'-----------------------------------------
'Query for Flow Totals Daily
'-----------------------------------------
Private Sub QueryRuntimesDaily(ByVal adoConn As ADODB.Connection, ByRef adoRS As ADODB.Recordset, cMRReport As MRReport)
' On Error GoTo ErrorHandler
Dim sSqlQuery As String
Dim i As Integer
Dim startDateSerial
Dim endDateSerial
startDateSerial = CDec(DateAdd("n", 1 * i, g_lStartDate))
' MsgBox startDateSerial
endDateSerial = CDec(DateAdd("n", 1 * i + 15, g_lStartDate))
' MsgBox endDateSerial
For i = 0 To 95
' sSqlQuery = "SELECT LogDateTime, CL2_RESIDUAL,ZW1_TURBIDITY,ZW2_TURBIDITY,ZW3_TURBIDITY,ZW4_TURBIDITY FROM MR_Carrolton_DB.dbo.DailyRuntimes ORDER BY LogDateTime"
sSqlQuery = "SELECT LogDateTime, CL2_RESIDUAL " & _
" FROM MR_Carrolton_DB.dbo.DailyRuntimes" & _
" WHERE LogDateTime >= " & startDateSerial & _
" AND LogDateTime < " & endDateSerial & _
" ORDER BY LogDateTime"
'Copy sSqlQuery value to RawData worksheet for troubleshooting
RawData.Range("B2").Value = sSqlQuery
'Open recordset (executes SQL query)
adoRS.Open sSqlQuery, adoConn, 0, 1, 1
'If recordset is not empty then copy data to raw sheet
If adoRS.BOF = False And adoRS.EOF = False Then
RawData.Cells((i + 4), 2).CopyFromRecordset adoRS
End If
'Close recordset after each query
adoRS.Close
'Update Progress Bar
cMRReport.UpdateProgressBar i, 96, "Querying for Daily Runtimes..."
'Prevent VBA from locking up Excel
'while running through loops
DoEvents
Next i
Exit Sub
ErrorHandler:
'Clean Up
If Not adoConn Is Nothing Then
If adoConn.State = adStateOpen Then adoConn.Close
End If
Set adoConn = Nothing
cMRReport.HandleError err, "Report", "QueryRuntimesMonthly"
End Sub
'-----------------------------------------
' Lock/Unlock Worksheets
'-----------------------------------------
Public Sub LockWorksheets()
Dim ws As Worksheet
Dim i As Integer
For Each ws In Worksheets
ws.Protect "reggie"
Next
End Sub
Public Sub UnLockWorksheets()
Dim ws As Worksheet
Dim i As Integer
For Each ws In Worksheets
ws.Unprotect "reggie"
Next
End Sub
it seems it is more of server administration issue than a coding one. Ping your server to check if there is connectivity problem. Your connection does not persist so you must check your "hosts" file and Sql Server settings if they are properly set.
Visit this page for step by step troubleshooting:
Resolving could not open a connection to sql server errors

SQL Functionality in Excel VBA

For those who don't know, it is fairly easy to add SQL functionality to VBA macros. The following article provides the code: http://analystcave.com/excel-using-sql-in-vba-on-excel-data/
I modified this a bit (happy to provide the code) so that it outputs nicely and put it in a Sub that I can call. This saves me from having to do multiple sorts, copy paste, etc. to find specific data from a large worksheet.
Note a problem, however, when working with the workbook from an online source e.g. Gmail:
.ConnectionString = "Data Source=" & ThisWorkbook.Path & "\" & ThisWorkbook.Name & ";"
This works fine when the file is saved to a drive, but from an online site, Excel can't connect. Any suggestions on modifying the connection string for when the file isn't saved anywhere?
For anyone who's interested, this code (based on the code from Analyst Cave) works great for using SQL in VBA. Save the following as a Sub:
Option Explicit
Sub QuerySQL(result_location As Range, query As String)
Dim ResultWS As Worksheet
Set ResultWS = ThisWorkbook.Sheets("Query Results")
ResultWS.Cells.ClearContents
If query = "" Then Exit Sub
Dim cn As Object, rs As Object
'Add to the workbook a database connection with itself
'Note other ConnectionString could be used to access a variety of media
Set cn = CreateObject("ADODB.Connection")
With cn
.Provider = "Microsoft.ACE.OLEDB.12.0"
.ConnectionString = "Data Source=" & ThisWorkbook.Path & "\" & ThisWorkbook.Name & ";" & _
"Extended Properties=""Excel 12.0 Xml;HDR=YES"";"
.Open
End With
'Build and execute the SQL query
Set rs = cn.Execute(query)
If rs.EOF = True Then Exit Sub
'Print column labels
Dim i As Long, j As Long
For i = 0 To rs.Fields.Count - 1
result_location.Offset(0, i).Value = rs.Fields(i).Name
Next i
'Print column contents
i = 0
Do
For j = 0 To rs.Fields.Count - 1
result_location.Offset(i + 1, j).Value = rs.Fields(j).Value
Next j
rs.MoveNext
i = i + 1
Loop Until rs.EOF
'Close the connections
rs.Close
cn.Close
Set rs = Nothing
Set cn = Nothing
End Sub
To use it, simply do the following:
Dim myQuery As String
myQuery = "SELECT * FROM [Sheet2$]"
Call QuerySQL(ThisWorkbook.Sheets("Sheet1").Range("A1"), myQuery)
It uses MS Access style SQL. The above will look to Sheet2 as the table and print the result starting in A1 on Sheet1.

Runtime error 3001 'Arguments are of the wrong type or out of acceptable range...'

I have an Excel table and want to update SQL Server table records' date value (with getdate function) which referred by 12th column of Excel.
My code is as below, but I'm seeing:
Run-time error 3001 Arguments are of the wrong type or out of acceptable range or are in conflict with one another.
In SQL table MODIFIEDDATE field is datetime type, and MAINREF field is integer type.
Private Sub CommandButton2_Click()
Dim conn2 As New ADODB.Connection
Dim rst2 As New ADODB.Recordset
Dim j As Integer
conn2.ConnectionString = "Provider=SQLOLEDB.1;Password=abc;Persist Security Info=True;User ID=sa;Initial Catalog=logodb;Data Source=A3650;Use Procedure for Prepare=1;Auto"
conn2.Open
For j = 0 To 1900
If Sayfa1.Cells(j + 4, 12) = "" Then
Sayfa1.Cells(j + 4, 13) = "empty"
Else
rst2.Open "UPDATE T_015 SET MODIFIEDDATE=GETDATE() WHERE MAINREF='" & Sayfa1.Cells(j + 4, 12) & "'", conn, 1, 3
rst2.Close
End If
Next j
End Sub
I've tried to change the SQL query like, (CInt(cell.value))
rst2.Open "UPDATE T_015 SET MODIFIEDDATE=GETDATE() WHERE MAINREF='" & CInt(Sayfa1.Cells(j + 4, 12)) & "'", conn, 1, 3
but, it didn't work.
The ADODB.Recordset object should not be used for an UPDATE query. Execute the SQL statement directly from the ADODB.Connection
Dim conn2 As New ADODB.Connection
conn2.ConnectionString = "Provider=SQLNCLI11;Server=MYSERVER;Database=TMP;UID=sa;password=abc;"
conn2.Open
conn2.Execute "UPDATE T_015 SET MODIFIEDDATE=GETDATE() WHERE MAINREF=1"
There are a couple of ways you can fulfil your requirements and it seems that you are mixing the two.
One way is to update records one at a time, and to do this you would use the Connection object or, more preferably, the Command object. I say preferably because parameterised commands are a far more robust way of executing your SQL. If you intend to use SQL then it's something you probably ought to read about. The way you would do that is as follows:
Public Sub ParameterisedProcedure()
Dim conn As ADODB.Connection
Dim cmd As ADODB.Command
Dim prm As ADODB.Parameter
Dim v As Variant
Dim j As Integer
'Read the sheet data
v = Sayfa1.Range("L4", "M1904").Value2
'Open the database connection
Set conn = New ADODB.Connection
conn.ConnectionString = "Provider=SQLOLEDB.1;" & _
"Password=abc;" & _
"Persist Security Info=True;" & _
"User ID=sa;" & _
"Initial Catalog=logodb;" & _
"Data Source=A3650;" & _
"Use Procedure for Prepare=1;" & _
"Auto"
conn.Open
'Loop through the values to update records
For j = 1 To UBound(v, 1)
If IsEmpty(v(j, 1)) Then
v(j, 2) = "empty"
Else
'Create the parameterised command
Set cmd = New ADODB.Command
cmd.ActiveConnection = conn
cmd.CommandType = adCmdText
cmd.CommandText = "UPDATE T_015 " & _
"SET MODIFIEDDATE=? " & _
"WHERE MAINREF=?"
prm = cmd.CreateParameter(Type:=adDate, Value:=Now)
cmd.Parameters.Append prm
prm = cmd.CreateParameter(Type:=adInteger, Value:=v(j, 1))
cmd.Parameters.Append prm
cmd.Execute
End If
Next
'Write the updated values
Sayfa1.Range("L4", "M1904").Value = v
'Close the database
Set prm = Nothing
Set cmd = Nothing
conn.Close
End Sub
The other way is to use Transactions and you would indeed use a Recordset for that (ie similar to what you have already done). In this case I'd suggest it is the better way to do it because executing one command at a time (as in the above code) is very slow. A vastly quicker way would be to commit all your updates in one transaction. Like a parameterised command, it's also safe from rogue strings entering your SQL command text. The code would look like this:
Public Sub TransactionProcedure()
Dim conn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim cmdText As String
Dim v As Variant
Dim j As Integer
'Read the sheet data
v = Sayfa1.Range("L4", "M1904").Value2
'Open the database connection
Set conn = New ADODB.Connection
conn.ConnectionString = "Provider=SQLOLEDB.1;" & _
"Password=abc;" & _
"Persist Security Info=True;" & _
"User ID=sa;" & _
"Initial Catalog=logodb;" & _
"Data Source=A3650;" & _
"Use Procedure for Prepare=1;" & _
"Auto"
conn.Open
'Retrieve the data
Set rs = New ADODB.Recordset
cmdText = "SELECT * FROM T_015"
rs.Open cmdText, conn, adOpenStatic, adLockReadOnly, adCmdText
'Loop through the values to update the recordset
On Error GoTo EH
conn.BeginTrans
For j = 1 To UBound(v, 1)
If IsEmpty(v(j, 1)) Then
v(j, 2) = "empty"
Else
'Find and update the record
rs.Find "MAINREF=" & CStr(v(j, 1))
If Not rs.EOF Then
rs!ModifiedDate = Now
rs.Update
End If
End If
Next
conn.CommitTrans
'Write the updated values
Sayfa1.Range("L4", "M1904").Value = v
'Close the database
rs.Close
conn.Close
Exit Sub
EH:
conn.RollbackTrans
Sayfa1.Range("L4", "M1904").Value = v
rs.Close
conn.Close
MsgBox Err.Description
End Sub

Error in VBA Excel-SQL coding as "User Defined Type Not Defined" for "Recordset"

I am relatively experienced in VBA coding, but I am totally new in MS SQL server 2008.
I am trying to export an Excel table like below to a SQL server:
A B C D E
1 Name Year ID
2 Jill 2015 17
3 Jack 2012 13
4 Mike 1999 25
5
After activating ADO and DAO in the tools, it fixed the error "User Defined Type Not Defined" for the line Dim rs As New ADODB.Recordset. But now I get an error for the line Set dbclass = New clsDB. I don't understand what is wrong. The same definition structure works in another Workbook.
Private Sub Transtable()
Dim connOk As Boolean
Dim MyWorkBook As Workbook
Dim CurSheet As Worksheet
Dim listObj As ListObject
Dim rs As New ADODB.Recordset
' Dim dbclass As New clsDB ' both description leads to the same "User Undefined" error
' Dim dbclass As New ADODB.clsDB
Set dbclass = New clsDB
dbclass.Database = "Tables"
dbclass.ConnectionType = SqlServer
dbclass.DataSource = "E72b1783"
dbclass.UserId = Application.UserName
connOk = dbclass.OpenConnection(False, True)
If connOk = False Then
MsgBox "Connection not successfull"
Else
MsgBox "Connection successfull"
End If
tableName = "TableName1"
Set CurSheet = Sheet2
Set listObj = CurSheet.ListObjects(tableName) 'Table Name
'get range of Table
HeaderRange = listObj.HeaderRowRange.Address
DataRange = listObj.DataBodyRange.Address
dbclass.Open "Provider=Microsoft.ACE.OLEDB.12.0;" & _
"Data Source=" & ThisWorkbook.FullName & ";" & _
"Extended Properties=""Excel 12.0;HDR=Yes;IMEX=1"";"
strSQL = "SELECT * FROM [" & ws.Name & "$" & Replace(DataRange, "$", "") & "];"
rs.Open strSQL, dbclass, adOpenStatic, adLockReadOnly
arrData = rs.GetRows
rs.Close
dbclass.Close
Set rs = Nothing
Set dbclass = Nothing
Set listObj = Nothing
Set CurSheet = Nothing
End Sub
Make sure the Data Access Objects library (DAO) is checked in the Tools. Also look at the ActiveX Data Objects library (ADO) in the list. References list in the VBA Editor's menu/ribbon. (The specific name of the library can be different in different versions of Access)

"Application Defined or Object Defined" error in VBA-SQL connection

I am trying to write an Exce-Vba code for SQL connection. The code, first will open the connection to the server, then it will copy a 4 columns of table (Range("C22:G81")) from my Excel-sheet to the SQL-server (I am only trying to send numerical table now as a test, I don't send any column name)
I have been trying to solve a "Application Defined or Object Defined" error quite long time. I get the error for the connection string strCon = "Provider=Microsoft.ACE.OLEDB.12.0; Data Source=" & strName & ";Extended Properties=""Excel 12.0;"
I even tried with another version with password option like strCon = "Provider=Microsoft.ACE.OLEDB.12.0; Data Source=" & strName & ";Extended Properties=""Excel 12.0; Jet OLEDB:Database Password='passwd';"
But I get the same error. I am quite new in SQL-coding. I wonder if I am missing something important.
Lasly, I don't know if it is related to this error, but I manually created 4 columns in the SQL server for my 4 columns in the Excel. Do I need to write something specific that those 4 columns in the Excel-sheet will find the right columns in the SQL-server?
Thanks in advance...
The code:
Private Sub inlasning()
Dim MyWorkBook As Workbook
Dim rs As New ADODB.Recordset
Dim conn As New ADODB.Connection
Dim ServerName As String, DataBaseName As String, strSQL As String
Set conn = New ADODB.Connection
ServerName = "E45c7642"
DataBaseName = "Tables"
' Specify the OLE DB provider
conn.Provider = "sqloledb"
' Set SQLOLEDB connection properties
conn.Properties("Data Source").Value = ServerName
conn.Properties("Initial Catalog").Value = DataBaseName
' Windows NT authentication.
conn.Properties("Integrated Security").Value = "SSPI"
conn.Open
Dim ValidSheet As Worksheet
Dim HeaderRange As Range
Dim DataRange As Range
Dim ColRange As Range
Dim LastRange As Range
Dim strName As String, strCon As String
strName = ThisWorkbook.FullName
Application.ScreenUpdating = False
Set ValidSheet = ThisWorkbook.Sheets("Sheet2") '
Set HeaderRange = ValidSheet.Range("C20:G21")
Set ColRange = HeaderRange.Find(TheHeader, , , xlWhole)
Set LastRange = ColRange.End(xlDown)
Set DataRange = ValidSheet.Range("C22:G81") ' This is what I am trying to transfer, only numeric values without column names
strCon = "Provider=Microsoft.ACE.OLEDB.12.0; Data Source=" & strName _
& ";Extended Properties=""Excel 12.0;"
conn.Open strCon
strSQL = "SELECT * FROM [" & ValidSheet.Name & "$" & Replace(DataRange, "$", "") & "];"
rs.Open strSQL, dbclass, adOpenStatic, adLockReadOnly
arrData = rs.GetRows
rs.Close
conn.Close
Set rs = Nothing
Set conn= Nothing
Set ValidSheet = Nothing
End Sub
After getting the same error for the "connection string", I changed the strategy, and I used dbclass procedure to open a connection. So the new code is like below. (I found this coding from a guy, but he is on vacation now, so I can't ask him).
It gets connection (dbclass) properties automatically, which are saved in the main ThisWorkbook. This code doesn't give any error at all, but it doesn't copy the column from the Excel to the database. I tried different versions for the sql-query, like SQL = .... VALUES('result') or SQL = .... VALUES(result), but there is no result again, without error.
Private Sub Testing_Click()
Dim FindColValues() As Double
Dim ValidBook As Workbook
Dim ValidSheet As Worksheet
Dim DataRange As Range
Dim dataa As Range
Application.ScreenUpdating = False
TheSheet = "Sheet2"
Set ValidSheet = Worksheets(TheSheet)
Set DataRange = ValidSheet.Range("C21:C81")
' Below creating an array "result(it)" from the seleced range.
For Each dataa In DataRange
ReDim Preserve result(it)
result(it) = dataa.Value
it = it + 1
Next
' Below is just an alternative array for "in case"
arrData = ValidSheet.Range("C22:G81").Value
SQL = "INSERT INTO Table_test (Column1) VALUES ('result()');"
dbclass.ExecuteSQL SQL
End Sub
Below is dbclass connection properties which is read automatically by the other function:
Private Sub Workbook_Open()
Dim connOk As Boolean
Dim rs As New ADODB.Recordset
Dim MyWorkBook As Workbook
Dim CurSheet As Worksheet
Set dbclass = New clsDB
dbclass.Database = "Tables"
dbclass.ConnectionType = SqlServer
dbclass.DataSource = "E45c7642"
dbclass.UserId = Application.UserName
connOk = dbclass.OpenConnection(False, True)
If connOk = False Then
MsgBox "Cannot connect"
Else
MsgBox "The server is connected"
End If
End Sub
Finally I found the problem for my second code. As I wrote before, in my alternative code (second code), I didn't get any error at all in VBA, but it didn't save my table into the server.
Now I know the reason, because my real value was in "comma" format, but the server saved the value in "dot" format. So I added Str(value) to convert the "comma" value to the "dot" value, and it works now:
....
SQL = "INSERT INTO Table_test (Column1) VALUES (" & Str(result(1)) & ")"
dbclass.ExecuteSQL SQL
End Sub

Resources