Inserting values into SQL Server table via VBA - vba

Suppose I have a table in Excel with two columns (Name, Rate) (say, this table's name tExcel). Table starts at cell (2,1), and Date is static (in cell (1,1))
I want to insert those values into SQL Server 2008 tRate table with following logic
insert tRate(ID, Rate, Date)
select
s.ObjectID, e.Rate, Date -- date comes from cell(1,1). DateType is smalldatetime
from
tExcel e, tSecurity s
where
e.Name = s.Number
I've created a connection
Sub disp_cust()
Dim adoCN As ADODB.Connection
Dim sConnString As String
Dim sSQL As String
Dim lRow As Long, lCol As Long
Set cn = New ADODB.Connection
sConnString = "Provider=sqloledb;Server=xxx;Database=xxx;User Id=xxx;Password=xxx"
Set adoCN = CreateObject("ADODB.Connection")
adoCN.Open sConnString
adoCN.Close
Set adoCN = Nothing
End Sub
Thanks for help.
EDIT to #jaimetotal answer
sql= "insert tRate(ID, Rate, Date) SELECT s.ObjectId ," & Replace(Row.Cells(2).Value, ",", ".") & ",'" & defaultDate & "' FROM tSecurity s where s.number = '" & row.Cells(1).Value & "'; "

For this sample, I assume that tExcel.Number is the first column and tExcel.Rate is the second. The idea here is to do a for each row in the table (or range) and create an insert/select statement.
Dim rng as Range
Dim defaultDate As string
Dim sql as string, bulkSql as string
Set rng = Range("A1:XX") -- Range of the table.
defaultDate = Format(Range("A2").Value, "yyyy/mm/dd")
bulkSql = ""
'generated sample: insert tRate(ID, Rate, Date) SELECT s.ObjectId, '0.15', '2015/08/24' FROM tSecurity s where s.Number = '007'
For Each row In rng.Rows
sql= "insert tRate(ID, Rate, Date) SELECT s.ObjectId " & "','" & row.Cells(2).Value & "','" & defaultDate & "' FROM tSecurity s where s.number = '" & row.Cells(1).Value & "'; "
bulkSql = bulkSql & sql
Next row
adoCn.Execute bulkSql, , adCmdText
Edit:
If you really mean a table, than you can use this sample based from here.
Dim lo As Excel.ListObject
Dim ws As Excel.Worksheet
Dim lr As Excel.ListRow
Set ws = ThisWorkbook.Worksheets(1)
Set lo = ws.ListObjects("tExcel")
'The other code from the previous sample. Use the following ForEach instead
For Each lr In lo.ListRows
Dim Rate as String
Dim Number as String
Rate = Intersect(lr.Range, lo.ListColumns("Rate").Range).Value
Number = Intersect(lr.Range, lo.ListColumns("Number").Range).Value
'Generate the query from these values instead
Next lr

Related

Loop through range and execute SQL statement when cell not empty

I think I need help with this one:
I look for a way to send a number of line items within a transaction to a database with as little coding as possible
A transaction can consist of 1 or several lineitems for a defined set of products (CategoryIDs). Each combination of CategoryIDs and LineItems is stored in a seperate row. Rows with 0 lineItems are to be ignored.
The products are listed in Worksheet-Column B, and the number of products purchased (the lineitem number) is stored in Column C
In addition, I have a CustomerID and a TransactionID, but these two values are "outside" of the loop because they are the same for the complete transaction, so they are not part of my question.
What I would like to accomplish is:
let the code loop through each row
ignore all rows with 0 line items
at a row with >0 line items, run an SQL insert containing the categoryID and the lineitems of that row
go to next row
I am not sure if this is at all possible in the way I try to do this:
Private Sub AbsendenNeu_Click()
Dim Cell As Range
'Variables for the connection to the SQL server
Dim con As ADODB.Connection
Dim rs As ADODB.Recordset
Dim conStr As String
Dim strSQL As String
'variables "outside the loop" that I am not too concerned with in this question
Dim CustomerUniqueName As String
Dim TransactionID As String
CustomerUniqueName = Worksheets("Eingabe").CustomerSelect.Value
TransactionID = "1-" & CustomerUniqueName & Now()
'These are the two variables "in the loop"
Dim CatID As Range
Dim LineItems As Range
'Open the database connection
Set con = New ADODB.Connection
con.Open "Driver={ODBC Driver 17 for SQL Server};Server=tcp:my-servername,1433;Database=my-database;Uid=my-User;Pwd=My-Password;Encrypt=yes;TrustServerCertificate=no;Connection Timeout=30;"
'this is the loop I try to get to work
With Worksheets("EmissionenNeu") 'This is the worksheet that contains the CategoryIDs and LineItems
Set CatID = Range("B" & Cell.Row)
Set LineItems = Range("C" & Cell.Row)
For Each Cell In Range("C2:C39")
If Cell.Value > 0 Then
strSQL = "INSERT INTO tblTransactions(ShopID,TransactionID,CategoryID,CustomerUniqueName,LineItems) VALUES(1,'" & TransactionID & "','" & CatID & "', '" & CustomerUniqueName & "','" & LineItems & "');"
con.Execute strSQL
End If
Next Cell
con.Close
Set con = Nothing
' End With
End Sub
I read this example in a different context so I am not sure if I can adapt this to my case. I get an error at " Set CatID = Range("B" & Cell.Row) " that says "Object Variable or With-Block Variable not defined" (in German), which sounds pretty basic, and I have the feeling more problems might wait ahead. Is the route I am trying at all possible?
Thanks in advance.
Try something like this:
Private Sub AbsendenNeu_Click()
Dim Cell As Range, rw As Range
Dim con As ADODB.Connection
Dim strSQL As String
Dim CatID, LineItems
Dim CustomerUniqueName As String
Dim TransactionID As String
CustomerUniqueName = Worksheets("Eingabe").CustomerSelect.Value
TransactionID = "1-" & CustomerUniqueName & Now()
'Open the database connection
Set con = New ADODB.Connection
con.Open "Driver={ODBC Driver 17 for SQL Server};Server=tcp:my-servername,1433;" & _
"Database=my-database;Uid=my-User;Pwd=My-Password;Encrypt=yes;" & _
"TrustServerCertificate=no;Connection Timeout=30;"
'loop each row in the input range
For Each rw In Worksheets("EmissionenNeu").Range("B2:C39").Rows
CatID = rw.Cells(1).Value
LineItems = rw.Cells(2).Value
If Len(LineItems) > 0 Then
strSQL = "INSERT INTO tblTransactions(ShopID,TransactionID,CategoryID," & _
"CustomerUniqueName,LineItems) VALUES" & _
"(1,'" & TransactionID & "','" & CatID & "', '" & _
CustomerUniqueName & "','" & LineItems & "')"
con.Execute strSQL
End If
Next rw
con.Close
Set con = Nothing
End Sub

Create comma delimited string array from a column in excel to use in VBA sql query

I need to get values from Sheet1 Column A (number of rows changes every day - can be more than 7,000) and Sheet2 Column B (also dynamic - changes every day), put those values from the columns into an array in STRING type (can be two arrays as long as I can use them in the query) and use the array in vba query WHERE ... IN ('array') to be run in MS-SQL server.
I've tried different ways to get the values into an array but have failed as many solutions offered need to use Array AS Variant when I need String type (to work in the query). One method that kind of worked was getting the values (comma separated) into one cell in another sheet and using that cell.value in the query. But that method is only good for total rows of 3000 or less. I've tried adding more - like cell2.value, cell3.value, but I would get errors (ex)if there were no values available for cell3.value. Please help.
Sub GetData()
Dim oConn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim fld As ADODB.Field
Dim mssql As String
Dim row As Integer
Dim Col As Integer
Dim WB As ThisWorkbook
'============THIS IS THE PART I NEED HELP WITH ======================
Dim strArray() As String 'TRYING TO GET VALUES FROM COLUMN AS ARRAY
Dim TotalRows As Long
Dim i As Long
TotalRows = Rows(Rows.count).End(xlUp).row
ReDim strArray(1 To TotalRows)
For i = 1 To TotalRows
strArray(i) = Cells(i, 1).Value & "','" 'TRYING TO INCLUDE COMMAS BETWEEN VALUES
Next
'===========================================================================
Set WB = ThisWorkbook
Application.ScreenUpdating = False
Set oConn = New ADODB.Connection 'NEED TO CONNECT TO SQL SERVER TO RUN QUERY
Set rs = New ADODB.Recordset
mssql = "SELECT Order.ID, Order.OrderDate, Order.Account" _
& " FROM dbo.tbl_Order" _
& " WHERE Order.ID IN ('" & strArray() & "0'")" '<=== THIS IS WHERE I NEED TO INSERT STRING ARRAY
oConn.ConnectionString = "driver={SQL Server};" & _
"server=SERVER01;authenticateduser = TRUE;database=DATABASE01"
oConn.ConnectionTimeout = 30
oConn.Open
rs.Open mssql, oConn
If rs.EOF Then
MsgBox "No matching records found."
rs.Close
oConn.Close
Exit Sub
End If
' ===clear data in columns in worksheet as new values are copied over old ones
' ===this part is working fine
Worksheets("Sheet3").Range("A:P").ClearContents
' START WRITING DATA TO SHEET3
row = 5
Col = 1
For Each fld In rs.Fields
Sheet3.Cells(row, Col).Value = fld.Name
Col = Col + 1
Next
rs.MoveFirst
row = row + 1
Do While Not rs.EOF
Col = 1
For Each fld In rs.Fields
Sheet1.Cells(row, Col).Value = fld
Col = Col + 1
Next
row = row + 1
rs.MoveNext
Loop
rs.Close
oConn.Close
End Sub
You don't want to build an array but rather a string with the values you need. I stripped down your code to illustrate how this works:
Sub GetData()
Dim values As String
Dim mssql As String
Dim TotalRows As Long
Dim i As Long
TotalRows = Rows(Rows.Count).End(xlUp).row
For i = 1 To TotalRows
values = values & "'" & Cells(i, 1).Value & "',"
Next
values = Mid(values, 1, Len(values) - 1)
mssql = "SELECT Order.ID, Order.OrderDate, Order.Account " & _
"FROM dbo.tbl_Order " & _
"WHERE Order.ID IN (" & values & ")"
MsgBox mssql
End Sub
Get Zero-Based From Column
If you can use an array of strings (don't know SQL), for column A you could e.g. use:
Dim strArray() As String: strArray = getZeroBasedFromColumn(Range("A2"))
If you have to use a string then you will have to do:
Dim strArray As String
strArray = "'" & Join(getZeroBasedFromColumn(Range("A2")), "','") & "'"
and use strArray without the parentheses.
Qualify the first cell range (A2) appropriately e.g. WB.Worksheets("Sheet1").Range("A2").
Both solutions use the following function.
The Code
Function getZeroBasedFromColumn( _
FirstCell As Range) _
As Variant
Const ProcName As String = "getZeroBasedFromColumn"
On Error GoTo clearError
If Not FirstCell Is Nothing Then
Dim rg As Range
Set rg = FirstCell.Resize(FirstCell.Worksheet.Rows.Count - FirstCell.Row + 1) _
.Find("*", , xlFormulas, , , xlPrevious)
If Not rg Is Nothing Then
Set rg = FirstCell.Resize(rg.Row - FirstCell.Row + 1)
Dim rCount As Long: rCount = rg.Rows.Count
Dim Data As Variant
If rCount = 1 Then
ReDim Data(1 To 1, 1 To 1): Data(1, 1) = rg.Value
Else
Data = rg.Value
End If
Dim arr() As String: ReDim arr(0 To rCount - 1)
Dim i As Long
For i = 1 To rCount
arr(i - 1) = CStr(Data(i, 1))
Next i
getZeroBasedFromColumn = arr
End If
End If
ProcExit:
Exit Function
clearError:
Debug.Print "'" & ProcName & "': Unexpected Error!" & vbLf _
& " " & "Run-time error '" & Err.Number & "':" & vbLf _
& " " & Err.Description
Resume ProcExit
End Function
Sub getZeroBasedFromColumnTEST()
Dim arr() As String: arr = getZeroBasedFromColumn(Range("A1"))
End Sub
An alternative (if you have the necessary database permissions) is to create a temporary table and use a JOIN in place of the WHERE IN().
Option Explicit
Sub GetData()
' get list of order numbers from Col A Sheet1 and COl B Sheet2
Dim wb As Workbook, wsOut As Worksheet
Dim rngA As Range, rngB As Range
Dim ar, ar1, ar2, iLastRow As Long, SQL As String
Dim i As Long, n As Long, id As String
Set wb = ThisWorkbook
Set wsOut = wb.Sheets("Sheet3")
' copy sheet1 Column A into array ar1
iLastRow = Sheet1.Cells(Rows.Count, "A").End(xlUp).row
Set rngA = Sheet1.Range("A1:A" & iLastRow)
If rngA.Rows.Count = 1 Then
ar1 = Array(0, rngA.Cells(1, 1).Value2)
Else
ar1 = Application.Transpose(rngA.Value2)
End If
'Debug.Print "A", LBound(ar1), UBound(ar1)
' copy sheet2 column B into array ar2
iLastRow = Sheet2.Cells(Rows.Count, "B").End(xlUp).row
Set rngB = Sheet2.Range("B1:B" & iLastRow)
If rngB.Rows.Count = 1 Then
ar2 = Array(0, rngB.Cells(1, 1).Value2)
Else
ar2 = Application.Transpose(rngB.Value2)
End If
'Debug.Print "B", LBound(ar2), UBound(ar2)
' connect to DB and create temporary table
Dim oConn As New ADODB.Connection
With oConn
.ConnectionString = "driver={SQL Server};" & _
"server=SERVER01;authenticateduser = TRUE;database=DATABASE01"
.ConnectionTimeout = 30
.Open
End With
oConn.Execute "CREATE TABLE #tmp (ID varchar(20) NOT NULL,PRIMARY KEY (ID ASC))"
' prepare insert query
SQL = "INSERT INTO #tmp (ID) VALUES (?)"
Dim cmd As New ADODB.Command
With cmd
.CommandType = adCmdText
.ActiveConnection = oConn
.CommandText = SQL
.Parameters.Append .CreateParameter("p1", adVarChar, adParamInput, 20)
End With
' insert array values into temp table
Dim t0 As Single: t0 = Timer
For Each ar In Array(ar1, ar2)
oConn.BeginTrans
For i = 1 To UBound(ar)
id = Trim(ar(i))
If Len(id) > 0 Then
cmd.Execute n, id
End If
Next
oConn.CommitTrans
'Debug.Print i - 1 & " Inserted"
Next
n = oConn.Execute("SELECT COUNT(*) FROM #tmp")(0)
'Debug.Print n & " records inserted into #tmp in " & Format(Timer - t0, "#.0 secs")
' select records using join as where filter
SQL = " SELECT Ord.ID, Ord.OrderDate, Ord.Account" _
& " FROM [tbl_Order] as Ord" _
& " JOIN #tmp ON Ord.ID = #tmp.ID"
' output result
Dim rs As New ADODB.Recordset
Set rs = oConn.Execute(SQL, n)
wsOut.Range("A:P").ClearContents
' header
Dim fld, cell As Range
Set cell = wsOut.Cells(5, 1)
For Each fld In rs.Fields
cell = fld.Name
Set cell = cell.Offset(0, 1)
Next
' data
wsOut.Cells(6, 1).CopyFromRecordset rs
oConn.Close
' end
n = wsOut.Cells(Rows.Count, 1).End(xlUp).row - 6
MsgBox n & " rows witten in " & Format(Timer - t0, "0.00 secs")
End Sub

Database Update in VBA

Sub uoload_data()
Dim s(40) As Integer
Dim Row As Integer
Dim i As Integer
i = 0
For Row = 7 To 39
s(i) = Sheets("Data").Cells(Row, 5).Value
i = i + 1
Next
Dim cn As Object
Dim rs As Object
Dim strSql As String
Dim strConnection As String
Dim AppPath As String
Set cn = CreateObject("ADODB.Connection")
AppPath = Application.ActiveWorkbook.Path
strConnection = "Provider=Microsoft.ACE.OLEDB.12.0;" & _
"Data Source=C:\Users\devi\Desktop\Ability.accdb;"
cn.Open strConnection
strSql = "INSERT INTO MyTable Values ('" & s(0) & " ',
'" & s(1) & " ','" & s(2) & " ','" & s(3) & " ' )"
Set rs = cn.Execute(strSql)
Set rs = Nothing
cn.Close
Set cn = Nothing
End Sub
I have a excel sheet of 40 field. I would like to update all field to access database. while insert record into database i am using insert into statement. In the mean time i need to write all fields of array into insert into statement. So please help me out to compact statement.
You can use Join() here
strSql = "INSERT INTO MyTable Values ('" & Join(s, "','") & "')"
The values in s() are integers, but you're wrapping the values in single-quotes, so are your DB columns text-type?
If they are numeric columns then you should drop the single-quotes.

Shift Excel Cell down after executing a macro for second time

I've written a (below)macro that pulls data from the sql server 2008 r2. My issue is when the user runs the macro for first time by entering Jobnumber (say J0001) excel puts data on the spreadsheet starting from cell "A1" which is fine. The issue here is, when the user runs the macro for the second time by entering the jobnumber (say J0002), excel puts the data for Jobnumber (J0002) on cell "A1" and shifts the cells for J0001(first job) to cell "F" instead of moving down. How can I shift the previous entry down in the spreadsheet with the latest entry on top?
Here is my macro and attachment:
Sub Task()
Dim sqlstring As String
Dim connstring As String
Dim Strcode As String
Strcode = Trim(InputBox("Please enter a Job #", "Task history"))
sqlstring = "select distinct m.JobNumber , cast(m.ExpectedDate as DATE) 'Ship Date' ,m.CustLongName 'Customer' & _
" from ArchiveJobHeader m left join AuxiliaryInfoFile af (nolock) on af.jobnumber=m.jobnumber & _
" where m.JobNumber = '" & Trim(Strcode) & "'" & _
" order by 'Resulttime'"
connstring = "ODBC;DSN=SQLDSN;UID=test;PWD=test123"
Dim thisQT As QueryTable
Set thisQT = ActiveSheet.QueryTables.Add(Connection:=connstring, Destination:=Range("a1", "a1000"))
thisQT.BackgroundQuery = False
thisQT.Sql = sqlstring
thisQT.Refresh
End Sub][1]
If you incorporate a lastRow check and then assign a variable the Next Row number, you can concatenate your Range and it will be a new row every time.
Dim lastRow As Long, nextRow As Long
lastRow = Sheets("Sheet Name").Range("A" & Rows.count).End(xlUp).row
nextRow = lastRow + 1
Then when you set your Range, concatenate the variable with the string.
Set thisQT = ActiveSheet.QueryTables.Add( _
Connection:=connstring, _
Destination:=Range("A" & nextRow))
I'm not sure what you are doing with row 1000 as shown in your question. But this is the idea of using a variable with your normal Range Address.
You could have something like this:
Sub a()
'Must set Reference to "Microsoft ActiveX Data Objects 2.8 Library"
Dim ws As Worksheet
Dim n As Long ' Row To Write In
Dim sql As String
Dim cn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim i As Long
Set ws = ThisWorkbook.Worksheets("Tab Name")
'Assuming you already have Headings in row 1 _
and start adding records from "A2" down...
n = ws.Range("A10000").End(xlUp).row + 1
'you sql string above is missing some double quotes...
sql = "select distinct m.JobNumber, cast(m.ExpectedDate as DATE) 'Ship Date', m.CustLongName 'Customer'" & _
" from ArchiveJobHeader m left join AuxiliaryInfoFile af (nolock) on af.jobnumber=m.jobnumber" & _
" where m.JobNumber = '" & Trim(Strcode) & "'" & _
" order by 'Resulttime'"
Set cn = New ADODB.Connection
' using an ODBC DSN... as in <http://msdn.microsoft.com/en-us/library/ms807027.aspx>
cn.Open "SQLDSN", "test", "test123"
Set rs = cn.Execute(sql) ' likely, in your case, to return one record only, _
so you are on there right away
For i = 0 To rs.Fields.Count
ws.Cells(n, i + 1) = rs(i)
Next
rs.Close
cn.Close
End Sub
You would need to put more work into this, I am afraid, but this is the direction you may consider.

Sql query taking values from the column in the excelsheet using VBA(macro)

I need to use sql query using VBA. My input values for the query is from the Column in the excel sheet.I need to take all the values present in the column and it should be passed as input to the query to sqlserver. But i could'nt get the answer. I am getting type mismatch as error. could any one help me out. Thanks in advance
for example in J column contains J1=25, j2=26, ....so on
stSQL = "SELECT * FROM prod..status where state in"
stSQL = stSQL & wsSheet.Range("J:J").Value
My full code is below
Sub Add_Results_Of_ADO_Recordset()
'This was set up using Microsoft ActiveX Data Components version 2.8
Dim cnt As ADODB.Connection
Dim rst As ADODB.Recordset
Dim stSQL As Variant
Dim wbBook As Workbook
Dim wsSheet As Worksheet
Dim rnStart As Range
Const stADO As String = "Provider=SQLOLEDB.1;Integrated Security=SSPI;" & _
"Persist Security Info=False;" & _
"Initial Catalog=prod;" & _
"Data Source=777777777V009D\YTR_MAIN4_T"
'where BI is SQL Database & AURDWDEV01 is SQL Server
Set wbBook = ActiveWorkbook
Set wsSheet = wbBook.Worksheets("sheet1")
With wsSheet
Set rnStart = .Range("A2")
End With
' My SQL Query
stSQL = "SELECT * FROM prod..status where state in"
stSQL = stSQL + wsSheet.Range("J:J").Value
Set cnt = New ADODB.Connection
With cnt
.CursorLocation = adUseClient
.Open stADO
.CommandTimeout = 0
Set rst = .Execute(stSQL)
End With
'Here we add the Recordset to the sheet from A1
rnStart.CopyFromRecordset rst
'Cleaning up.
rst.Close
cnt.Close
Set rst = Nothing
Set cnt = Nothing
End Sub
change to
stSQL = stSQL + " ('" + Replace(wsSheet.range("J:J").Value, "'", "") + ")"
but sql IN statement is usually used like this
state IN (25,26,28)
But if you are only using one integer value you might want to go this way.
stSQL = "SELECT * FROM prod..status where state = "
stSQL = Val(wsSheet.range("J:J").Value)
There is though one thing that is dangerous in using a in statement.
If your In part of the statement is very long, it will become slow and then with even larger in statements crash altogeather.
The solution for that kind of situation is creating a temp table with the in values and do where in (temp table) or a inner join based on the temp table
There is no space after your "in" in your stSQL variable. So if your cell contains the value "TEST", stSQL will be
SELECT * FROM prod..status where state inTEST
I used for loop and mid command to convert the values in the column to a single variable. Below is the code i used to perform the function which i required
' Getting the last row
With wsSheet
lastrow1 = .Range("J" & .Rows.Count).End(xlUp).Row
End With
' Appending the values to a single variable
For i = 1 To lastrow
s1 = s1 & "'" & Val(wsSheet.Cells(i, 10)) & "'" & ","
Next
' Variable which could be used in IN command
If lastrow > 0 Then
s1 = Mid(s1, 1, Len(s1) - 1)
s1 = "(" & s1 & ")"
Else
Exit Sub
End If