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
Related
I've read through postStackoverflow 14814098 and would like to know (2) things.
Can you update MS SQL Tables from Excel by creating a string with an Update statement that refers to an Excel Table. Below is a rough idea in VBA of what I mean.
If you add the SQL Statement to the server, how do you call it from Excel using VBA?
Background: I'm attempting to pull a table from the MS SQL Server, Load results into Excel Sheet as an Excel Table where I can exit the sheet and Update all changes back to the server table.
I set up a class and worksheet module to update the server after individual cells are changed in the worksheet, but now I would like to update all the changes at once.
Is there a better way to go about getting the result?
Sub UpdateSqlWithExcelTableJoin()
Dim cmd As ADODB.Command
Dim strSQL As String
Set cnn = New ADODB.Connection
cnn.ConnectionString = "DRIVER=SQL Server;SERVER=MYSERVERNAME;DATABASE=MYDATABASENAME;Trusted_Connection=Yes"
Set cmd = New ADODB.Command
cnn.Open
cmd.ActiveConnection = cnn
cmd.CommandType = adCmdText
Call setString2
cmd.CommandText = strSQLUpdate
cmd.Execute
cnn.Close
Set cmd = Nothing
Set cnn = Nothing
End Sub
Sub setString2()
strSQLUpdate = _
"Update test.profile " & vbNewLine & _
"Set test.profile.Field = ExcelTable.Field " & vbNewLine & _
" test.profile.Profile_Name = ExcelTable.Profile_Name " & vbNewLine & _
"From test.profile " & vbNewLine & _
"INNER JOIN OPENROWSET('MICROSOFT.JET.OLEDB.4.0', 'Excel 8.0;Database=C:\Users\USERNAME\ONEDRIVE - FOLDER\SQL_VBA_b.xlsm;', 'Select ID, Profile_Name' " & vbNewLine & _
"From '[Sheet3$]') As ExcelTable " & vbNewLine & _
"ON test.profile.ID = ExcelTable.ID " & vbNewLine & _
"WHERE (test.profile.ID = ExcelTable.ID " & vbNewLine & _
" AND test.profile.Profile_Name = ExcelTable.Profile_Name)"
Debug.Print strSQLUpdate
End Sub
I always found this to be easier to run this through an MS-Access connection than to connect directly to SQL Server.
Set up an Acess data base with two ODBC connections. A. Define the Excel data as a table to Access. B. Define the SQL Server table as a table to Access.
In Excel VBA change your ADOdb connection to connect to the Access db.
Now you can run a single update statement in ODBC SQL that looks like this:
Update SQLServerTable
Set SQLServerColumn = ExcelColumn
From SQLServerTable S
Inner Join ExcelTable E
Where sqlServerkey = ExcelKey
This is more flexible than trying to do it directly because if the update relationship grows more complex you can always code the FROM clause as an Access saved query, and that is the only good way to do nested queries in ODBC (queries that use other queries in FROM).
You can't use select at that position it will always take the whole sheet.
Also you are missing a comma after Field
Update test.profile
Set test.profile.Field = ExcelTable.Field,
test.profile.Profile_Name = ExcelTable.Profile_Name
From test.profile
INNER JOIN OPENROWSET('MICROSOFT.JET.OLEDB.4.0', 'Excel 8.0;Database=C:\Users\USERNAME\ONEDRIVE - FOLDER\SQL_VBA_b.xlsm;', [Sheet3$]) As ExcelTable
ON test.profile.ID = ExcelTable.ID
WHERE (test.profile.ID = ExcelTable.ID
AND test.profile.Profile_Name = ExcelTable.Profile_Name)
ado to retrieve records from SQL
save excel file
add excel file and SQL Table in Access
create Update Query using linked tables (sql and excel)
Open workbook / update data / close workbook / run query.
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.
I have a form where a user selects a vendor's name from a combobox, whose catalog file is to be imported. The combobox selection then drives a query to create a one-record recordset (rsProfile) containing several profile variables queried from a table of all vendor profiles. These variables are then used in a series of different queries to reformat, translate and normalize the vendor's uniquely structured files to a standardized format that can be imported into our system.
I am frustrated that I can't figure out how to build my stored queries that will use one or more parameters that are automatically populated from the profile recordset.
Here is my rsProfile harvesting code. It works. Note that intVdrProfileID is a global variable set and used in other places.
Private Sub btn_Process_Click()
Dim ws As Workspace
Dim db, dbBkp As DAO.Database
Dim qdf As DAO.QueryDef
Dim rsProfile, rsSubscrip As Recordset
Dim strSQL As String
Dim strBkpDBName As String
Dim strBkpDBFullName As String
strBkpDBName = Left(strVdrImportFileName, InStr(strVdrImportFileName, ".") - 1) & "BkpDB.mdb"
strBkpDBFullName = strBkpFilePath & "\" & strBkpDBName
Set db = CurrentDb
Set ws = DBEngine.Workspaces(0)
MsgBox ("Vendor Profile ID = " & intVdrProfileID & vbCrLf & vbCrLf & "Backup file path: " & strBkpFilePath)
' Harvest Vendor Profile fields used in this sub
strSQL = "SELECT VendorID, Div, VPNPrefix, ImportTemplate, " & _
"VenSrcID, VenClaID, ProTyp, ProSeq, ProOrdPkg, ProOrdPkgTyp, JdeSRP4Code, " & _
"PriceMeth, " & _
"ProCost1Frml, ProCost2Frml, " & _
"ProAmt1Frml, ProAmt2Frml, ProAmt3Frml, ProAmt4Frml, ProAmt5Frml " & _
"FROM tZ100_VendorProfiles " & _
"WHERE VendorID = " & intVdrProfileID & ";"
Set qdf = db.QueryDefs("qZ140_GetProfileProcessParms")
qdf.SQL = strSQL
Set rsProfile = qdf.OpenRecordset(dbOpenSnapshot)
DoCmd.OpenQuery "qZ140_GetProfileProcessParms"
' MsgBox (qdf.SQL)
I have used QueryDefs to rewrite stored queries at runtime, and although it works, it is quite cumbersome and does not work for everything.
I was hoping for something like the sample below as a stored query using DLookups. I can get this to work in VBA, but I can't get anything to work with stored queries. I am open to other suggestions.
Stored Query "qP0060c_DirectImportTape":
SELECT
DLookUp("[VPNPrefix]","rsProfile","[VendorID]=" & intVdrProfileID) & [PartNo] AS VenPrtId,
Description AS Des,
DLookup("[Jobber]","rsProfile",[VendorID=" & intVdrProfileID) AS Amt1,
INTO tP006_DirectImportTape
FROM tJ000_VendorFileIn;
ADDENDUM:
Let me adjust the problem to make it a bit more complex. I have a collection of about 40 queries each of which use a different collection of parameters (or none). I also have a table containing the particular set of queries that each vendor 'subscribes' to. The goal is to have a database where a non-coding user can add new vendor profiles and create/modify the particular set of queries which would be run against that vendor file. I have almost 100 vendors so far, so coding every vendor seperately is not practical. Each vendor file will be subjected to an average of 14 different update queries.
Simplified Example:
Vendor1 file needs to be processed with queries 1, 2 and 5. Vendor2 file might need only update queries 2 and 4. The parameters for these queries might be as follows:
query1 (parm1)
query2 (parm1, parm4, parm8, parm11)
query4 (parm5, parm6, parm7, parm8, parm9, parm10, parm11)
query5 () -no parms required
This is the core query processing that loops through only the queries relevant to the current vendor file. rsSubscrip is the recordset (queried from a master table) containing this filtered list of queries.
' Run all subscribed queries
MsgBox "Ready to process query subscription list."
With rsSubscrip
Do While Not .EOF
db.Execute !QueryName, dbFailOnError
.MoveNext
Loop
.Close
End With
You can set the parameters of a predefined query using the syntax;
Set qdf = CurrentDB.QueryDefs(QueryName)
qdf.Parameters(ParameterName) = MyValue
To add parameters to the query, add the following before the SELECT statement in the sql
PARAMETERS [ParameterOne] DataType, [ParameterTwo] DataType;
SELECT * FROM tblTest;
I'm trying to import rows from one db to another, basically it something to do with this SQL:
SELECT * INTO [MSAccess;DATABASE=C:\MainDB.mdb;].[Header] FROM [Header] WHERE ID=9
As it returns this error: Could not find installable ISAM.
Any ideas? To help explain I've added my code:
Dim sSQL As String
Dim iCertMainNo As Integer
Dim cnLocal As New System.Data.OleDb.OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0; Data Source=" & App_Path() & "LocalDB.mdb;")
Dim cnMain As New System.Data.OleDb.OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0; Data Source=" & My.Settings.MainDB & ";")
cnLocal.Open()
cnMain.Open()
Dim cmd As New System.Data.OleDb.OleDbCommand("SELECT * INTO [MSAccess;DATABASE=" & My.Settings.MainDB & ";].[tblCertHeader] FROM tblCertHeader WHERE ID = " & iCertNo, cnLocal)
cmd.ExecuteNonQuery()
cnMain.Close()
cnLocal.Close()
I'm thinking it's either do it the way listed above. Or to open two connections get one row from the local and then insert it into cnMain - but again not sure how to do this without listing all the fields... Can I just simply insert the row ?
it appears you are running from one MS Access database to another, so the connect string is much simpler:
SELECT * INTO [;DATABASE=C:\MainDB.mdb;].[Header] FROM [Header] WHERE ID=9
BTW It may not be possible to update a database in C:\, if that is a real path.
EDIT I tested with this:
''Dim sSQL As String
''Dim iCertMainNo As Integer
Dim cnLocal As New System.Data.OleDb.OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0; Data Source=C:\Docs\dbFrom.mdb;")
''Dim cnMain As New System.Data.OleDb.OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0; Data Source=" & My.Settings.MainDB & ";")
cnLocal.Open()
''cnMain.Open()
Dim cmd As New System.Data.OleDb.OleDbCommand("SELECT * INTO [;DATABASE=C:\Docs\DBTo.mdb;].[Header] FROM Header WHERE ID = 2", cnLocal)
cmd.ExecuteNonQuery()
''cnMain.Close()
cnLocal.Close()
And it worked fine for me. I commented out iCertMainNo because you did not use it. Your string included only iCertNo, for which i used the actual value for test purposes. I did not see any reason for two connections.
This is basic stuff, but I'm somewhat unfamiliar with VBA and the Word/Access object models.
I have a two column database of about 117000 records. The columns are 'surname' and 'count'. I want a user to be able to type SMITH in a textbox and hit submit. I then want to run something like
SELECT table.count FROM table WHERE surname = string
and return the value of table.count in a string.
It feels like this should be five or six lines of code (which I have but won't post) but I'm obviously missing something!
Cheers
First of all, be careful naming the column 'count' -- this is a keyword in SQL and might cause problems. Similarly, don't call the table 'table'.
Here is some sample code which shows one way of doing it:
' This example uses Microsoft ActiveX Data Objects 2.8,
' which you have to check in Tools | References
' Create the connection. This connection may be reused for other queries.
' Use connectionstrings.com to get the syntax to connect to your database:
Dim conn As New ADODB.Connection
conn.Open "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=c:\tmp\Database1.accdb"
Dim cmd As New ADODB.Command
Set cmd.ActiveConnection = conn
' Replace anything which might change in the following SQL string with ?
cmd.CommandText = "select ct from tbl where surname = ?"
' Create one parameter for every ?
Dim param As ADODB.Parameter
Set param = cmd.CreateParameter("surname", adBSTR, adParamInput, , TextBox1.Text)
cmd.Parameters.Append param
Dim rs As ADODB.Recordset
Set rs = cmd.Execute
MsgBox rs("ct")
rs.Close
conn.Close
It is possible to use InsertDatabase:
Sub GetData()
ActiveDocument.Bookmarks("InsertHere").Select
Selection.Range.InsertDatabase Format:=0, Style:=0, LinkToSource:=False, _
Connection:="TABLE Members", SQLStatement:= _
"SELECT [Count] FROM [Members]" _
& " WHERE Surname='" _
& ActiveDocument.FormFields("Text1").Result & "'", _
DataSource:="C:\docs\ltd.mdb", From:=-1, To:= _
-1, IncludeFields:=True
End Sub
This is an edited macro recorded using the database toolbar.
EDITED Warning: this code, as shown, is subject to a SQL Injection attack.