Unique items from Access to Excel - sql

I have this code:
Dim cn As Object
Dim rs As Object
Dim strSql As String
Dim strConnection As String
Set cn = CreateObject("ADODB.Connection")
strConnection = "Provider=Microsoft.ACE.OLEDB.12.0;" & _
"Data Source=\\lm\central\Permkt\Svc02-User-Disk\Sales\Sales-Private\Consumer Marketing\Marketing Analytics\Testing Framework\2014Data.accdb"
strSql = "SELECT distinct project1 FROM 2014Data"
cn.Open strConnection
Set rs = cn.Execute(strSql)
rw = 1
For Each myfield In rs.Fields
Cells(rw, 7) = myfield
rw = rw + 1
Next myfield
rs.Close
Set rs = Nothing
cn.Close
Set cn = Nothing
End Sub
And now i am getting the first project1 in the cell as a value, but I should have two unique project1's . How would I get to the second?
Thanks so much for the first comments, the SQL is at least now executing but not sending back both project1 items

You have Dim'ed rs,but never Set rs

I did some stuff on running SQL within VBA:
Public Sub GetCn(ByRef dbcon As ADODB.Connection, ByRef dbrs As ADODB.Recordset, _
sqlstr As String, servername As String, dbname As String)
Set dbcon = New ADODB.Connection
dbcon.CursorLocation = adUseClient
dbcon.Open "Provider=SQLNCLI;Server=" & servername & ";Database=" & dbname & ";Trusted_Connection=yes;"
'"PROVIDER=Microsoft.Jet.OLEDB.4.0;Data Source=" & dbfile & ";", _
'usernm , pword
Set dbrs = New ADODB.Recordset
'Debug.Print sqlstr
dbcon.CommandTimeout = 200
Debug.Print sqlstr
dbrs.Open sqlstr, dbcon
End Sub
Public Sub RunSQL(sql As String)
Dim adoconn As ADODB.Connection
Dim adors As ADODB.Recordset
Dim dbname As String
Dim servername As String
servername = Worksheets("DBSettings").Range("B1").value
dbname = Worksheets("DBSettings").Range("B2").value
Call GetCn(adoconn, adors, sql, servername, dbname)
End Sub
Sub OpenDatabaseConnection(ByVal servername As String, ByVal databasename As String, sql As String, myRange As Range)
Dim connectionstring As String
'Dim SQL As String
Dim adoconn As ADODB.Connection
Dim adors As ADODB.Recordset
Dim dbname As String
If servername = "" Then servername = Worksheets("DBSettings").Range("B1").value
If databasename = "" Then databasename = Worksheets("DBSettings").Range("B2").value
Call GetCn(adoconn, adors, sql, servername, databasename)
NrOfRows = adors.RecordCount
myRange.CopyFromRecordset adors
'LOOP DOOR KOLOMMEN en OUTPUT Columns 1 rij hoger
Dim fieldname As String, counter As Integer
counter = 1
Dim StartRange As Range
Set StartRange = myRange.Worksheet.Range("A1")
For Each Field In adors.Fields
StartRange.Cells(1, counter).value = Field.name
counter = counter + 1
Next
adors.Close
adoconn.Close
Set adors = Nothing
Set adoconn = Nothing
Dim sn() As Variant, wsnn As String
wsnn = myRange.Worksheet.name
sn = Array(wsnn)
'Call NameDeletionV3(sn)
'Call CreateName("RawData", CreateRange(Worksheets(sn), CInt(1), GetLastColumn(Worksheets(sn)), CInt(1), GetLastRow(Worksheets(sn))), Worksheets(sn))
'myRange = Worksheets("Q1-old").Range("B1")
End Sub
and then for usage:
Sub SQL_Execute()
Dim sql As String, FromDate As Date, EndDate As Date
sql = "SELECT * FROM TABLENAME"
Dim myRange As Range
Dim sheetname As String: sheetname = "Sheet1"
Worksheets(sheetname).Cells.Delete
Set myRange = Worksheets(sheetname).Range("A2")
Debug.Print sql
Call OpenDatabaseConnection("", "", sql, myRange)
End Sub

Related

Send a recordset to another macro

I have a macro for updating an SQL table in an Excel Add-in.
In order to use the same macro from multiple files I want to be able to create the recordset outside of the connection and then send it as a parameter to the update macro. Is this possible?
I have tried looking at the solutions found for in memory recordsets but these seemes to focus more on creating the columns rather than column-value pairs.
Sub test()
Dim ws As Worksheet
Dim serverName As String
Dim dataBase As String
Dim forecastDate As Date
Dim projectNum As Long
Dim SqlStr As String
Dim rst As New ADODB.Recordset
Set ws = ActiveSheet
serverName = "Servername"
dataBase = "database"
forecastDate = ws.Cells(2, "B").Value
projectNum = ws.Cells(3, "B").Value
SqlStr = "SELECT * From forecast WHERE forecastDate='" & forecastDate & "' AND projectNum = '" & projectNum & "';"
Set rst = New ADODB.Recordset
rst!forecastDate = forecastDate
rst!projectNum = projectNum
rst!Data = Cells(4, "B").Value
Application.Run "updateMacro", serverName, dataBase, SqlStr, rst
rst.Close
End Sub
'Part of the updateMacro:
Set conn = New ADODB.Connection
cs = "DRIVER=SQL Server;DATABASE=" & dataBase & ";SERVER=" & serverName & ";Trusted_connection=yes;"
conn.Open cs
'Set rst = New ADODB.Recordset
rst.Open SqlStr, conn, adOpenDynamic, adLockOptimistic 'adLockPessimistic
If rst.EOF Then
rst.AddNew
End If
'get the recordset from caller macro and update
rst.Update
rst.Close
Set rst = Nothing
conn.Close
Set conn = Nothing
I would like to create the recordset outside of the updateMacro and use it in that macro or create some sort of column-value pairs that could be copied to the recordset in the updateMacro.
You can declare the recordset as global or also pass the recordset between functions/subs. Please see code below for an example:
Option Explicit
'Global Recordset to be sued by other functions
Private rsMain As ADODB.Recordset
Public Function ImportData(ByVal fyYear As String) As Long
Dim sConnString As String, sqlYears As String
Dim conn As ADODB.Connection
Dim tCount As Long
sConnString = "Provider=SQLOLEDB;Data Source=server2;" & "Initial Catalog=FPSA;" & "Integrated Security=SSPI;"
sqlYears = "select ltrim(rtrim(FinYearDesc)) as FinYearDesc, Month, AccountType, ltrim(rtrim(AccountName))as AccountName, " & _
"ActualValue, BudgetValue from [GL_AccountMovements] where FinYearDesc >= '" & fyYear & "'"
Set conn = New ADODB.Connection
Set rsMain = New ADODB.Recordset
rsMain.CursorLocation = adUseClient
rsMain.Open sqlYears, conn, _
ADODB.adOpenForwardOnly, _
ADODB.adLockBatchOptimistic
Set rsMain.ActiveConnection = Nothing
conn.Close
If Not rsMain.EOF Then
tCount = rsMain.RecordCount
End If
ImportData = tCount
End Function
'An example of using Global Recordset
Function GetAccountsByYearMonth(ByVal strYTDLastYear as String) As Double
Dim lastYearYTDAct As Double
rsMain.Filter = strYTDLastYear
Do While Not rsMain.EOF
lastYearYTDAct = lastYearYTDAct + rsMain.Fields("ActualValue")
rsMain.MoveNext
Loop
GetAccountsByYearMonth = lastYearYTDAct
End Function
Thanks

Export Excel to SQL VBA: Could not find stored procedure

I am trying to automate the export of my data from excel to SQL via VBA. I don't have much knowledge in VBA and Excel tells me the following error (see below). Where should I create that procedure? In SQL? How should that one be designed?
(the xxx in the following code, I put them)
Sub testexportsql()
Dim cn As ADODB.connection
Dim ServerName As String
Dim DatabaseName As String
Dim TableName As String
Dim UserID As String
Dim Password As String
Dim rs As ADODB.recordset
Dim RowCounter As Long
Dim NoOfFields As Integer
Dim StartRow As Long
Dim EndRow As Long
Dim ColCounter As Integer
Set rs = New ADODB.recordset
ServerName = "xxx" ' Enter your server name here
DatabaseName = "DATAWAREHOUSE" ' Enter your database name here
TableName = "dbo.AlbertaFire_import" ' Enter your Table name here
UserID = "sa" ' Enter your user ID here
' (Leave ID and Password blank if using windows Authentification")
Password = "xxx" ' Enter your password here
NoOfFields = 331 ' Enter number of fields to update (eg. columns in your worksheet)
StartRow = 2 ' Enter row in sheet to start reading records
EndRow = 200 ' Enter row of last record in sheet
' CHANGES
Dim shtSheetToWork As Worksheet
Set shtSheetToWork = ActiveWorkbook.Worksheets("Sheet2")
'********
Set cn = New ADODB.connection
cn.Open "Driver={SQL Server};Server=" & ServerName & ";Database=" & DatabaseName & _
";Uid=" & UserID & ";Pwd=" & Password & ";"
rs.Open TableName, cn, adOpenKeyset, adLockOptimistic
'EndRow = shtSheetToWork.Cells(Rows.Count, 1).End(xlUp).Row
For RowCounter = StartRow To EndRow
rs.AddNew
For ColCounter = 1 To NoOfFields
'On Error Resume Next
rs(ColCounter - 1) = shtSheetToWork.Cells(RowCounter, ColCounter)
Next ColCounter
Debug.Print RowCounter
Next RowCounter
rs.UpdateBatch
' Tidy up
rs.Close
Set rs = Nothing
cn.Close
Set cn = Nothing
End Sub
rs.Open TableName, cn, adOpenKeyset, adLockOptimistic
Run-time error '-2147217900 (80040e14)':
[Microsoft][OBDC SQL Sever Driver][SQL Server] Could not find stored procedure 'dbo.AlbertaFire_import'
I tried to reproduced the error. The code was working fine as long the table on the SQL server exist. If the table doesn't exist I get the same error code but as description "automation-error".
I guess the table doesn't exist on your server. Create the table AlbertaFire_import and try. If it works you maybe need to delete old records before you import new data. You can do this with "Execute" a bit SQL:
cn.Open "Driver={SQL Server};Server=" & ServerName & ";Database=" & DatabaseName & ";Uid=" & UserID & ";Pwd=" & Password & ";"
cn.Execute "delete from " + TableName
rs.Open TableName, cn, adOpenKeyset, adLockOptimistic
I hope it helps...
There are several ways to do something like this.
Sub sql_login()
'******************************************************
' Connection info to log into SQL Server
'******************************************************
Dim ServerName As String
Dim dbname As String
Dim uname As String
Dim pword As String
ServerName = "your_server_name"
dbname = "Northwind"
'uname = "**************"
'pword = "**************"
'******************************************************
' Calls the SQLConnect to query batch information
'******************************************************
Call SQLConnect(ServerName, dbname) ', uname, pword)
End Sub
Sub SQLConnect(ServerName As String, dbname As String) ', uname As String, pword As String)
'******************************************************
' Logs into SQL Server to get actual batch information
'******************************************************
Dim Cn As adodb.Connection
Set Cn = New adodb.Connection
'On Error GoTo ErrHand
With Cn
.ConnectionString = "your_server_name;Database=Northwind;Trusted_Connection=True;"
End With
'******************************************************
' Calls the the SQL Query
'******************************************************
Call sql_query(Cn)
End Sub
Sub sql_query(Cn As adodb.Connection)
'******************************************************
' Performs SQL Query
'******************************************************
Dim RS As adodb.Recordset
Dim sqlString As String
Set RS = New adodb.Recordset
sqlString = "Select * From Northwind.dbo.TBL"
RS.Open sqlString, Cn, adOpenStatic, adLockOptimistic
Cn.Execute (sqlString)
Dim fld As adodb.Field
'******************************************************
' Create Field Headers for Query Results
'******************************************************
i = 0
With Worksheets("sheet1").Range("A1")
For Each fld In RS.Fields
.Offset(0, i).Value = fld.Name
i = i + 1
Next fld
End With
'******************************************************
' Copy Query Results into Excel
'******************************************************
Worksheets("sheet1").Range("A1").CopyFromRecordset RS
End Sub
Or . . .
Sub InsertInto()
'Declare some variables
Dim cnn As adodb.Connection
Dim cmd As adodb.Command
Dim strSQL As String
'Create a new Connection object
Set cnn = New adodb.Connection
'Set the connection string
cnn.ConnectionString = "Provider=SQLOLEDB.1;Integrated Security=SSPI;Persist Security Info=True;Initial Catalog=Northwind;Data Source=your_server_name"
'cnn.ConnectionString = "DRIVER=SQL Server;SERVER=your_server_name;DATABASE=Northwind;Trusted_Connection=Yes"
'Create a new Command object
Set cmd = New adodb.Command
'Open the Connection to the database
cnn.Open
'Associate the command with the connection
cmd.ActiveConnection = cnn
'Tell the Command we are giving it a bit of SQL to run, not a stored procedure
cmd.CommandType = adCmdText
'Create the SQL
strSQL = "UPDATE TBL SET JOIN_DT = '2013-01-22' WHERE EMPID = 2"
'Pass the SQL to the Command object
cmd.CommandText = strSQL
'Execute the bit of SQL to update the database
cmd.Execute
'Close the connection again
cnn.Close
'Remove the objects
Set cmd = Nothing
Set cnn = Nothing
End Sub
There are a few other things you can do as well, all related to the methodologies mentioned above.

Insert dd/mm/yyyy hh:mm:ss.000 in datetime in SQL Server

I have a timestamp in Excel in the format d/m/yyy hh:mm:ss.000 and I send it to a datetime datatype in SQL Server. But when I transfer the data it has transferred correctly but without the milliseconds every data has .000 on the end. In Excel I have the date with the correct milliseconds. Does someone have a solution?
You can get Text of Cell and format before insert to db:
Dim strValue as String
strValue=shtSheetToWork.Cells(RowCounter, 13).Text
format again strValue before insert to db rs(0), if it is datetime type.
Sub Sectie1Invert()
Dim Cn As ADODB.Connection
Dim ServerName As String
Dim DatabaseName As String
Dim TableName As String
Dim UserID As String
Dim Password As String
Dim rs As ADODB.Recordset
Dim rs1 As ADODB.Recordset
Dim RowCounter As Long
Dim NoOfFields As Integer
Dim StartRow As Long
Dim EndRow As Long
Dim ColCounter As Integer
Dim trigger As Boolean
Set rs = New ADODB.Recordset
ServerName = "NLDONL0113" ' Enter your server name here
DatabaseName = "Stroomwaarden" ' Enter your database name here
TableName1I = "Sectie1invert" ' Enter your Table name here
UserID = "" ' Enter your user ID here
Password = "" ' Enter your password here
NoOfFields = 1 ' Enter number of fields to update (eg. columns in your worksheet)
StartRow = 3 ' Enter row in sheet to start reading records
EndRow = 349 ' Enter row of last record in sheet
Dim shtSheetToWork As Worksheet
Set shtSheetToWork = ActiveWorkbook.Worksheets("Sheet1")
'********
Set Cn = New ADODB.Connection
Cn.Open "Driver={SQL Server};Server=" & ServerName & ";Database=" & DatabaseName & _
";Uid=" & UserID & ";Pwd=" & Password & ";"
rs.Open TableName1I, Cn, adOpenKeyset, adLockOptimistic
For RowCounter = StartRow To EndRow
rs.AddNew
'On Error Resume Next
rs(0) = shtSheetToWork.Cells(RowCounter, 13)
rs(3) = shtSheetToWork.Cells(RowCounter, 14)
Debug.Print RowCounter
Next RowCounter
rs.UpdateBatch
rs.Close
Set rs = Nothing
Cn.Close
Set Cn = Nothing
End Sub

Why does this VBA code for SQL queries on CSV files work intermittently?

A very simple query function that takes in a path for a source CSV file and a SQL statement as a string (I'm also transposing the data from the VBA function),
Public Function RunQuery(FilePath As String, SQLStatement As String)
Dim Conn As New ADODB.Connection
Dim RecSet As New ADODB.Recordset
With Conn
.Provider = "Microsoft.Jet.OLEDB.4.0"
.ConnectionString = "Data Source=" & FilePath & ";" & _
"Extended Properties=""text;HDR=Yes;FMT=Delimited;IMEX=1"""
End With
Conn.Open
RecSet.Open SQLStatement, Conn
RecSet.MoveFirst
RunQuery = RecSet.GetRows()
Conn.Close
Set RecSet = Nothing
Set Conn = Nothing
End Function
This code works intermittently against a CSV files, some data is retrieved correctly and some is not.
An example are these two CSV files - Abbreviated and Full. The following SQL query works perfectly on the Abbreviated file, but returns #VALUE on the Full file.
SELECT birthYear FROM [File]
It's definitely not a data limit/size issue as the Full file only contains 1800 rows. I'm completely befuddled and would appreciate any thoughts/pointers.
Incidentally if I wrap up the logic into a Sub rather than a UDF then it works perfectly without any errors,
Public Sub RunQuerySub()
Dim Conn As New ADODB.Connection
Dim RecSet As New ADODB.Recordset
Dim FilePath As String
FilePath = ActiveSheet.Range("Path")
With Conn
.Provider = "Microsoft.Jet.OLEDB.4.0"
.ConnectionString = "Data Source=" & FilePath & ";" & _
"Extended Properties=""text;HDR=Yes;FMT=Delimited;IMEX=1"""
End With
Dim SQLStatement As String
SQLStatement = ActiveSheet.Range("SQL")
Conn.Open
RecSet.Open SQLStatement, Conn
ActiveSheet.Cells(1, 8).CopyFromRecordset RecSet
Conn.Close
Set RecSet = Nothing
Set Conn = Nothing
End Sub
I am very confused, and would appreciate any pointers.
I adapted the technique for using a Sub and managed to get a Function which returns an array for both abbreviated and full files.
Highlight a range of 1892 cells in a column & use this array function
=RunQuery("C:\stackoverflow", "SELECT birthYear FROM [full.csv]")
This is the function. It replaces Null values in the resultset with zero.
Public Function RunQuery(FilePath As String, SQLStatement As String)
Dim Conn As New ADODB.Connection
Dim RecSet As New ADODB.Recordset
Dim rows As Variant
On Error GoTo ErrHandler
With Conn
.Provider = "Microsoft.Jet.OLEDB.4.0"
.ConnectionString = "Data Source=" & FilePath & ";" & _
"Extended Properties=""text;HDR=Yes;FMT=Delimited;IMEX=1"""
End With
Conn.Open
RecSet.Open SQLStatement, Conn
RecSet.MoveFirst
rows = RecSet.GetRows()
Conn.Close
Set RecSet = Nothing
Set Conn = Nothing
Dim nrows As Integer, i As Integer, valu As Integer
nrows = UBound(rows, 2) + 1
ReDim arr2(1 To nrows, 1 To 1) As Integer
For i = 1 To nrows
If IsNull(rows(0, i - 1)) Then
valu = 0
Else
valu = rows(0, i - 1)
End If
arr2(i, 1) = valu
Next
RunQuery = arr2
Exit Function
ErrHandler:
Debug.Print Err.Number, Err.Description
Resume Next
End Function
When I suggested running it from a Sub I didn't really mean as a Sub.
I meant do something like below, where your function is unchanged and the only difference is you're running it from VBA instead of as a UDF.
When running from VBA you will be able to see any errors instead of just getting #VALUE in a worksheet cell.
Sub Tester()
Dim arr
arr = RunQuery("yourPath", "yourSQL")
End sub
Public Function RunQuery(FilePath As String, SQLStatement As String)
Dim Conn As New ADODB.Connection
Dim RecSet As New ADODB.Recordset
With Conn
.Provider = "Microsoft.Jet.OLEDB.4.0"
.ConnectionString = "Data Source=" & FilePath & ";" & _
"Extended Properties=""text;HDR=Yes;FMT=Delimited;IMEX=1"""
End With
Conn.Open
RecSet.Open SQLStatement, Conn
RecSet.MoveFirst
RunQuery = RecSet.GetRows()
Conn.Close
Set RecSet = Nothing
Set Conn = Nothing
End Function
This button click event handler produced the results by calling RunQuerySub. Three input parameters are defined in B2, B3. B4.
Sub Button1_Click()
Dim FilePath As String, SQLStatement As String, TargetColumn As String
FilePath = Sheet1.Range("B2").Text
SQLStatement = Sheet1.Range("B3").Text
TargetColumn = Sheet1.Range("B4").Text
Call RunQuerySub(FilePath, SQLStatement, TargetColumn)
End Sub
The subroutine is much as you had it, but there were some Null values which caused issues with assigning to a Range object, so I replaced these with zeroes. The resultset from RecSet.GetRows() is a 2D variant array with the birthYear values in the 2nd dimension. I assigned these to an array with the values in the first dimension so it would populate the range by row.
Functions don't appear to allow you to assign values to ranges - at any rate I could not find a way of doing it.
Public Sub RunQuerySub(FilePath As String, SQLStatement As String, TargetColumn As String)
Dim Conn As New ADODB.Connection
Dim RecSet As New ADODB.Recordset
Dim rows As Variant
On Error GoTo ErrHandler
With Conn
.Provider = "Microsoft.Jet.OLEDB.4.0"
.ConnectionString = "Data Source=" & FilePath & ";" & _
"Extended Properties=""text;HDR=Yes;FMT=Delimited;IMEX=1"""
End With
Conn.Open
RecSet.Open SQLStatement, Conn
RecSet.MoveFirst
rows = RecSet.GetRows()
Conn.Close
Set RecSet = Nothing
Set Conn = Nothing
Dim dest As Range
Dim nrows As Integer, i As Integer, valu As Integer
nrows = UBound(rows, 2) + 1
ReDim arr2(1 To nrows, 1 To 1) As Integer
For i = 1 To nrows
If IsNull(rows(0, i - 1)) Then
valu = 0
Else
valu = rows(0, i - 1)
End If
arr2(i, 1) = valu
Next
Dim rangeDefn As String
rangeDefn = TargetColumn & "1:" & TargetColumn & CStr(nrows)
With ThisWorkbook.Sheets("Sheet1")
Set dest = .Range(rangeDefn)
End With
dest = arr2
Exit Sub
ErrHandler:
Debug.Print Err.Number, Err.Description
Resume Next
End Sub

SQL Select query in Excel VBA

I have email addresses on Sheet 1 cell A1:A735. I need to use those cell data in a where clause. Currently it is hardcoded. I am fetching data from Sql and want to paste data in Active range A1.
I cannot figure out how to loop through.
Sub GetDataFromADO()
Dim objMyConn As ADODB.Connection
Dim objMyCmd As ADODB.Command
Dim objMyRecordset As ADODB.Recordset
Dim Email2 As Range
Dim Worksheet1 As Worksheet
Set objMyConn = New ADODB.Connection
Set objMyCmd = New ADODB.Command
Set objMyRecordset = New ADODB.Recordset
objMyConn.ConnectionString = "some connection string ;"
objMyConn.Open
Set objMyCmd.ActiveConnection = objMyConn
objMyCmd.CommandText = "SELECT * FROM [abc].[dbo].[excusers] where email = 'asif#gmail.com'"
objMyCmd.CommandType = adCmdText
Set objMyRecordset.Source = objMyCmd
objMyRecordset.Open
ActiveSheet.Range("a1").CopyFromRecordset objMyRecordset
End Sub
You can loop through the cells like so:
With Sheet1
For i = 1 To 735
sText = "SELECT * FROM [abc].[dbo].[excusers] where email = '" _
& Replace(.Cells(1, i), "'", "''") & "'"
objMyCmd.CommandText = sText
Next
End With
This should give you a way to call a subroutine the connects for you. You would pass in the parameters required.
Sub adocnnRoutine_SP(ByVal ReturnVal As String, ByVal cnnstr As String, ByVal CallVal As Range, Optional CallHDR As Range)
'ReturnValue is the string to send to SQL Such as "Select * from TableName where email = 'username#email.com'"
'CallVal places the results in that one cell as a starting point Such as Sheet2.Range("A2")
'CallHDR is optional header placement point Such as Sheet2.Range("A1")
Dim cn As ADODB.Connection, rs As ADODB.RECORDSET
Set cn = New ADODB.Connection
Set rs = New ADODB.RECORDSET
On Error GoTo CleanUp
cn.Open cnnstr
rs.Open ReturnVal, cnnstr
If Not CallHDR Is Nothing Then
With CallHDR
For Each field In rs.Fields
.Offset(0, Offset).Value = field.Name
Offset = Offset + 1
Next field
End With
End If
CallVal.CopyFromRecordset rs
CleanUp:
Debug.Print Err.Description
cn.Close
Set rs = Nothing
Set cn = Nothing
End Sub
And Then you can loop through your sheet1 emails as required.