I'm trying to retrieve and update rows in Excel/SQL and found this script which works but I tried to make it update as soon as I enter the "existencia" value instead of hitting run on the update macro, found the Worksheet change function but I'm not sure how to add it to this macro.
Private Sub Worksheet_Change(ByVal Target As Range)
Dim con As New ADODB.Connection
Dim cmd As New ADODB.Command
Dim rst As New ADODB.Recordset
Dim i As Long
Dim vDB As Variant
Dim Ws As Worksheet
con.ConnectionString = "Provider=SQLOLEDB; data source=LAPTOP\SQLEXPRESS;initial catalog=Inventario;Integrated Security=SSPI;"
con.Open
Set cmd.ActiveConnection = con
Set Ws = ActiveSheet
'The assumption is that the data on the Excel sheet is listed from a1 Cell, including fields.
vDB = Ws.Range("a1").CurrentRegion
For i = 2 To UBound(vDB, 1)
cmd.CommandText = "UPDATE Productos SET Existencia='" & vDB(i, 4) & "' WHERE id_cod=" & vDB(i, 1) & " "
cmd.Execute
Next i
con.Close
Set con = Nothing
End Sub
I have used the Worksheet_Change event to do a lot of things, but I have never ever used Worksheet_Change to update data in a SQL Server table. This doesn't really make sense, if you think about it. You can make all kinds of changes in an Excel file, all day long. People do this all the time. When you are totally done with your updates, changes, or whatever, you can push the data to a table in a database. You should not do this intermittently. Otherwise, you will have to do all kinds of cleanup in your database table(s). You don't want to create this kind of overhead for yourself.
Related
I created many spreadsheets which use stored procedures. I have a custom VBA code which works fine when I try to get data from SQL. However, today I wanted to execute parametrized stored procedure which inserts and updates data on a database table. When I run macro no errors show up, however there's no insert/update action on database. I have no idea why. I established SQL connection in my workbook (myConn) as I do everytime I need to connect with SQL so it's correct for sure. This is my standard VBA code:
Sub SaveData()
Dim myValue As Double
myValue = Sheets("XYZ").Range("valueToSave").Value
With ActiveWorkbook.Connections("myConn").OLEDBConnection
.CommandText = Array( _
"EXEC DB.[dbo].[myProc] '" & myValue & "'")
End With
ActiveWorkbook.Connections("myConn").Refresh
End Sub
I need to insert data into column of decimal(6,4) type (in SQL table). myProc does it perfectly when I run it manually via SSMS but not here using VBA code. valueToSave is an Excel range which stores one decimal value (for example: 23,56, 11,21 etc.). When I run macro nothing happens. When I run macro and go to 'Connection Properties' > 'Definitions' > 'Command Text' then I can see there's a procedure with parameter (EXEC DB.[dbo].[myProc] '11,23'). So my acode above seems working but not executing stored procedure.
Has it something to do with data type? Honestly, I tried with other VBA types: String, Variant, Integer but it's not working. I also changed data type of that column in SQL table (to varchar, int etc.) but it also doesn't work. The most interesting thing is that the code above works fine when I withdraw data from db, it doesn't work when need to insert/update data.
PS. I guess I added all required refrences:
Using ADODB
Option Explicit
Sub SaveData()
Const PROC_NAME = "DB.dbo.myproc"
Dim wb As Workbook
Dim ado As ADODB.Connection, cmd As ADODB.Command
Dim sCon As String, v As Single
Set wb = ThisWorkbook
v = wb.Sheets("XYZ").Range("valueToSave").Value
' get connection string
sCon = wb.Connections("myConn").OLEDBConnection.Connection
sCon = Replace(sCon, "OLEDB;", "")
' open connection
Set ado = New ADODB.Connection
ado.Open sCon
Set cmd = New ADODB.Command
With cmd
.ActiveConnection = ado
.CommandType = adCmdStoredProc
.CommandText = PROC_NAME
.Parameters.Append .CreateParameter("P1", adDecimal, adParamInput)
With .Parameters(0)
.NumericScale = 2
.Precision = 18
.Value = v
End With
.Execute
End With
ado.Close
MsgBox PROC_NAME & " " & v, vbInformation, "Done"
End Sub
I have a problem here that I will probably need to solve in several iterations, this being the first. maybe you have an idea:
I need to speed up regular updates from 4 views in an SQL database on Azure to an Excel Worksheet. There are probably a lot of moving parts here, among them: The code, my connection to the internet, the service that provides me with a static IP address, the fact that I reference views and not tables, and the (rather basic) service level I booked in Azure.
What my code does is simple: it opens a connection, updates 4 worksheets from 4 views (3 of them with 5 - 10 rowns of data, 1 with about 2.000), and closes the connection again.
This takes up to 30 seconds, which seems an awfully long time.
I would like to make sure it is not my code that slows this down. My first attemt was to use Powerquery for the connections, call the connection, then set autofilters with VBA to have only the rows visible that I needed. That took ages.
My second try is: not use Powerquery, get rid of the autofilters, and use VBA instead to pass a SELECT to the SQL Server, so the work of selecting the data is done there, and send over only what I need. But it seems the difference that this makes is not really significant.
The code I use is:
Dim WS1 As Worksheet
Set WS1 = Worksheets("Eingabe")
Dim SelectedCustomer As Range
Set SelectedCustomer = WS1.Range("C39")
Dim cn As ADODB.Connection
Dim SQLStr1 As String
Dim rs1 As ADODB.Recordset
Set rs1 = New ADODB.Recordset
Dim SQLStr2 As String
Dim rs2 As ADODB.Recordset
Set rs2 = New ADODB.Recordset
Dim SQLStr3 As String
Dim rs3 As ADODB.Recordset
Set rs3 = New ADODB.Recordset
Dim SQLStr4 As String
Dim rs4 As ADODB.Recordset
Set rs4 = New ADODB.Recordset
'Open a connection to SQL Server - using the connection string provided by Azure
Set cn = New ADODB.Connection
cn.Open "Driver={ODBC Driver 17 for SQL Server};Server=tcp:server-
displacethis.database.windows.net,1433;Database=my-database;Uid=my-userID;Pwd=my-
password;Encrypt=yes;TrustServerCertificate=no;Connection Timeout=30;"
'First table
SQLStr1 = "SELECT CustomerID,CustomerUniqueName,CustomerFirstName,CustomerLastName,TransactionDate,EmissionenNeukauf,EmissionenSecondhand,600 As DurchschnittDE, 300 As Paris2030 FROM vAccumulatedEmissionsByCustomer WHERE CustomerUniqueName = '" & SelectedCustomer & " ' "
rs1.Open SQLStr1, cn, adOpenStatic
'Dump to spreadsheet
With Worksheets("vAccumulatedEmissions").Range("A2:I100")
.ClearContents
.CopyFromRecordset rs1
'Do some column formatting
Worksheets("vAccumulatedEmissions").Range("F:F").NumberFormat = "#,##0.00"
'Tidy up
rs1.Close
Set rs1 = Nothing
End With
'Then the next table follows. The connection os closed after the last table:
cn.Close
Set cn = Nothing
My questions is, very simply, is this highly ineffective code, and could that be the reason for the long time it runs, or should I look somewhere else to speed up the whole process?
Thank you in advance,
Ulrich
I am trying to pass a parameter from Excel to a SQL Server Stored Procedure. The parameter is a date, the aim is to pass the date into the query then the query return results for said date. I have included pictures of how I have currently connected to the database.
Picture 1 (Irrelevant really but thought I'd include it anyway): https://imgur.com/TgRKOkc
Picture 2: https://imgur.com/FkH34qQ - Here I am currently hardcoding the parameter in the .CommandText area to check whether the functionality is working OK if the parameter were to be passed correctly. The data returned is correct with the hard coded value. This is where I am hoping to replace the '2018-08-19' with a dynamic parameter entered into cell A14 of the spreadsheet by the client.
Picture 3: https://imgur.com/3LGTP3I - This is where I feel like I am messing up, I am brand new to VBA so I am unaware how to declare the value entered in a particular cell (A14 in this case) as the parameter to pass to the stored procedure on refresh of the excel document. Worth noting I am aware that I am point to "PreDealingFormA" in the VBA code and the connection is "PreDealingFormA1" this is just an anomaly in the screen shots, I have since changed this and it hasn't solved the problem. I am aware that the code pictured in screenshot three is on the command of a button being clicked, I previously thought this was the route to go down, however due to requirements a button cannot be implemented. The aim is to instead pass the parameter entered into cell A14 and execute the stored procedure on refresh of the excel document.
Any help is appreciated on this as I am brand new to VBA so as basic as this may seem, it's hard for me to get my head around at the moment.
You can do it like this.
Sub RunSProc()
Dim cn As ADODB.Connection
Dim cmd As ADODB.Command
Dim rs As ADODB.Recordset
Dim strConn As String
Set cn = New ADODB.Connection
strConn = "Provider=SQLOLEDB;"
strConn = strConn & "Data Source=Server_Name;"
strConn = strConn & "Initial Catalog=DB_Name;"
strConn = strConn & "Integrated Security=SSPI;"
cn.Open strConn
Set cmd = New ADODB.Command
cmd.ActiveConnection = cn
cmd.CommandText = "MyOrders"
cmd.CommandType = adCmdStoredProc
cmd.Parameters.Refresh
cmd.Parameters(1).Value = ActiveSheet.Range("E1").Text
cmd.Parameters(2).Value = ActiveSheet.Range("E2").Text
Set rs = cmd.Execute()
If Not rs.EOF Then
Worksheets("sheet2").Range("A5:D500").CopyFromRecordset rs
rs.Close
End If
End Sub
In this case, the setup looks like this.
The process is run in the environment of Excel VBA 2010 and MS SQL Server 2008.
Assume that there is a simple one column data with 1500 rows in an Excel-sheet and we want to export it to the database with SQL-queries in VBA code (SQL procedure in VBA exports maximum 1000 rows at once in default mode).
There is one limitation in this problem: the export procedure must be with dbclass-connection instead of ADODB connection. (The code-owner is not me. The code-owner is using dbclass for a quite big VBA code, so probably he wouldn't accept to change the whole code).
I found an option like lngRecsAff for ADODB.Connection which is used like:
Sub test()
Dim cn As ADODB.Connection
Dim strSQL As String
Dim lngRecsAff As Long
Set cn = New ADODB.Connection
cn.Open "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\test.xls; Extended Properties=Excel 8.0"
strSQL = "Insert INTO [odbc;Driver={SQL Server};Server=SQL09;Database=Tom;UID=userID;PWD=password].tbl_test1 Select * FROM [Sheet1$]"
cn.Execute strSQL, lngRecsAff
cn.Close
Set cn = Nothing
End Sub
I tried to implement that lngRecsAff in my dbclass execution like:
Sub test()
Dim connOk As Boolean
Dim rs As New ADODB.Recordset
Set dbclass = New clsDB
Dim Value1() As Variant
Dim lngRecsAff As Long
Dim strSQL as String
Dim mstrErr as Boolean
dbclass.Database = "database_name"
dbclass.ConnectionType = SqlServer
dbclass.DataSource = "server_name"
dbclass.UserID = Application.UserName
connOk = dbclass.OpenConnection(False, True)
If connOk = False Then
MsgBox "Unsuccessful connection!"
Else
MsgBox "Successful connection"
End If
strSQL = "INSERT INTO [dbo].[table1](Column1) Values('" & Value1 & "')"
mstrErr = dbclass.ExecuteSQL(strSQL, lngRecsAff) ' The result mstrErr is a Boolean
' Some closing options here
End Sub
I got en error like lngRecsAff is not suitable for my ExecuteSQL procedure. Normally my execution mstrErr = dbclass.ExecuteSQL(strSQL) works without any problem.
Maybe I can do the SQL-procedure with a for-loop, then I can send the data in small pieces. But I want to find a more efficient, "nicer" solution which sends the whole array at once.
So is there any special option for dbclass which can send more than 1000 rows from Excel to the database?
You can query the Excel-Sheet directly using liked server.
In your Management Studio click to "Server Objects" and then right click onto "linked server". There you'll be able to add a new linked server.
If you need further help you can find tuorials easily. One is here:
https://www.mssqltips.com/sqlservertip/2018/using-a-sql-server-linked-server-to-query-excel-files/
So here's my dilemma I'm attempting to create a macro that runs a query out of sql that located in cell (Sheets("SQL").Range("G1")), and paste the data from that query into Sheets("Data").Range("B1"). I came up with the code below but I keep getting a compile error: User-defined type not defined. Please any insight on what I'm doing wrong will be appreciated.
Sub ConnectSqlServer()
Dim conn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim sConnString As String
Dim StrSQL As Variant
Application.ScreenUpdating = False
Application.Cursor = xlWait
Set cnPubs = New ADODB.Connection
Set rsPubs = New ADODB.Recordset
StrSQL = " SET NOCOUNT ON "
' Create the connection string.
sConnString = "Provider=SQLOLEDB; DATA SOURCE=CFS-SERVERSQL;" & _
"Initial Catalog=dmtrans;" & _
"Integrated Security=SSPI;"
' Create the Connection and Recordset objects.
Set conn = New ADODB.Connection
Set rs = New ADODB.Recordset
' Open the connection and execute.
conn.Open sConnString
Set rs = conn.Execute(Sheets("SQL").Range("G1"))
' Check we have data.
If Not rs.EOF Then
' Transfer result.
Sheets("Data").Range("B4:S50000").ClearContents
Sheets(Data).Range("b4").CopyFromRecordset rs
' Close the recordset
rs.Close
Else
MsgBox "Error: No records returned.", vbCritical
End If
' Clean up
If CBool(conn.State And adStateOpen) Then conn.Close
Set conn = Nothing
Set rs = Nothing
End Sub
You probably did not set the reference for your ADODB objects.
You can either do this with early binding:
(Don't know the names exactly. I am using a german Excel)
Extras - References: Add the reference Microsoft ActiveX Data Objects vXXX with a click in the checkbox to your project.
Or you can do this via late binding. In this case you must declare your variable as Object and instantiate it via CreateObject. Please find a related question here: Excel VBA: Late binding reference