SQL VBA: Selecting all tables with specific table name and field name - sql

im working with access and VBA. As for now, I am trying to create a query with a SQL statement.
I have a bunch of tables, all of them are named "innen" at the end and they vary at the start. Each of these tables contain the column name "OP" (also other field names). Now my goal is to select all tables with the name containing '%innen' and the column name "OP". So far i tried this:
Sub Aktuell()
Dim strSQL As String
Dim db As DAO.Database
Set db = CurrentDb
Dim qdf As QueryDef
strSQL = "SELECT [*].OP FROM MSysObjects WHERE TABLE_NAME LIKE '%innen' ORDER BY MAX;"
db.Execute strSQL
Set qdf = CurrentDb.CreateQueryDef("NewQuery8", strSQL)
DoCmd.OpenQuery qdf.Name
End Sub
i tried this here aswell:
strSQL = "SELECT * " & _
"FROM INFORMATION_SCHEMA.TABLES " & _
"WHERE COLUMN_NAME = 'OP_Datum';"
But i keep getting errors.
Any ideas? does it even work with a sql statement via vba?

Here is a VBA solution for you.
Option Compare Database
Function GetFieldList(TableName As String) As String()
On Error GoTo Er
Dim Flds() As String
Dim fc As Long
Dim I As Long
'Initialize Dynamic Flds() Array
Flds = Split("")
fc = CurrentDb.TableDefs(TableName).Fields.Count - 1
If fc >= 0 Then
ReDim Preserve Flds(fc)
For I = 0 To fc
Flds(I) = CurrentDb.TableDefs(TableName).Fields(I).Name
Next I
End If
Done:
GetFieldList = Flds
Erase Flds
Exit Function
Er:
Resume Done
End Function
Sub flTest()
Dim I As Long
Dim Fields As Variant
Fields = GetFieldList("Customers")
If UBound(Fields) = -1 Then
MsgBox "Table Not Found, or Table has no fields", vbCritical + vbOKOnly
Exit Sub
End If
For I = LBound(Fields) To UBound(Fields)
Debug.Print """" & Fields(I) & """"
Next I
End Sub
I'll bet there is a way to so the same thing using nothing but SQL. Although, Access is a unique animal. You can do this using SQL Server. I'm not 100% sure Access can handle it. Well, why not try it and see for yourself.

Related

Inherited MS Access Database, Tracking Sources of Queries

I have just inherited a database at my new company. Old DB owner left no good documentation and queries very hard to keep track of. Looking for programmatic answer to track sources of fields in every query (what table it come from). Prefer something can be exported to Excel to study, Access visualization is no good. Am familiar with VBA.
This is pretty messy but could save you time collecting each query's SQL code. The following code exports all SQL stored in the QueryDefs collection into a text file. I have it splitting the code with a space delimiter, but a comma might be preferable. The data will not be normalized, I don't have the time to go to that level of complexity. Just make sure to update strPath before you execute. Hopefully this helps.
Sub PullQuerySQL()
Dim dbs As Database
Dim i As Integer
Dim fso As Object
Dim oFile As Object
Dim varParse() As String
Dim element As Variant
Dim strPath As String
strPath = ".txt"
Set dbs = CurrentDb()
Set fso = CreateObject("Scripting.FileSystemObject")
Set oFile = fso.CreateTextFile(strPath)
For i = 0 To dbs.QueryDefs.Count - 1
oFile.WriteLine dbs.QueryDefs(i).Name
varParse = Split(dbs.QueryDefs(i).SQL, " ")
For Each element In varParse
oFile.WriteLine element
Next element
Next i
oFile.Close
Set oFile = Nothing
Set fso = Nothing
Set dbs = Nothing
End Sub
I have been through this with many inherited databases. I find it extremely helpful to create an Access table with the fields and the tables/queries that they come from. Try this code below. It will prompt you for the name of the query that you are looking to "map" as I call it. It will then create a new table named "queryName Definitions".
Option Compare Database
Public Sub MapQuery()
Dim strQueryName As String
Dim rst As DAO.Recordset
Dim fld As Field
Dim strSource As String
Dim strField As String
Dim strValue As String
Dim strSQL1 As String
Dim strSQL2 As String
Dim booExists As Boolean
strQueryName = InputBox("Please enter the name of the query that you are looking to map")
Set rst = CurrentDb.OpenRecordset(strQueryName)
On Error GoTo error1
booExists = IsObject(CurrentDb.TableDefs(strQueryName & " Definitions"))
DoCmd.DeleteObject acTable, strQueryName & " Definitions"
continue:
strSQL1 = "CREATE TABLE [" & strQueryName & " Definitions]" & " (FieldName CHAR, SourceName CHAR);"
DoCmd.RunSQL (strSQL1)
DoCmd.SetWarnings False
For Each fld In rst.Fields
strField = fld.Name
strSource = fld.SourceTable
Debug.Print strValue
strSQL2 = "INSERT INTO [" & strQueryName & " Definitions]" & "(FieldName, SourceName) VALUES(""" & strField & """, """ & strSource & """);"
DoCmd.RunSQL (strSQL2)
Next fld
error1:
If Err.Number = 3265 Then
Resume continue
Else
MsgBox Err.Description
End If
DoCmd.SetWarnings True
Exit Sub
DoCmd.SetWarnings True
End Sub

ACCESS SQL Adding Multiple Columns If They Don't Exist

I'm trying to add several new columns to a table that already exists in Access 2007 or 2010 with a query. Right now my code looks like this (yes, I'm terribly new to this):
ALTER TABLE AC_PROPERTY
ADD JAB_1 double,
JAB_2 double,
JAB_3 double;
This correctly adds the three columns when none of them already exist, but if any of them exist I get an error message and the query fails to run. I need it to add each columns only if it does not exist. Can anyone please help with what my code should look like to check if each column exists before trying to add it?
NOTE: I would just do 3 queries for each column, but I actually need to add 20+ columns. This is just a simple example of my actual use.
Thanks a billion!
Here is some old code.... it would be better to just check all fields in the same subroutine rather than opening/closing the DB, TDF, etc.
Option Compare Database
Option Explicit
Function Check_If_Exists()
Dim strStatus As String
' Add calls for the fields you want to append
strStatus = Add_Field("MyFLd2", "Double")
If strStatus = "Exists" Then
Debug.Print "field present"
ElseIf strStatus = "Added" Then
Debug.Print "field added"
End If
End Function
Function Add_Field(strFN, strType) As String
Dim db As DAO.Database
Dim td As DAO.TableDef
Dim fd As DAO.Field
Dim strSQL As String
On Error GoTo Error_Trap
Set db = CurrentDb
Set td = db.TableDefs("Table1")
' ' List all field names
' For Each fd In td.Fields
' Debug.Print fd.Name
' Next fd
If IsNull(td.Fields(strFN)) Then
Add_Field = "Exists"
End If
Set fd = Nothing
Set td = Nothing
Set db = Nothing
Exit Function
Error_Trap:
Debug.Print Err.Number & vbTab & Err.Description
If Err.Number = 3265 Then
Add_Field = "Added"
strSQL = "alter table Table1 ADD " & strFN & " " & strType & ";"
db.Execute strSQL
db.TableDefs.Refresh
End If
Exit Function
Resume
End Function

Adding field to MS Access Table using VBA

I need to add a calculated field to an existing table. I am aware of two ways to do this and I'm wondering if anyone has any input on which is best and how to make them work:
Using TableDef.CreateField, then TableDef.Fields.Append
Using a DDL Alter Table ADD COLUMN statement
I tried using the first method, but I keep getting a 3211 error because Access could not lock the table. I don't have the table open. However, I am calling CreateField from a form that has accessed which fields currently exist in the table.
Here's the code for calling CreateField:
`
Public Sub AddFieldToTable(strTable As String, strField As String, nFieldType As Integer)
Dim db As DAO.Database
Dim tdf As DAO.TableDef
Dim fld As DAO.Field
On Error GoTo ErrorHandler
Set db = CurrentDb
Set tdf = db.TableDefs(strTable)
Set fld = tdf.CreateField(strField, nFieldType)
tdf.Fields.Append fld
MsgBox "The field named [" & strField & "] has been added to table [" & strTable & "]."
Set tdf = Nothing
Set db = Nothing
Exit Sub
ErrorHandler:
MsgBox "An error has occurred. Number: " & Err.Number & ", description: " & Err.Description
Exit Sub
End Sub
`
I get the error on the tdf.fields.append line. Would executing an ALTER TABLE statement be better? What are the tradeoffs?
You can use DDL to create fields:
Long:
CurrentDb.Execute "ALTER TABLE t ADD COLUMN a Long not null", dbFailOnError
(tack on NOT NULL IDENTITY(1,1) for an autonumber)
CurrentDb.Execute "ALTER TABLE t ADD COLUMN b text(100)", dbFailOnError
Boolean:
CurrentDb.Execute "ALTER TABLE t ADD COLUMN c Bit not null", dbFailOnError
DateTime:
CurrentDb.Execute "ALTER TABLE t ADD COLUMN d datetime null", dbFailOnError
Memo:
CurrentDb.Execute "ALTER TABLE t ADD COLUMN e memo null", dbFailOnError
Obviously, this lends itself well to functionalization, and you could just pass in your own eternal enum, combined with a Select, to construct the string and execute it:
Public Sub AddFieldToTable(TableName as string, FieldName as string, _
FieldType as Long, FieldLen as Long, FieldAllowsNull as Boolean)
Dim FieldText as String
Select Case(FieldType)
Case 0:
FieldText = "Long"
Case 1:
FieldText = "text(" & FieldLen & ")"
Case 2:
FieldText = "bit"
Case 3:
FieldText = "datetime"
Case 4:
FieldText = "memo"
End Select
Dim Sql as string
Sql = "ALTER TABLE " & TableName & " ADD COLUMN " & FieldName & " " & FieldText
If FieldAllowsNull then
Sql = Sql & " NULL"
Else
Sql = Sql & " NOT NULL"
End If
CurrentDb.Execute Sql, dbFailOnError
End Sub
I got the code working with either the CreateField or the ALTER TABLE statement. The key here was that I had used a recordset to access the table's data (I needed to check whether the field already existed and/or contained data before I ran the AddField method). I moved the rst.close statement up to before I edited the table structure and it worked! No more 3211.
`
Set db = CurrentDb
Set rst = db.OpenRecordset(strTable)
bFieldExists = Field_Exists(rst, strOutputField) ' Custom field_exists in table function
If bFieldExists then nFieldType = rst(strOutputField).Type
If CheckFieldHasValues(strTable, strOutputField) = True Then ' custom CheckField function
If MsgBox("The output field has values in it. Proceed?", vbYesNo) = vbNo Then Exit Sub
End If
rst.Close ' Recordset must release the table data before we can alter the table!
If bFieldExists = False Then
AddFieldToTable strTable, strOutputField, dbCurrency
End If
Set db = Nothing
I just did the following in a module and it works fine
Sub AddTableFields()
Dim db As DAO.Database
Dim t As DAO.TableDef
Dim f As DAO.Field
Set db = CurrentDb
Set t = db.TableDefs("tl_LongTermStat")
Dim intY As Integer
Dim intQ As Integer
For intY = 2012 To 2018
For intQ = 1 To 4
Set f = t.CreateField("Y" & intY & "Q" & intQ, dbText, 10)
t.Fields.Append f
Next
Next
Debug.Print "AddTableFields() done"
End Sub

Check if access table exists

I want to log web site visits' IP, datetime, client and refferer data to access database but I'm planning to log every days log data in separate tables in example logs for 06.06.2010 will be logged in 2010_06_06 named table. When date is changed I'll create a table named 2010_06_07. But the problem is if this table is already created.
Any suggestions how to check if table exists in Access?
You can use the hidden system table MSysObjects to check if a table exists:
If Not IsNull(DlookUp("Name","MSysObjects","Name='TableName'")) Then
'Table Exists
However, I agree that it is a very bad idea to create a new table every day.
EDIT: I should add that tables have a type 1, 4 or 6 and it is possible for other objects of a different type to have the same name as a table, so it would be better to say:
If Not IsNull(DlookUp("Name","MSysObjects","Name='TableName' And Type In (1,4,6)")) Then
'Table Exists
However, it is not possible to create a table with the same name as a query, so if you need a look up to test for a name, it may be best to add 5, that is query, to the Type list.
Here's another solution, will be a bit faster than looping over all of the tables.
Public Function doesTableExist(strTableName As String) As Boolean
Dim db As DAO.Database
Dim td As DAO.TableDef
Set db = CurrentDb
On Error Resume Next
Set td = db.TableDefs(strTableName)
doesTableExist = (Err.Number = 0)
Err.Clear
End Function
I tested various methods for finding out if a table exists several years ago. Here is the code for all of them as I implemented, including my simple test routine.
Public Function TableExists(strTableName As String, Optional ysnRefresh As Boolean, Optional db As DAO.Database) As Boolean
' Originally Based on Tony Toews function in TempTables.MDB, http://www.granite.ab.ca/access/temptables.htm
' Based on testing, when passed an existing database variable, this is the fastest
On Error GoTo errHandler
Dim tdf As DAO.TableDef
If db Is Nothing Then Set db = CurrentDb()
If ysnRefresh Then db.TableDefs.Refresh
Set tdf = db(strTableName)
TableExists = True
exitRoutine:
Set tdf = Nothing
Exit Function
errHandler:
Select Case Err.Number
Case 3265
TableExists = False
Case Else
MsgBox Err.Number & ": " & Err.Description, vbCritical, "Error in mdlBackup.TableExists()"
End Select
Resume exitRoutine
End Function
Public Function TableExists2(strTableName As String, Optional ysnRefresh As Boolean, Optional db As DAO.Database) As Boolean
On Error GoTo errHandler
Dim bolCleanupDB As Boolean
Dim tdf As DAO.TableDef
If db Is Nothing Then
Set db = CurrentDb()
bolCleanupDB = True
End If
If ysnRefresh Then db.TableDefs.Refresh
For Each tdf In db.TableDefs
If tdf.name = strTableName Then
TableExists2 = True
Exit For
End If
Next tdf
exitRoutine:
Set tdf = Nothing
If bolCleanupDB Then
Set db = Nothing
End If
Exit Function
errHandler:
MsgBox Err.Number & ": " & Err.Description, vbCritical, "Error in mdlBackup.TableExists1()"
Resume exitRoutine
End Function
Public Function TableExists3(strTableName As String, _
Optional db As DAO.Database) As Boolean
' Based on testing, when NOT passed an existing database variable, this is the fastest
On Error GoTo errHandler
Dim strSQL As String
Dim rs As DAO.Recordset
If db Is Nothing Then Set db = CurrentDb()
strSQL = "SELECT MSysObjects.Name FROM MSysObjects "
strSQL = strSQL & "WHERE MSysObjects.Name=" & Chr(34) & strTableName & Chr(34)
strSQL = strSQL & " AND MSysObjects.Type=6;"
Set rs = db.OpenRecordset(strSQL)
TableExists3 = (rs.RecordCount <> 0)
exitRoutine:
If Not (rs Is Nothing) Then
rs.Close
Set rs = Nothing
End If
Exit Function
errHandler:
MsgBox Err.Number & ": " & Err.Description, vbCritical, _
"Error in TableExists1()"
Resume exitRoutine
End Function
Public Sub TestTableExists(strTableName As String, intLoopCount As Integer)
Dim dteStart As Date
Dim i As Integer
Dim bolResults As Boolean
dteStart = Now()
For i = 0 To intLoopCount
bolResults = TableExists(strTableName, , CurrentDB())
Next i
Debug.Print "TableExists (" & intLoopCount & "): " & Format(Now() - dteStart, "nn:ss")
dteStart = Now()
For i = 0 To intLoopCount
bolResults = TableExists2(strTableName, , CurrentDB())
Next i
Debug.Print "TableExists2 (" & intLoopCount & "): " & Format(Now() - dteStart, "nn:ss")
dteStart = Now()
For i = 0 To intLoopCount
bolResults = TableExists3(strTableName, CurrentDB())
Next i
Debug.Print "TableExists3 (" & intLoopCount & "): " & Format(Now() - dteStart, "nn:ss")
End Sub
I have found querying system tables or tabledefs to be unreliable and introduce unpredictable behaviour in scripts where tables get regularly created and dropped.
Based on my results, my hypothesis is that these tables aren't necessarily updated at the exact instant a CREATE or DROP is executed, or that concurrency issues are preventing me from getting an accurate result.
I've found the following method to be more reliable:
Public Function TableExists(theDatabase As Access.Application, _
tableName As String) As Boolean
' Presume that table does not exist.
TableExists = False
' Define iterator to query the object model.
Dim iTable As Integer
' Loop through object catalogue and compare with search term.
For iTable = 0 To theDatabase.CurrentData.AllTables.Count - 1
If theDatabase.CurrentData.AllTables(iTable).Name = tableName Then
TableExists = True
Exit Function
End If
Next iTable
End Function
There should be no runtime issue iterating unless there is an staggeringly enormous collection of tables.
This question is quite old but I found that no answer is satisfying, because:
they do not handle the case of "bad" linked tables, where the linked table points to a non existing db or table.
since linked tables are potentially huge, we must be able to check them with a fast query.
So here is my simple but more complete solution:
Function isTableOk(tblName As String) As Boolean
'works with local or linked tables
Dim db As DAO.Database, rs As DAO.Recordset
Dim sSql As String
sSql = "SELECT TOP 1 ""xxx"" AS Expr1 FROM [" & tblName & "]"
On Error Resume Next
Err.Clear
Set db = CurrentDb
Set rs = db.OpenRecordset(sSql)
isTableOk = (Err.Number = 0)
rs.Close
End Function
You can even check table in an external Access db with this version:
Function isTableOk(tblName As String, Optional dbName As String) As Boolean
'works with local or linked tables, or even tables in external db (if dbName is provided)
Dim db As DAO.Database, rs As DAO.Recordset
Dim sSql As String
Set db = CurrentDb
sSql = "SELECT TOP 1 'xxx' AS Expr1 FROM [" & tblName & "]"
If Len(dbName) > 0 Then 'external db
sSql = sSql & " IN '" & dbName & "'"
End If
Err.Clear
On Error Resume Next
Set rs = db.OpenRecordset(sSql)
isTableOk = (Err.Number = 0)
rs.Close
End Function

How can i call the below function to populate my access form list control

I need to populate the access form list box from a access table.
Below is the code which I copy-pasted on button click event:
Public Sub PopulateLBWithData(DBPath As String, _
TableName As String, FieldName As String, _
oListControl As Object,Optional Distinct As Boolean = False, _
Optional OrderBy As String)
''#PURPOSE: Populate a list box, combo box
''#or control with similar interface with data
''#from one field in a Access Database table
''#Parameters: DBPath: FullPath to Database
''#TableName: The Name of the Table
''#FieldName: Name of the Field
''#Distinct: Optional -- True if you want distinct value
''#Order By: Optional -- Field to Order Results by
''#Must have reference to DAO in your project
Dim sSQL As String
Dim db As DAO.Database
Dim rs As DAO.Recordset
Dim td As DAO.TableDef
Dim f As DAO.Field
Dim sTest As String
Dim bIsNumeric As Boolean
Dim i As Integer
On Error Resume Next
''#validate all parameters
oListControl.AddItem "a"
oListControl.Clear
If Err.Number > 0 Then Exit Sub
sTest = Dir(DBPath)
If sTest = "" Then Exit Sub
Set db = Workspaces(0).OpenDatabase(DBPath)
If Err.Number > 0 Then Exit Sub
Set td = db.TableDefs(TableName)
If Err.Number > 0 Then
db.Close
Exit Sub
End If
Set f = td.Fields(FieldName)
If Err.Number > 0 Then
db.Close
Exit Sub
End If
If Len(OrderBy) Then
Set f = td.Fields(OrderBy)
If Err.Number > 0 Then
db.Close
Exit Sub
End If
End If
sSQL = "SELECT "
If Distinct Then sSQL = sSQL & "DISTINCT "
sSQL = sSQL & "[" & FieldName & "] FROM [" & TableName & "]"
If OrderBy <> "" Then sSQL = sSQL & " ORDER BY " & OrderBy
Set rs = db.OpenRecordSet(sSQL, dbOpenForwardOnly)
With rs
Do While Not .EOF
oListControl.AddItem rs(FieldName)
.MoveNext
Loop
.Close
End With
db.Close
End Sub
But this function need arguments according to the VBA conventions.
Please help me how i can call this function to populate my vba form list box from the same access table?
That code is overly complex for what you're probably trying to do.
Why not try to just set the control's row source and then requery.
If you want to retain the parameterization, then pass in the SQL.
Dim strSQL As String
strSQL = "SELECT MyField FROM MyTable;"
Me.lstMyListBox.RowSource = strSQL
Me.lstMyListBox.Requery