How to check if a worksheet exists in an excel file - excel

I am receiving an excel(Book1.xls) file from the user and sometimes one of the worksheets name is XYZ and sometimes its name is XYZ1.
I am doing
Dim Conn,Rs
Set Conn = CreateObject("ADODB.Connection")
Conn.ConnectionTimeout = 10
Conn.CommandTimeout = 300
Conn.Open "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\Book1.xls;Extended Properties=""Excel 12.0;HDR=No;IMEX=1"";"
Set RS = Conn.Execute("SELECT * FROM [XYZ$B1:B1]")
and when the worksheets name is XYZ1, the last line in the above code is blowing up with an error message:
The Microsoft Access database engine could not find the object 'XYZ$CB1:B1'. Make sure the object exists and that you spell its name and the path name correctly. If 'XYZ$CB1:B1' is not a local object, check your network connection or contact the server administrator.
Instead of the last line I would like to do something like
sheetNameString = Conn.Execute("SELECT Join(SheetName) from Book1.xls")
If InStr(sheetNameString,"XYZ1") Then
Set RS = Conn.Execute("SELECT * FROM [XYZ1$B1:B1]")
Else
Set RS = Conn.Execute("SELECT * FROM [XYZ$B1:B1]")
End If

Adding the following code after the conn.open worked for me:
Dim Cat, Tbl, Tabs
Cat = CreateObject("ADOX.Catalog")
Cat.ActiveConnection = Conn
Tabs = Cat.Tables
For Each Tbl In Tabs
'Tbl.Name holds the name
Next

You can use the "ADOX.Catalog" to get a list of the tables in a database:
Public Function getTableNames(oConn)
Dim aRVal : aRVal = Array()
Dim oCatalog : Set oCatalog = CreateObject( "ADOX.Catalog" )
Set oCatalog.ActiveConnection = oConn
Dim oTable
For Each oTable In oCatalog.Tables
ReDim Preserve aRVal( UBound( aRVal ) + 1 )
aRVal( UBound( aRVal ) ) = oTable.Name
Next
getTableNames = aRVal
End Function
If "XYZ1$" = getTableNames(Conn)(0) Then
Set RS = Conn.Execute("SELECT * FROM [XYZ1$B1:B1]")
Else
Set RS = Conn.Execute("SELECT * FROM [XYZ$B1:B1]")
End If
(not tested, assumes just one sheet/table, needs improvements (e.g. ReDim aRVal(oCatalog.Tables.Count - 1) instead of ReDim Preserve), not sure whether the trailing $ will be kept)
Another way:
Public Function getTableNames(oConn)
Const adSchemaTables = 20
Dim oRS : Set oRS = oConn.OpenSchema(adSchemaTables)
Dim aRVal : aRVal = Array()
Do Until oRS.EOF
ReDim Preserve aRVal(UBound(aRVal) + 1)
aRVal(UBound(aRVal)) = oRS.Fields("TABLE_NAME").Value
oRS.MoveNext
Loop
getTableNames = aRVal
End Function

Related

Single Result from recordset returning w/o being transposed

I am working on a project where I'm using SQL to return records from a database into a listbox using userinput. Things are fine when there's more than one result, the listbox reads properly. However, when there's only one record, it doesn't transpose it like it does with all the others. Multiple Returns Single Return
I've tried to do an "if employeeoutput.recordcount = 1 then don't transpose" type of thing, but that still sends me with the Single Return.
Private Sub cmdSearch_Click()
Stop
'connect to database
'make dynamic code
'profit
'set up DB and Recordset
Dim calldbconn As ADODB.Connection
Dim EmployeeOutput As ADODB.Recordset
Dim EmpData As Variant
'its a special tool we're going to use later
Dim InContactInput As String
Dim FNameInput As String
Dim LNameInput As String
Dim TeamNameInput As String
Dim TeamNumberInput As Integer
Dim SQLString As String
'set it up for a new recordset
Set calldbconn = New ADODB.Connection
Set EmployeeOutput = New ADODB.Recordset
'set the vars
InContactInput = txtInContactNum.Value
FNameInput = txtFirstName.Value
LNameInput = txtLastName.Value
TeamNameInput = cboTeamName.Value
TeamNumberInput = cboTeamName.ListIndex + 1
'---------A bunch of stuff here to build the SQL String----------
'----------------------------------------------------------------
'Time to connect
calldbconn.ConnectionString = StrCallDB
calldbconn.Open
On Error GoTo CloseConnection
With EmployeeOutput
.ActiveConnection = calldbconn
.Source = SQLString
.LockType = adLockOptimistic
.CursorType = adOpenKeyset
.Open
EmpData = .GetRows
On Error GoTo CloseRecordset
End With
'And now, the list box
With lstResults
.Clear
.List = Application.Transpose(EmpData)
End With
'close and reset
CloseRecordset:
EmployeeOutput.Close
CloseConnection:
calldbconn.Close
Set EmployeeOutput = Nothing
Set calldbconn = Nothing
End Sub

Adding Records to Access Table (Inconsistent Results)

I am working on a VBA project in Excel as a record management tool. In one case, a record(s) need to be added to 2 tables within the same Access DB with varying field info. I am able to successfully add to the first table (Tours_Items) which can add multiple records by looping through the available rows. However, I cannot get the following records to add to their respective table (Tours_Tours). When the code is executed I get the following error: "Run-time error'438': Object doesn't support this property or method".
The VBA for executing on Tours_Items is just about identical to Tours_Tours outside of the table names and the looping feature that is required for Tours_Items so I am just utterly bamboozled as to why it will not execute correctly when the more complicated one will. The subs run one after the other; Tours_Items and then Tours_Tours. I have even tried just executing Tours_Tours and I still get the same error.
The fields correctly match the Access DB fields (I have triple checked), and even if they did not, VBA would not throw the object error. The Access DB fields are appropriately configured to accept the types of data that are being added to the record.
Code:
Sub DBAddTours_Items()
Dim DBFile, Tbl As String
Dim SQL As String
Dim j, NxtRowItems, NxtItemRw As Integer
Dim con, rs As Object
Dim wsToursTtl As Worksheet
Set wsToursTtl = Worksheets("Tour Subtotal")
'Fill Items to DB
'Connect to the Database
Set con = CreateObject("ADODB.connection")
'Set File and tables
DBFile = ThisWorkbook.path & "\" & "DailyInfo.accdb"
con.Open "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & DBFile
'Define
NxtRowItems = WorksheetFunction.CountA(wsToursTtl.Range("B:B"))
NxtItemRw = WorksheetFunction.CountA(Sheets("Tours_Items").Range("A:A")) + 1
'Create the ADODB recordset object.
Set rs = CreateObject("ADODB.Recordset")
'Define Table and SQL
Tbl = "Tours_Items"
SQL = "SELECT * FROM " & Tbl
'Set the necessary recordset properties.
With rs
.CursorType = 1 'adOpenKeyset on early binding
.LockType = 3 'adLockOptimistic on early binding
'Open the recordset.
.Open SQL, con
'Loop through and add all items
For j = 2 To NxtRowItems
.AddNew
.Fields("ItemID") = (j - 3) + NxtItemRw
.Fields("TourID") = wsToursTtl.Cells(j, 1)
.Fields("ItemNum") = wsToursTtl.Cells(j, 2)
.Fields("Admission") = wsToursTtl.Cells(j, 3)
.Fields("Ticket") = wsToursTtl.Cells(j, 4)
.Fields("Premium/BAT") = wsToursTtl.Cells(j, 5)
.Fields("Tour") = wsToursTtl.Cells(j, 6)
.Fields("Price") = wsToursTtl.Cells(j, 7)
.Update
Next j
'Close Recordset
.Close
End With
'Close the connection.
con.Close
'Release Variables
Set rs = Nothing
Set con = Nothing
SQL = ""
Tbl = ""
End Sub
Sub DBAddTours_Tours()
Dim DBFile, Tbl As String
Dim SQL As String
Dim con, rs As Object
Dim wsToursTtl As Worksheet
Set wsToursTtl = Worksheets("Tour Subtotal")
'Fill Tour info Tours_Tours
'Connect to the Database
Set con = CreateObject("ADODB.connection")
'Set File and tables
DBFile = ThisWorkbook.path & "\" & "DailyInfo.accdb"
con.Open "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & DBFile
'Create the ADODB recordset object.
Set rs = CreateObject("ADODB.Recordset")
'Define Table and SQL
Tbl = "Tours_Tours"
SQL = "SELECT * FROM " & Tbl
'Set the necessary recordset properties.
With rs
.CursorType = 1 'adOpenKeyset on early binding
.LockType = 3 'adLockOptimistic on early binding
'Open the recordset.
.Open SQL, con
'Add new record to table
.AddNew
.Field("TourID") = wsToursTtl.Range("TourID_Dest").Value
.Field("TourDate") = wsToursTtl.Range("TourDate_Dest").Value
.Field("TourTime") = wsToursTtl.Range("TourTime_Dest").Value
.Field("Premium/Bat/Disc") = Me.PremiumBAT_Lbl.Caption
.Field("TourType") = wsToursTtl.Range("TourType_Dest").Value
.Field("GuestName") = wsToursTtl.Range("GuestName_Dest").Value
.Field("GuestAddress") = wsToursTtl.Range("GuestAddress_Dest").Value
.Field("GuestPhone") = wsToursTtl.Range("GuestPhone_Dest").Value
.Field("GuestEmail") = wsToursTtl.Range("GuestEmail_Dest").Value
.Field("GuestMember") = wsToursTtl.Range("GuestMem_Dest").Value
.Field("TourComments") = wsToursTtl.Range("Comments_Dest").Value
.Field("SaleDate") = wsToursTtl.Range("SaleDate_Dest").Value
.Field("TransactionDetails") = wsToursTtl.Range("Trxn_Dest").Value
.Field("SoldBy") = wsToursTtl.Range("SoldBy_Dest").Value
.Field("PaymentType") = wsToursTtl.Range("PayType_Dest").Value
.Field("BookedNum") = 2
.Field("LastModified") = Now
.Field("ModifiedBy") = Application.UserName
.Update
'Close Recordset
.Close
End With
'Close the connection.
con.Close
'Release Variables
Set rs = Nothing
Set con = Nothing
SQL = ""
Tbl = ""
End Sub
Any help is much appreciated!
The correct collection to access a field is .Fields plural.
So DBAddTours_Items is correct where in your other procedure, DBAddTours_Tours you called upon .Field singular. Which doesn't exist.
Documentation found here: https://learn.microsoft.com/en-us/sql/ado/guide/data/the-fields-collection?view=sql-server-ver15

Writing to Named Cells in Excel from Access

I've Searched Forums here and I can't seem to get this code to work.
I am Trying to Open a Workbook in Excel, and then populate a few of the Cells(Named Ranges). I can successfully open the workbook(the workbook has a bit of VBA that runs when it opens as well, formatting stuff only) but when I get down to the inputting information I get a 'Run-Time Error "438" Object Doesn't support this property or method.'
From the Previous answers on other similar questions I have done everything the way it was suggested however, I can't seem to get it to work.
Option Compare Database
Option Explicit
Public Sub MaterialInput()
Dim xlapp As Excel.Application
Dim wb As Excel.Workbook
Dim ws As Excel.Worksheet
Dim RsClient As Recordset
Dim RsJobsite As Recordset
Dim db As Database
Dim ClientSTR As String
Dim JobsiteSTR As String
Dim customer As Variant
Set db = CurrentDb
JobsiteSTR = "SELECT T1Jobsites.JobsiteNickName FROM T1Jobsites WHERE T1Jobsites.JobsiteID = 1" ' & Form_LEM.TxtJobsiteID
Set RsJobsite = db.OpenRecordset(JobsiteSTR, dbOpenSnapshot, dbSeeChanges)
ClientSTR = "SELECT T1Companies.CompanyName " & _
"FROM T1Companies INNER JOIN T1Jobsites ON T1Companies.CompanyID = T1Jobsites.CompanyId " & _
"WHERE (((T1Jobsites.JobsiteID)=1))"
'ClientSTR = "SELECT T1Companies.CompanyName FROM T1Companies INNER JOIN T1Jobsites ON T1Companies.CompanyID = T1Jobsites.CompanyID" & _
" WHERE T1JobsitesID = 1" '& Form_LEM.TxtJobsiteID
Set RsClient = db.OpenRecordset(ClientSTR, dbOpenSnapshot, dbSeeChanges)
Set xlapp = CreateObject("excel.application")
Set wb = xlapp.Workbooks.Open("C:\Users\coc33713\Desktop\VISION - EXCEL FILES\VISIONCOUNT.xlsm")
Set ws = xlapp.Worksheets("CountSheet")
xlapp.Visible = True
'Tried this second after reading another forum
'the comments Recordset will be the actual values used, but I can't get the String "TEST" to work
wb.ws.Range("Client").Value = "TEST" 'RsClient!CompanyName
'Tried this way first
xlapp.ws.Range("'SiteName'").Value = "Test" 'RsJobsite!JobsiteNickName"
xlapp.ws.Range(Date).Value = "Test" 'Form_LEM.TxtDate
xlapp.ws.Range(ProjectName).Value = "Test" 'Form_LEM.TxtPlant
xlapp.ws.Range(ScaffoldID).Value = "Test" 'Form_LEM.cboScaffnum.Value
xlapp.ws.Range(ScaffoldNumber).Value = "Test" 'Form_LEM.cboScaffnum.Column(1)
Set xlapp = Nothing
Set wb = Nothing
Set ws = Nothing
Set RsClient = Nothing
Set RsJobsite = Nothing
Set db = Nothing
End Sub
As a Sidenote this is not a form it is just spreadsheet
Thank you everyone!
Use
ws.Range("Client").Value = "Test"
Or
Dim sName as String
sName = "Client"
ws.Range(sName).Value = "Test"
Reason being is that you have the ws object set already, so there is no need to assign parentage to it again. In fact, trying to do so will break syntax rules.
FWIW (not your issue - that is solved by Scott's answer): Note that
Set ws = xlapp.Worksheets("CountSheet")
should be
Set ws = wb.Worksheets("CountSheet").
Using xlapp.Worksheets("CountSheet")
is effectively xlApp.ActiveWorkbook.Worksheets("CountSheet") which might be (and probably is) xlApp.Workbooks("VISION - EXCEL FILES\VISIONCOUNT.xlsm").Worksheets("CountSheet") but it is better to do it correctly rather than leave it to chance.
Thank you guys!
This should do what you want.
Sub DAOFromExcelToAccess()
' exports data from the active worksheet to a table in an Access database
' this procedure must be edited before use
Dim db As Database, rs As Recordset, r As Long
Set db = OpenDatabase("C:\FolderName\DataBaseName.mdb")
' open the database
Set rs = db.OpenRecordset("TableName", dbOpenTable)
' get all records in a table
r = 3 ' the start row in the worksheet
Do While Len(Range("A" & r).Formula) > 0
' repeat until first empty cell in column A
With rs
.AddNew ' create a new record
' add values to each field in the record
.Fields("FieldName1") = Range("NamedRange1").Value
.Fields("FieldName2") = Range("NamedRange2").Value
.Fields("FieldNameN") = Range("NamedRangeN").Value
' add more fields if necessary...
.Update ' stores the new record
End With
r = r + 1 ' next row
Loop
rs.Close
Set rs = Nothing
db.Close
Set db = Nothing
End Sub

Need help optimizing vba display function

I have been doing some database work with excel both as the database and the database driver via macros with vba. I built a function that should parse through a list of database records with testID fields. I wanted to display each test only once based on it's testID, but the way the database is set up means I have to eliminate duplicate testID's. I do this by iterating through the recordset and checking the current test against the previous one before showing in the list. The problem I'm having is that the function is excruciatingly slow. For only 12 tests in the database, it takes about 3 seconds to display them in the view spreadsheet. I'd love to hear some ideas on how to optimize the run-time. Here's the function:
Public Function showAllTests()
Dim conn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim cstring, sql As String
Dim r, c As Integer
Dim testsAr As Variant
Dim inAr As Boolean
cstring = "Provider = Microsoft.ACE.OLEDB.12.0; Data Source=I:\DBtrials.xlsx; Extended Properties=""Excel 12.0 Xml; HDR=YES;ReadOnly=False"";"
sql = "SELECT [TestID], [Status], [PFIBox], [WireType], [StartingDia], [Customer], [numSamples], [Assigned] FROM [Tests$]"
Set conn = New ADODB.Connection
Set rs = New ADODB.Recordset
Call conn.Open(cstring)
Set rs = conn.Execute(sql)
If rs.EOF Then
Range("C6:J1000").ClearContents
End If
r = 6
count = 0
'Iterates through the recordset, eliminating duplicates and populating cells in the tests sheet
While Not rs.BOF And Not rs.EOF
Dim prevID, currID As String
Dim currCell As Range
inAr = False
If Not count = 0 Then
prevID = ActiveWorkbook.Sheets("Tests").Cells(r - 1, 3).Value
currID = CStr(rs(0))
If prevID = currID Then
inAr = True
End If
End If
For c = 3 To (rs.Fields.count + 2)
Set currCell = ActiveWorkbook.Sheets("Tests").Cells(r, c)
If Not IsNull(rs(c - 3).Value) And inAr = False Then
currCell.Value = CStr(rs(c - 3))
ElseIf IsNull(rs(c - 3).Value) Then currCell.Value = ""
Else:
Exit For
End If
Next c
If inAr = False Then
r = r + 1
End If
rs.MoveNext
count = count + 1
Wend
conn.Close
Set conn = Nothing
End Function
use GROUP BY
sql = "SELECT [TestID], [Status], [PFIBox], [WireType], [StartingDia], [Customer], [numSamples], [Assigned] FROM [Tests$] GROUP BY [TestID]"
Also some of these drivers - Microsoft.ACE.OLEDB.12.0 etc have terrible perfomance in VBA. sometimes i get better perforamance from OBDC 6.2 than ADO

Not able to get out of the loop after getfirstitem in lotus script

Sub Initialize
On Error GoTo ErrorOut
Dim sess As NotesSession
Dim db As NotesDatabase
Dim doc, searchDoc, reqNumDoc As NotesDocument
Dim body As NotesMIMEEntity
Dim header As NotesMIMEHeader
Dim stream As NotesStream
Dim vwSearchRequests As NotesView
Dim reqNum, totalNotify, totalAccepted, totalRejected, totalOOO, totalNoRes As Integer
Dim reqSer, reqJRSS, reqSPOC, reqNumStr As String
Dim reqDate As String
Dim reqNumColl As NotesDocumentCollection
Dim reqPanelRes As NotesItem
Dim reqPanelResValue As Variant
Set sess = New NotesSession
Set db = sess.CurrentDatabase
Set vwSearchRequests = db.GetView("RequestDocReport")
vwSearchRequests.Autoupdate = False
Set searchDoc = vwSearchRequests.GetFirstDocument
While Not searchDoc Is Nothing
reqSer = "Service"
reqJRSS = searchDoc.PS_JRSS(0)
reqSPOC = "Hiring SPOC"
totalAccepted = 0
totalRejected = 0
totalOOO = 0
totalNoRes = 0
totalNotify = 0
reqNum = searchDoc.PS_RequestNo(0)
reqNumStr = {PS_RequestNo = "} & reqNum & {"}
Set reqNumColl = vwSearchRequests.GetAllDocumentsByKey(reqNumStr)
Set reqNumDoc = reqNumColl.GetFirstDocument
While Not reqNumColl Is Nothing
If Not reqNumDoc.GetFirstItem("PanelResponse") Is Nothing Then
reqPanelResValue = reqNumDoc.GetItemValue("PanelResponse")
MsgBox CStr(reqPanelResValue(0))
'Exit Sub
If CStr(reqPanelResValue(0)) = "Accepted" Then
totalAccepted = totalAccepted + 1
End If
If CStr(reqPanelResValue(0)) = "Rejected" Then
totalRejected = totalRejected + 1
End If
If CStr(reqPanelResValue(0)) = "OOO" Then
totalOOO = totalOOO + 1
End If
Else
If CStr(reqPanelResValue(0)) = "" Then
totalNoRes = totalNoRes + 1
End If
End If
totalNotify = totalNotify + 1
Set reqNumDoc = reqNumColl.GetNextDocument(reqNumDoc)
Wend
what is the error in code? The code is getting stuck after
If Not reqNumDoc.GetFirstItem("PanelResponse") Is Nothing Then
reqPanelResValue = reqNumDoc.GetItemValue("PanelResponse")
Instead of line
While Not reqNumColl Is Nothing
write
While Not reqNumDoc Is Nothing
You got an infinitive loop because the collection reqNumColl is not nothing all the time even when you reached the last document in collection. Instead you have to test the document reqNumDoc.
Another issue might be your code for collection calculation:
reqNumStr = {PS_RequestNo = "} & reqNum & {"}
Set reqNumColl = vwSearchRequests.GetAllDocumentsByKey(reqNumStr)
The way you coded it the first sorted column in view should contain
PS_RequestNo = "12345"
Probably, your view contains in first sorted column just the request number. If so, your code would be just:
Set reqNumColl = vwSearchRequests.GetAllDocumentsByKey(reqNum)
if column contains a numeric value or
Set reqNumColl = vwSearchRequests.GetAllDocumentsByKey(cStr(reqNum))
if it contains a string.
Apart from any other problems you might have in your code (and #Knut is correct about the cause of your infinite loop), this is not a good pattern:
If Not reqNumDoc.GetFirstItem("PanelResponse") Is Nothing Then
reqPanelResValue = reqNumDoc.GetItemValue("PanelResponse")
You're retrieving the item twice when you don't actually have to.
This woould be much better:
If reqNumDoc.HasItem"PanelResponse") Then
reqPanelResValue = reqNumDoc.GetItemValue("PanelResponse")

Resources