Excel VBA - Get Data from SQL based of Cell range values - vba

I've been tasked to get data from a SQL DB based off the values in Column A Row 3 onwards.
Example of Excel Sheet:
ITEM | QTY TO PICK | QTY ON ORDER | Column 2 | Column 3 etc
PART 1 | 5 | <Data will be populated here>
PART 2 | 12 | <Data will be populated here>
This code runs through a Command Button.
The data pulled from SQL will be populated starting in C3 onwards.
However, my below code only returns one row at a time.
I know where I need to make changes, I just don't know what. After at least 2 hours googling, I'm thoroughly stumped.
ItemNumber = Range("A3").Value
I've tried amending to ("A3:A100").Value but I just get errors.
Full code below;
Private Sub CommandButton2_Click()
' Create a connection object.
Dim cn As ADODB.Connection
Set cn = New ADODB.Connection
' Provide the connection string.
Dim strConn As String
'Use the SQL Server OLE DB Provider.
strConn = "Provider=SQLOLEDB;"
'Connect to the Pubs database on the local server.
strConn = strConn & "server=<server name>;INITIAL CATALOG=<DB Name>;"
'Use an integrated login.
strConn = strConn & " INTEGRATED SECURITY=sspi;"
'Now open the connection.
cn.Open strConn
'
'
ActiveSheet.Range("C3:G10000").Clear ' clear out existing data
Dim ItemNumber As String
ItemNumber = Range("A3").Value
' Create a recordset object.
Dim rs As ADODB.Recordset
Set rs = New ADODB.Recordset
SQLStr = "Select * from vw_WorksOrder WHERE ITEMNO = " & ItemNumber & ""
rs.Open SQLStr, cn
' Copy the records into cell A1 on Sheet1.
Sheet4.Range("C3").CopyFromRecordset rs
' Tidy up
rs.Close
cn.Close
Set rs = Nothing
Set cn = Nothing

You could try building the SQL like so
"Select * from vw_WorksOrder WHERE ITEMNO in (" & _
join(application.transpose(range("a1:a5").Value),",") & ")"
or, for strings.
"Select * from vw_WorksOrder WHERE ITEMNO in ('" & _
join(application.transpose(range("a1:a5").Value),"','") & "')"

My advise is:
1.)
Create a table on SQL Server which contains parameters for your sql statement (itemnumber = 1)
2.)
Write a cycle which loops tru the ranges and adds values one by one to the table like:
i = 1
while cells(i,3).value <> ""
... Insert into temptable values (parameter1,parameter2, etc)
i = i+1
wend
3.)
Modify your sql statement for the recordset joining the table with the parameters and the data you would like to pull and paste that data.

Related

Excel VBA ADODB - How to populate data based on an excel column values directly from SQL database without copying data into excel sheet

I'm running the following code for appending the data into columns B to G. My data is in Snowflake datawarehouse. The data is in millions so I cannot copy it into excel sheet first.
Option Explicit
Sub GetData()
Dim rs as ADODB.Recordset
Dim cn as ADODB.Connection
Dim connstr as String
Dim sqlstr as String
Set cn = New ADODB.Connection
cn.ConnectionString = DSNdetails.connstr
'DSNdetails is a module that holds DSN, DBCName, Databasename,Uid and password
cn.Open connstr
cn.Execute "USE WAREHOUSE db_warehouse;"
sqlstr = "SELECT employee_num, name, address, email, phone_num FROM Company;"
Set rs = New ADODB.Recordset
rs.Open sqlstr, cn, adStateOpen, adLockReadOnly
Sheet1.Cells(2,1).CopyFromRecordset rs
rs.Close
Set rs = Nothing
cn.Close
Set cn = Nothing
End Sub
This code runs perfectly. However the issue starts when I add WHERE clause and IN operator.
A | B | C | D |
Employee_num | name | address | phone
123
234
456
and so on
So now my new query is as below and Table1 is column A (I have considered it as string as well but same error) User can enter employee_num in each row and no matter how many rows they enter the data should populate in columns B to G if that employee_num in column A exists in SQL/Snowflake database
sqlstr = "SELECT employee_num, name, address, phone_num FROM Company WHERE employee_num IN ('"& _
Join(Application.Transpose(Range("Table1[Employee_num]").Value),'",")&");"
the code when debugged shows points at
rs.Open sqlstr, cn, adStateOpen, adLockReadOnly
Edit: Now I have a new runtime error "ODBC driver does not support the requested properties"

How to add time stamp in macro from selected excel sheet cells

My vba code pulls data of last "11" hours from Database.
I want to pull data of my own time like "between (1/4/19 1:30 & 13/4/19 1:30)".Also want to add the data in sheet 1 like a table.
May i know how to add this custom time & make it a table using macro code.
Option Explicit
Sub DbConnection()
' NA Query connection with DB
Dim cn As New ADODB.Connection
Dim rs As New ADODB.Recordset
Dim strConn As String
Dim mssql As String
strConn = "Driver={Redshift (x86)};Server=abc;Database=xyz;UID=abc;PWD=12345; Port=1234"
cn.Open strConn
cn.CommandTimeout = 60
mssql = "(using this line in SQL query here)"
where review_completed_timestamp_utc::TIMESTAMP > current_timestamp - interval'11 hour'
rs.Open mssql, cn
Sheets(1).Range("A2").CopyFromRecordset rs
End Sub
For the SQL part, since you didn't provide the names, you can do somenthing like this:
SELECT column_name(s)
FROM table_name
WHERE column_name BETWEEN value1 AND value2;

Excel Vlookup against SQL database

I normally vlookup my data against a table (database) in another workbook. (Excel to Excel --that's normally everyone does).
Since my table (database) grows more than 1.4 million rows therefore I need to transfer it to SQL Table in MS SQL Database.
I cannot move regular excel files just for Vlookup to SQL.
How do I vlookup excel against SQL tables.
Any solution with visual basic OR TSQL to fulfill requirement.
thanks
Yes, obviously, the limitation is just over a million rows, so you can use a Power Pivot Table to connect to multiple CSV files, aggregate the date you need, even if the total number of rows is over a million, and consolidate everything in one worksheet.
See the links below for more ideas of how to do this.
https://powerpivotpro.com/2017/01/import-csv-files-folder-filenames-excel/
http://sfmagazine.com/post-entry/january-2016-excel-combining-many-csv-files/
https://support.office.com/en-us/article/create-a-pivottable-with-an-external-data-source-db50d01d-2e1c-43bd-bfb5-b76a818a927b
You would use the 'Where' clause!
Sub ImportFromSQLServer()
Dim Cn As ADODB.Connection
Dim Server_Name As String
Dim Database_Name As String
Dim User_ID As String
Dim Password As String
Dim SQLStr As String
Dim RS As ADODB.Recordset
Set RS = New ADODB.Recordset
Server_Name = "your_server_name_here"
Database_Name = "your_DB_name_here"
'User_ID = "******"
'Password = "****"
SQLStr = "select * from dbo.TBL Where EMPID = '2'" 'and PostingDate = '2006-06-08'"
Set Cn = New ADODB.Connection
Cn.Open "Driver={SQL Server};Server=" & Server_Name & ";Database=" & Database_Name & ";"
'& ";Uid=" & User_ID & ";Pwd=" & Password & ";"
RS.Open SQLStr, Cn, adOpenStatic
With Worksheets("Sheet1").Range("A1")
.ClearContents
.CopyFromRecordset RS
End With
RS.Close
Set RS = Nothing
Cn.Close
Set Cn = Nothing
End Sub
Yes, you should be able to do that:
googling "excel connect to sql server" brings the result (description how to connect to a sql data source, to long to cite it here)
https://support.office.com/en-us/article/connect-a-sql-server-database-to-your-workbook-power-query-22c39d8d-5b60-4d7e-9d4b-ce6680d43bad
Then, when you have the connection, write your own lookup function in VBA, doing whatever you like.

Getting user ID from a Spreadsheet and Access database

Why is this code not working? Sorry for the generic question....
I am tasked with generating reports with reference information that needs to be drawn from an access database and an excel spreadsheet.
Basically in my role I'm responsible for providing service to people who live in a community; the record of all the people I provide service for is contained in an access database. There's reference information; address, name, situation, and other information needed for regular reports to funders or the board of directors.
I also provide service to local businesses; this information is contained within a spreadsheet, and not a database. The information could be put into a relational database, with the two related together; but there is resistance at the organization for significant changes to the system, nor is there really the knowledge of how to do this.
So I'm trying to move forward with a spreadsheet - if I provide service to person A or organization B, that this spreadsheet will check both the access database, and the excel spreadsheet to see whether that person or organization is entered; if it is, it should populate a table with that information, and assign it a unique code.
The unique code is determined on the basis of the database; whether or not the person or organization has been entered into the database before.
The spreadsheet I am working at the base with is this:
The bottom table I am looking to be a 'lookup' table. Its name is Lookup. The code I want to run with it looks like this (but obv not is this):
Sub getUserID()
Dim myTable As ListObject
Set myTable = Sheets("Client Codes").ListObjects("Lookup")
If myTable.ListRows.Count >= 1 Then
myTable.DataBodyRange.Delete
End If
With Sheets("Client Codes").ListObjects("Lookup").Add(SourceType:=0, Source:=Array(Array("ODBC;DSN=MS Access Database;DBQ=C:\database\here\test.accdb;DefaultDir=F:\Housing;DriverId=25;FIL=MS Access;MaxBufferSize=2048;PageTimeo"), Array("ut=5;")), Destination:=myTable.Range(Cells(1, 1)))
.CommandText = Array("SELECT Clients.ID, Clients.LastName, Clients.FirstName " & Chr(13) & "" & Chr(10) & "FROM `C:\database\here\test.accdb`.Clients Clients" & Chr(13) & "" & Chr(10) & "WHERE (Clients.LastName='" & Range("b1").End(xlDown) & "') AND (Clients.FirstName='" & Range("c1").End(xlDown) & "')")
End With
With Sheets("Client Codes").ListObjects("Lookup").Add(SourceType:=0, Source:=Array(Array("ODBC;DSN=Excel Files;DBQ=C:\spreadsheet\here\text.xlsx;DefaultDir=c:\spreadsheet;DriverId=1046;MaxBufferSize=2"), Array("048;PageTimeout=5;")), Destination:=myTable.Range(Cells(1, 1)))
.CommandText = Array("SELECT `Businesses$`.Operation" & Chr(13) & "" & Chr(10) & "FROM `C:\spreadsheet\here\test.xlsx`.`Businesses$` `Businesses$`" & Chr(13) & "" & Chr(10) & "WHERE (`Businesses$`.Operation='" & Range("b1").End(xlDown) & "')")
End With
End Sub
The hope is to be able to query the database on the basis of either a persons first and last name, or to query the spreadsheet on the basis of organization name; and if there is a value that is found, to add some information to the table 'Lookup'. If nothing is found, then I will know its a new entry, and enter in the information as such.
For reference, the database has 3 fields (ID, LastName, FirstName); and the spreadsheet has 1 column (Operation).
Really the confusion is focused here:
How to 'add' the information based on a query to the listobject to a pre-existing table
How to do this both with an access database and an Excel spreadsheet
Any suggestions on other ways how this can be done would be appreciated; pull information from multiple data sources into one table so that it can be validated in that table.
EDIT: If I did this through Access or another database program, I would do an INNERJOIN on two tables; one of people, the other of businesses. I'm looking to keep excel though - I find it to be more user friendly.
EDIT: Code based on Ian's response....generates the following error message:
'run time error -2147467259, could not find installable ISAM'
Research on the internet seems to indicate the following:
1) People have gotten this error before
2) There might not be a proper DLL installed - not certain this is the case, because I'm trying to access access from excel, and it doesn't seem like there is a DLL for access here: https://support.microsoft.com/en-us/kb/209805
3) There might be issue of how the connection string is framed. The data source might need to be in quotes, the JET OLEDB needs to be used not ACE, the connection string needs to be extended to include 'extended properties' here: Error: "Could Not Find Installable ISAM"
The last one is obviously the biggest target (and has the most error about it).
Option Explicit
Sub getUserID()
Dim cmd As New ADODB.Command
Dim conn As New ADODB.Connection
Dim rs As New ADODB.Recordset
Dim strConn As String
Dim strSQL As String
Dim firstName As String
Dim lastName As String
firstName = "John"
lastName = "Smith"
strConn = "Provider = Microsoft.ACE.OLEDB.12.0;'DataSource=F:\Housing\bpTest.accdb'"
conn.Open strConn
strSQL = "SELECT * FROM Table Where FirstName = '" & firstName & "' AND LastName = '" & lastName & "';"
'& ... ' or You could put your InnerJoin SQL here
rs.Open strSQL, conn, adOpenDynamic, adLockOptimistic
If rs.EOF Then 'If the returned RecordSet is empty
MsgBox ("No record found")
Else
MsgBox (rs.Index)
End If
end sub
You will most like want to use ActiveX Data Objects to accomplish this. This will let you to pull data from the access database and also update the records in the access database from Excel.
Here is the Microsoft reference material: https://msdn.microsoft.com/en-us/library/ms677497(v=vs.85).aspx
And some sample code:
Dim cmd As New adodb.Command
Dim conn As New adodb.Connection
Dim rs As New adodb.Recordset
Dim strConn As String
strConn = "Provider = Microsoft.ACE.OLEDB.12.0;" _
& "Data Source=C:\AccessDatabse.accdb"
conn.Open strConn
strSQL = "SELECT * FROM Table Where FristName =" & strName & ... ' or You could put your InnerJoin SQL here
rs.Open strSQL, conn, adOpenDynamic, adLockOptimistic
If rs.EOF then 'If the returned RecordSet is empty
'...there is no match in database
Else
'the rs object will hold the ID you are looking for
End If
you can add a new records to the Access Database with:
myFieldList = Array("ID", "FirstName", "LastName")
myValues = Array(IDValue, FirstNameValue, LastNameValue)
rs.AddNew myFieldList, myValues

Locking record on MS ACCESS append/insert [duplicate]

I am using an MS Access append query to append inventory transactions to my ERP database (MYSQL).
Please advise how I would go about to modify my query to automatically insert the next sequential transaction ID (primary key) into the Inventory_transaction table, with ability to append multiple records at once.
My existing query works fine, but only when I append just one record.
I usually need to append multiple records simultaneously. Each record needs to have a unique sequential transaction ID (primary key). There would be multiple users using this app simultaneously, so I need minimal chance of duplicate a key violation, to prevent roll backs. I tried appending without using a primary key to see if my database would automatically assign a transaction ID, but unfortunately this this ERP field is not an auto-number and I cant modify the table structure...
Below are 2 queries.
This one currently works for generating a transaction ID for just one record.
SELECT Max([SYSADM_INVENTORY_TRANS].[TRANSACTION_ID])+1 AS new_inventory_transaction_ID
FROM SYSADM_INVENTORY_TRANS;
The 2nd query is the append query that contains the first query and I would much appreciate it if someone can modify the query so the user has ability to append multiple records at once with a unique transaction ID.
INSERT INTO SYSADM_INVENTORY_TRANS ( TRANSACTION_ID, WORKORDER_TYPE,
WORKORDER_BASE_ID, WORKORDER_LOT_ID, WORKORDER_SPLIT_ID, WORKORDER_SUB_ID,
OPERATION_SEQ_NO, REQ_PIECE_NO, PART_ID, TYPE, CLASS, QTY, COSTED_QTY,
TRANSACTION_DATE, WAREHOUSE_ID, LOCATION_ID, USER_ID, POSTING_CANDIDATE,
ACT_MATERIAL_COST, ACT_LABOR_COST, ACT_BURDEN_COST, ACT_SERVICE_COST,
CREATE_DATE, ADD_BURDEN, COUNT_SEQUENCE, DESCRIPTION )
SELECT T.new_inventory_transaction_ID, S.WORKORDER_TYPE, D.WORKORDER_BASE_ID,
D.WORKORDER_LOT_ID, D.WORKORDER_SPLIT_ID, D.WORKORDER_SUB_ID, D.OPERATION_SEQ_NO,
D.PIECE_NO, D.auto_issue_part_ID, S.TYPE, S.CLASS, D.[total_auto_issue Qty],
0 AS Expr6, Date() AS Expr1, D.BACKFLUSH_WHS_ID, D.BACKFLUSH_LOC_ID,
"SYSADM" AS Expr3, S.POSTING_CANDIDATE, S.ACT_MATERIAL_COST, S.ACT_LABOR_COST,
S.ACT_BURDEN_COST, S.ACT_SERVICE_COST, Date() AS Expr2, S.ADD_BURDEN,
S.COUNT_SEQUENCE, "ENTERED WITH ACCESS APP" AS Expr5
FROM tbl_static_autoissue_data AS S,
tbl_dynamic_autoissue_data AS D,
qry_transaction_ID_generator AS T;
Here are some notes that may help you towards your goal, however life would be a lot easier and a lot safer with autonumbers. This is VBA as you mention MS Access.
Function NextTranNumber(ByRef FirstTran As Long, _
ByRef LastTran As Long, Optional BlockSize = 1)
Dim cn As New ADODB.Connection
Dim rs As New ADODB.Recordset
Dim strSQL As String
Dim lngResult As Long
Dim strCon As String
lngResult = 0 'assume fail
strCon = TestCon ''Connection to back-end
cn.Open strCon
rs.CursorType = adOpenKeyset
rs.LockType = adLockPessimistic
rs.CursorLocation = adUseServer
''Where BEInfo is a single line table that holds a transaction seed
strSQL = "SELECT ASeqNumber FROM BEInfo"
rs.Open strSQL, cn, , , adCmdText
'Note this is ADO, so no rs.Edit
FirstTran = rs!ASeqNumber + 1
rs!ASeqNumber = rs!ASeqNumber + BlockSize
rs.Update
LastTran = rs!ASeqNumber
rs.Close
Set rs = Nothing
End Function
Sub TransactionProcessing()
Dim FirstTran As Long
Dim LastTran As Long
Dim db As Database
Dim sSQL As String
Dim Block As Long
Dim rs As DAO.Recordset
Set db = CurrentDb
'Existing temporary table
sSQL = "DELETE FROM FETempTrans"
db.Execute sSQL, dbFailOnError
'The records to be added to the main table
sSQL = "INSERT INTO FETempTrans ( ID, AText ) SELECT 0 AS ID, AText FROM Table1"
db.Execute sSQL, dbFailOnError
Block = db.RecordsAffected
'Reserve a transaction block based on the temp table count
NextTranNumber FirstTran, LastTran, Block
Set rs = db.OpenRecordset("FETempTrans")
Do While Not rs.EOF
rs.Edit
rs!ID = FirstTran
rs.Update
FirstTran = FirstTran + 1
rs.MoveNext
Loop
If FirstTran - 1 = LastTran Then
'compare the temp set to the main table
'if it passes, update the main table
Else
'fail
End If
End Sub