I am having a terrible time trying to get the most simple code to execute consistently in an Excel VBA module.
I'm just trying to assign the value of a field (column) in DB2 to a variable in the module. I've tried the following:
Connection:
Dim odbcName as String
Dim conn As New ADODB.Connection
Dim rs As New ADODB.Recordset
Dim strCmd As String
'ACME is the name of the ODBC connection I've established and verified/validated.
conn.Open("ACME")
strCmd = "SELECT UNIQUEID, MARKET, SECTOR, CLIENTID FROM PROJECTINFO WHERE UNIQUEID=1234567"
rs.Open Source:=strCmd, ActiveConnection:=conn, CursorType:=adOpenDynamic, LockType:=adLockOptimistic
All is good to that point. The next bit of code is:
If rs.EOF then
'Do some work to add a record if it doesn't already exist - this works fine.
Else
Dim sqlMarket as String
sqlMarket = rs.Fields("MARKET").Value
End If
I also tried:
sqlMarket = rs!MARKET
Both of those attempts, ON THAT LINE where I was attempting to assign a local variable the contents of the MARKET field in the recordset, caused it to crash. I mean, the entire workbook closes, then Excel reopens and tries to open the worksheets, but the module has crashed completely.
Ultimately, my intent is to check all the fields in the recordset against another set of data (in another Excel workbook), and if anything has changed, to update the DB2 recordset. But right now, I can't even seem to LOOK AT the contents of the DB2 recordset without Excel crashing.
Can anyone offer some advice as to what I'm either doing wrong, or what I'm missing?
Excel 365 (Version 1908) 64-bit
IBM DB21085I 64-bit (SQL11050 with level identifier 0601010F)
DB2 v11.5.0.1077
Windows 10 64-bit Version1909 (Build 18363.900)
Related
I need to connect to an Oracle database through Excel VBA.
I added the "Micorost ActiveX Data Objects 6.1" library in VBA.
Since I am using 64-bit version of Excel (in a 64-bit Windows 10), I think I cannot use "Microsoft ODBC for Oracle" (which seems to be 32-bit only); that's why I have installed "Oracle InstantClient v21.3" (InstantClient Basic, InstantClient ODBC and InstantClient SDK; all for Windows x64) into "D:\D:\Program Files\Oracle ODBC" and then installed it using the supplied "odbc_install.exe".
I have added that folder to the system PATH and TNS_ADMIN system variable.
My TnsName.ora has the following database definition:
DATAVIEW =
(DESCRIPTION =
(ADDRESS_LIST =
(ADDRESS = (PROTOCOL = TCP)(HOST = 10.x.x.x)(PORT = 1521))
)
(CONNECT_DATA =
(SERVICE_NAME = **MyStats**)
)
)
I can connect to that database using "PL SQL" and also whenever I add an entry in Windows 10's "ODBC Data Source Administrator (64-bit)", I can connect to the database. Moreover, using the "Data --> Get Data --> From Other Sources --> From ODBC" option in Excel itself, I can see the "MyStats" database in the list and can connect to it.
I am trying to connect to that database using the below code in Excel VBA:
Dim Database_Connection As ADODB.Connection
Dim Record_Set As ADODB.Recordset
Dim Database_Connection_String As String
Set Database_Connection = New ADODB.Connection
Set Record_Set = New ADODB.Recordset
Database_Connection_String = "Provider=OraOLEDB.Oracle;Data Source=(DESCRIPTION=(CID=GTU_APP)(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=10.x.x.x)(PORT=1521)))(CONNECT_DATA=(SID=MyStats)(SERVER=DEDICATED)));User Id=MyName;Password=MyPass;"
With Database_Connection
.ConnectionString = Database_Connection_String
.Open
End With
This runs into different run-time errors (in the ".Open" line).
I tried to supply "Provider=OraOLEDB.Oracle", "Driver=Oracle in Oracle ODBC". I tried TNS-based strings and standalone ones (giving "Data Source=", "Server=", "DBQ=" and ...). I either get run-time error 3706 which is saying the provider is unknown or Runtime-error -2147217805 (80040e73).
I searched a lot of forums and read a lot of examples. They rely on Microsoft ODBC for Oracle (which is what I don't want to use) or get the code to run somehow which I cannot replicate.
Can I give a different "Provider" name in the connection string or driver?
I'd appreciate if you can give me both TNS-based and standalone strings.
Had similar problem, but after long research I was able to resolve it with such connection string:
Dim Cn As New ADODB.Connection
With Cn
.ConnectionString = "Driver={Oracle in instantclient_21_3};" & _
"DBQ=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=<host>)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=<service_name>)));UID=<user_name>; PWD=<password>;"
Call .Open
End With
Please, change <...> values accordingly.
I get Operation is not allowed when the object is closed error.
Why I do get this error?
[Error location][2]
There is nothing wrong with the connection.
I can do SQL queries on the same cmd connection and have no problems with them.
Only this query messes me up.
I usually don't use Set search path or table short names, but just incase got
into very detail this time.
When I run the same query in PostgreSQL I get the result:
[postgres output for the same SQL query][1]
I have been digging around google for hours and I can't find any answers.
Does it have to do something with drivers?
How do I know what driver I use for excel?
What driver I should use?
How I change driver?
[References I use][3]
https://i.stack.imgur.com/R4nLe.png
https://i.stack.imgur.com/k4uJo.png
https://i.stack.imgur.com/Ytrlk.png
CODE
Public Function getDBArray(cmd, strSQL)
Dim recordID As Long, recordAmount As Long, totalFields As Integer, fieldID As Integer
Dim rs As New ADODB.Recordset
Set rs = CreateObject("ADODB.Recordset")
cmd.CommandText = strSQL '' << this is the strSQL i copy pasted directly to pgadmin and it worked
Set rs = cmd.Execute
totalFields = getTableAmount(rs.Fields)
recordAmount = getTableAmount(rs) ' << error in this function
recordID = 0
Apparently I was not suppose to used SET SEARCH PATH
I am getting weird return Values from my ADO Recordset.
I tried it in VBA, VB and C++, but everytime i get the same weird and wrong values.
The Data is stored in our ERP System as Dec(21,6), this information is passed to ADO correct (adNumeric, Precision 21, Scale 6).
I am connecting to the DB via ODBC and ADO, the connection works fine and varChar, Date and other formats are just passed fine, only the dec(21,6) values are showing this weird result.
However, if i run a MS Query, it returns the correct Values, so i think i am missing some settings, but can atm not figure out which ones ...
I already tried to cast/convert the values, but this is not supported by the ODBC.
I wrote an VB and an C++ Program and received the same wrong values.
Here is my simple VBA Code for testing:
Sub test()
Dim ADOODBCConnection As ADODB.Connection
Dim ADOODBCConnectionString As String
Dim ADOODBCRS As ADODB.Recordset
Dim TestString As String
Dim TestVariant As Variant
ADOODBCConnectionString = "DSN=myDSN" 'DSN Name
Set ADOODBCConnection = New ADODB.Connection
Set ADOODBCRS = New ADODB.Recordset
ADOODBCRS.Source = "SELECT quantity_amount FROM ENTITY.app_production_ProductionOrder WHERE type_BK ='101' AND number ='PA00041825';"
ADOODBCConnection.Open ADOODBCConnectionString
ADOODBCRS.ActiveConnection = ADOODBCConnection
ADOODBCRS.Open
'wrong Values are already in the recordset
TestString = ADOODBCRS.Fields(0).Value 'wrong Value
TestVar = ADOODBCRS.Fields(0).Value 'wrong ValueUrsprungspost
ADOODBCRS.Close
ADOODBCConnection.Close
End Sub
Here are some expected and some received values:
Expected Received
1 -13511005043556401
2 -13511005043556402
3 -13511005043556403
7 -13511005043556407
8 -13511005043556408
9 -13511005043556409
10 -885457226534512230449
500 -885456663593148743733
Ok, i found that ADO is returning the wrong Values, but not .NET ODBC, so i switched from Excel to VB and used .NET ODBC (system.data.odbc).
I am still confused, why this behaves the way it does, but it seems to be in the configuration of ADO or our ODBC, both i can not change, so i use an alternative.
Long story short: ADO gets wrong values, .NET ODBC gets the correct ones
Here is what I'm trying to do with Excel VBA:
Copy a range in Excel
Open an Access database
Delete records from the CV table
Paste the new records from Excel into the CV table
Run a make table query
Close the database
The code below worked - once. After it ran successfully once, it will not run again. There is no error message - the Access DB just never opens and the macro ends. Nothing ran behind the scenes, the Access DB was never touched.
I am speculating that the error might have to do with the fact that the application was opened once and maybe not closed properly and therefore can't reopen? (No idea if this is accurate/makes sense)
Sheets("NAHVCV").Select
Range("A:C").Select
Selection.Copy
Dim appAccess As New Access.Application
Set appAccess = Access.Application
appAccess.OpenCurrentDatabase AccessDBPath
appAccess.Visible = True
appAccess.CurrentDb.Execute "DELETE * FROM [CV]"
appAccess.DoCmd.OpenTable "CV", acViewNormal, acEdit
appAccess.DoCmd.RunCommand acCmdPasteAppend
appAccess.DoCmd.Close acTable, "CV", acSaveYes
appAccess.DoCmd.OpenQuery "qryMakFutRetroVariance"
appAccess.CloseCurrentDatabase
appAccess.Quit acQuitSaveAll
Maybe define the access application above/outside the sub:
Dim appAccess As New Access.Application
Sub Test()
'Add the rest of your code
End Sub
I had a similar problem to yours but once I moved the access application out of the sub, my access file opened up every time
Referring to your questions, Ashley, I distinguish
How to open Ms Access Database from Excel VBA (but not necessery for point 2)
Update data in Ms Access table CV from Excel VBA
Ad 1.:
To open an Access Database via method .OpenCurrentDatabase from Excel application VBA, select - with early binding - in Tools|References the library "Microsoft Access 16.0 Object Library" (V16.0 = Office 365).
After some tries, I understood that the opened Access Application, created by
Set apAccess = New Access.Application
is still linked to this object 'apAccess' created at runtime and is destoyed on ending Sub/Function, hence the object required to be defined globaly at VBA Module top, outside any Sub/Function (thanks, Brendon for the hint!) to 'remain alive' after Sub/Function ending:
Option Explicit,
Dim apAccess as Access.Application
Sub OpenAccessApp(sAccessPathFile as String)
Set apAccess = New Access.Application 'instantiate global defined Access object
appAccess.OpenCurrentDatabase(sAccessPathFile)
apAccess.Visible = True
End Sub
Do not destroy object 'apAccess' via Set apAccess = Nothing - this will close Ms Access DB!
Ad 2.:
To update records in another Ms Access database - from Excel VBA - you don't need to open the Access data base Window, as shown in Ad 1. above, but just use Access 'build-in' DAO objects; this requires linking Excel to Access database engine library (Tools|References, select library "Microsoft Access 16.0 database engine Object Library" - NOT "Microsoft Access 16.0 Object Library", mentioned above in Ad 1.); alternatively you can also process with ADODB (which requires a reference to another library), but below, I refer to DAO-object and -method, run in an Excel Project:
Sub UpdateAccData(sDBPathFileName as String) 'Parameter includes external Access Path\Filename
Dim db as DAO.Database 'define DAO object, only works when referencing to database engine library!
Set db = DBEngine.Workspaces(0),OpenDatabase(sDBPathFileName) 'Instantiate DAO Database object
'Run MakeTable query to build table 'CV' by SQL via Execute method:
db.Execute "SELECT 'Value1' As Field1, 815 as Field2, #05/18/2022# as Field3 INTO CV;", dbFailOnError
'Note: Field1 becomes Short Text, Field2 = Number (Long Integer), Field3 = Date/Time
But, I would dissuade to use MakeTable queries: in order to build a new table 'CV', MakeTable destroyes existing table 'CV', before creating a new one. If your data includes corrupted fields, table 'CV' failed to be build AFTER old 'CV' was already destroyed; as old table 'CV' was deleted, table 'CV' is missing in the entity relation! If Forms or Queries refer to table 'CV', they generates error, when opened or launched. Hence, keep table 'CV' and just remove records by DELETE and add new records via "INSERT INTO" as shown below (please update fields and Value acc. your need):
'Remove records in table 'CV' by SQL via Execute method:
db.Execute "DELETE CV.* FROM CV WHERE (((CV.Field5)='Criteria'));", dbFailOnError 'omit WHERE clause, if you would empty the whole table
'AddNew/insert new records in table 'CV' with method Execute:
db.Execute "INSERT INTO CV ( Field1, Field2, Field3 ) SELECT 'Value1', 815, #05/18/2022#;", dbFailOnError
'Alternative: Update existing records in table 'CV':
db.Execute "UPDATE CV SET CV.Field1='Value1', CV.Field2=815, CV.Field3=#05/18/2022#, WHERE (((CV.Field5)='Criteria'));", dbFailOnError
End Sub
Is there any way to read all the data from excel and put it in the datatable or any other container so that i can filter the data based on the conditions required. As shown in attached image i want to get the CuValue of a Partnumber whose status is Success and i want the latest record based on the Calculation date(Latest calculation date). In the below example i want the CuValue 11292 as it is the latest record with status Success..lue.
Thanks in advance
Your question seems very broad, but you're right to ask because there are many different possibilities and pitfalls.
As you don't provide any sample code, i assume you are looking for a strategy, so here is it.
In short: create a database, a table and a stored procedure. Copy the
data you need in this table, and then query the table to get the
result.
You may use ADO for this task. If it is not available on your machine you can download and install the MDAC redistributable from the Microsoft web site.
The advantage vs. OLE Automation is that you doesn't need to install Excel on the target machine where the import shall be executed, so you can execute the import also server-side.
With ADO installed, you will need to create two Connection objects, a Recordset object to read the data from the Excel file and a Command object to execute a stored procedure which will do the INSERT or the UPDATE of the subset of the source fields in the destination table.
Following is a guideline which you should expand and adjust, if you find it useful for your task:
Option Explicit
Dim PartNo as String, CuValue as Long, Status as String, CalcDate as Date
' objects you need:
Dim srcConn As New ADODB.Connection
Dim cmd As New ADODB.Command
Dim rs As New ADODB.Recordset
Dim dstConn As New ADODB.Connection
' Example connection with your destination database
dstConn.Open *your connection string*
'Example connection with Excel - HDR is discussed below
srcConn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _
"Data Source=C:\Scripts\Test.xls;" & _
"Extended Properties=""Excel 8.0; HDR=NO;"";"
rs.Open "SELECT * FROM [Sheet1$]", _
srcConn, adOpenForwardOnly, adLockReadOnly, adCmdText
' Import
Do Until rs.EOF
PartNo = rs.Fields.Item(0);
CuValue = rs.Fields.Item(1);
CalcDate = rs.Fields.Item(6);
Status = rs.Fields.Item(7);
If Status = "Success" Then
'NumSuccess = NumSuccess + 1
' copy data to your database
' using a stored procedure
cmd.CommandText = "InsertWithDateCheck"
cmd.CommandType = adCmdStoredProc
cmd(1) = PartNo
cmd(2) = CuValue
cmd(3) = CalcDate
cmd.ActiveConnection = dstConn
cmd.Execute
Else
'NumFail = NumFail + 1
End If
rs.MoveNext
Loop
rs.Close
Set rs = Nothing
srcConn.Close
Set srcConn = Nothing
dstConn.Close
Set dstConn = Nothing
'
By using a stored procedure to check the data and execute the insert or update in your new table, you will be able to read from Excel in fast forward-only mode and write a copy of the data with the minimum of time loss, delegating to the database engine half the work.
You see, the stored procedure will receive three values. Inside the stored procedure you should insert or update this values. Primary key of the table shall be PartNo. Check the Calculation Date and, if more recent, update CuValue.
By googling on the net you will find enough samples to write such a stored procedure.
After your table is populated, just use another recordset to get the data and whatever tool you need to display the values.
Pitfalls reading from Excel:
The provider of your Excel file shall agree to remove the first two or three rows, otherwise you will have some more work for the creation of a fictitious recordset, because the intelligent datatype recognition of Excel may fail.
As you know, Excel cells are not constrained to the same data type per-column as in almost all databases.
If you maintain the field names, use HDR=YES, without all the first three rows, use HDR=NO.
Always keep a log of the "Success" and "Fail" number of records read
in your program, then compare these values with the original overall
number of rows in Excel.
Feel free to ask for more details, anyway i think this should be enough for you to start.
There are lots ways you can do this.
1. You can create an access DB table and import by saving your sheet as can file first, into the access table. Then you can write queries.
2. You can create a sql DB and a table, write some code to import the sheet into that table.
3. You can Write some code in VBA and accomplish that task if your data is not very big.
4. You can write c# code to access the sheet using excel.application and office objects, create a data table and query that data table
Depends on what skills you want to employ to accomplish your task.