Part of a project I am working on is programmatically create a series of MDB files which will be the final deliverables. The specs from the client ask for some fields in the tables to have 3 decimal places.
I create the mdb files using python at first, and then to be able to alter the columns to DECIMAL (10,3), I use ADODB (C#, .NET 4.0) - as i couldn't find something suitable in Python. Problem though is that this will not be enough for the client. They want to open the MDB and in Design Mode to see the Decimal places for these fields to be set to 3. If they are not, the file is not accepted.
It has now taken me the best part of 3 days trying to come up with a solution. One likely candidate was DAO and the Field2 object but this object has so far eluded me. I am using the Microsoft DAO 3.6 Objects but Field2 doesn't seem to be part of the assembly. And dont even know if this will actually do what is required...
Does anyone know a way to do this? Don't care if its DAO, ADO, OLEDB or any other 3 letter acronym to be honest? How can I get from this:
to this - PROGRAMMATICALLY??
I can tell you how to do this in VBA/DAO, which may give you some ideas. You must append the property first:
sSQL = "create table testX (id counter, anumber decimal(10,3))"
CurrentProject.Connection.Execute sSQL
Dim db As Database
Dim tdf As TableDef
Dim fld As dao.Field
Set db = CurrentDb
Set tdf = db.TableDefs("testX")
Set fld = tdf.Fields("anumber")
'fld.Properties("DecimalPlaces") = 3
Set prp = fld.CreateProperty("DecimalPlaces", dbByte, 3)
tdf.Fields("anumber").Properties.Append prp
Public Sub DBF_SetDecPoints(ByVal DBNom As String, ByVal DBTab As String, ByVal Campo As String, ByVal NDEC As Short)
Dim DB As DAO.Database
Dim TD As DAO.TableDef
Dim FD As DAO.Field
Dim PP As DAO.Property
If DBF_Exists(DBNom, DBTab, Campo) Then
'Call DBF_SetProperty(DBNom$, DBTab$, Campo$, "Format", DaoText, "Fixed")
DB = DAOEngine.Workspaces(0).OpenDatabase(DBNom)
TD = DB.TableDefs(DBTab)
FD = TD.Fields(Campo)
PP = FD.CreateProperty("Format", DaoText, "Fixed")
On Error Resume Next
FD.Properties.Append(PP)
FD.Properties("Format").Value = "Fixed"
PP = FD.CreateProperty("DecimalPlaces", DaoByte, NDEC)
FD.Properties.Append(PP)
FD.Properties("DecimalPlaces").Value = NDEC
On Error GoTo 0
DB.Close()
End If
End Sub
Related
Okay I'm new here and will be my first question, I'm getting stuck with my code that I'm busy with.
I keep bumping same error which is related to the Runtime error: 3129 in Access Visual Basic
Now it is an importing button function set in a Public Sub check below my code:
Public Sub ImportDataTablebtn_Click()
Dim strSQL As String
Dim db As DAO.Database
Dim tbl1 As DAO.TableDef
Dim tbl2 As DAO.TableDef
Dim fld1tbl1 As DAO.Field
Dim fld2tbl1 As DAO.Field
Dim fld3tbl1 As DAO.Field
Dim fld1tbl2 As DAO.Field
Dim fld2tbl2 As DAO.Field
Dim fld3tbl2 As DAO.Field
Set db = CurrentDb
Set tbl1 = db.TableDefs("Clicks Returns")
Set tbl2 = db.TableDefs("Oct 2015 Clicks Returns")
Set fld1tbl1 = tbl1.Fields("SKU")
Set fld2tbl1 = tbl1.Fields("Item Description")
Set fld3tbl1 = tbl1.Fields("Oct 2015 FIN YTD TY % Returns")
Set fld1tbl2 = tbl2.Fields("Sku")
Set fld2tbl2 = tbl2.Fields("Item Description")
Set fld3tbl2 = tbl2.Fields("F21")
DoCmd.RunSQL strSQL
strSQL = "INSERT INTO tbl1 (fld1tbl1, fld2tbl1, fld3tbl1)" & _
"SELECT fld1tbl2, fld2tbl2 fld3tbl2 FROM tbl2;"
Set db = Nothing
End Sub
Now I know some of my lines like the table rename and field might not really be required, though I thought that it would just make it easier with the code...
Please let me know where I'm making my error as I feel like going crazy by keep looking at it over and over.
In the database is the tables that I'm referring towards and I want to move the certain fields from the one table to the other table as it is imported excel sheets, I'm busy making it an automated system where someone would just click on buttons on a form and it will sort out the data.
Thank you for taking the time to read this essay and also answering it
There are a couple of mistakes in your code.
First the code will not substitute your variables for the table/field names. So the string SELECT fld1tbl2 is not converted to SELECT [Item Description].
Second you are trying to run your query before you have built it.
Finally Access uses VBA and not VBScript. It's a different language. While they have much in common there are some important differences. Stick to the the VBA tag and you will be more likely to get the answers you need.
I've refactored your code. This version builds and then runs the query.
Public Sub ImportDataTablebtn_Click()
Dim strSQL As String
' Build Query.
strSQL = "INSERT INTO [Clicks Returns] " & _
"(SKU, [Item Description], [Oct 2015 FIN YTD TY % Returns]) " & _
"SELECT Sku, [Item Description], F21 FROM [Oct 2015 Clicks Returns];"
' Run Query.
DoCmd.RunSQL strSQL
End Sub
I actually have 2 questions:
1. How might I see who is using my Access database?
E.g: There is someone with an Access database opened and it created the .ldb file, I would like to see a list of who opened that database (it could be more than one person).
2. How might I see who is using a linked table?
E.g: I have 10 different Access databases, and all of them are using a same linked table. I would like to see who is using that linked table.
I don't even know if it's really possible, but I really appreciate your help!
For you information: The main problem is that lots of people use the same Access in the same network drive, so when I need to change it I have to kick them all out, but I never know who is actually using it.
Update: Rather than reading and parsing the .ldb/.lacdb file, a better approach would be to use the "User Roster" feature of the Access OLEDB provider as described in the Knowledge Base article
https://support.microsoft.com/en-us/kb/285822
and in the other SO question
Get contents of laccdb file through VBA
Original answer:
I put together the following a while ago. It looked promising but then I discovered that computers are not immediately removed from the lock file when they disconnect. Instead, Jet/ACE seems to (internally) mark them as inactive: If ComputerA disconnects and then ComputerB connects, ComputerB overwrites ComputerA's entry in the lock file.
Still, it does provide a list of sorts. I'm posting it here in case somebody can offer some suggestions for refinement.
I created two tables in my back-end database:
Table: [CurrentConnections]
computerName Text(255), Primary Key
Table: [ConnectionLog]
computerName Text(255), Primary Key
userName Text(255)
A VBA Module in my back-end database contained the following code to read (a copy of) the lock file and update the [CurrentConnections] table:
Public Sub GetCurrentlyConnectedMachines()
Dim cdb As DAO.Database, rst As DAO.Recordset
Dim fso As Object '' FileSystemObject
Dim lck As Object '' ADODB.Stream
Dim lockFileSpec As String, lockFileExt As String, tempFileSpec As String
Dim buffer() As Byte
Set cdb = CurrentDb
cdb.Execute "DELETE FROM CurrentConnections", dbFailOnError
Set rst = cdb.OpenRecordset("SELECT computerName FROM CurrentConnections", dbOpenDynaset)
lockFileSpec = Application.CurrentDb.Name
If Right(lockFileSpec, 6) = ".accdb" Then
lockFileExt = ".laccdb"
Else
lockFileExt = ".ldb"
End If
lockFileSpec = Left(lockFileSpec, InStrRev(lockFileSpec, ".", -1, vbBinaryCompare) - 1) & lockFileExt
'' ADODB.Stream cannot open the lock file in-place, so copy it to %TEMP%
Set fso = CreateObject("Scripting.FileSystemObject") '' New FileSystemObject
tempFileSpec = fso.GetSpecialFolder(2) & "\" & fso.GetTempName
fso.CopyFile lockFileSpec, tempFileSpec, True
Set lck = CreateObject("ADODB.Stream") '' New ADODB.Stream
lck.Type = 1 '' adTypeBinary
lck.Open
lck.LoadFromFile tempFileSpec
Do While Not lck.EOS
buffer = lck.Read(32)
rst.AddNew
rst!computerName = DecodeSZ(buffer)
rst.Update
buffer = lck.Read(32) '' skip accessUserId, (almost) always "Admin"
Loop
lck.Close
Set lck = Nothing
rst.Close
Set rst = Nothing
Set cdb = Nothing
fso.DeleteFile tempFileSpec
Set fso = Nothing
End Sub
Private Function DecodeSZ(buf() As Byte) As String
Dim b As Variant, rt As String
rt = ""
For Each b In buf
If b = 0 Then
Exit For '' null terminates the string
End If
rt = rt & Chr(b)
Next
DecodeSZ = rt
End Function
The following code in the Main_Menu form of the front-end database updated the [ConnectionLog] table
Private Sub Form_Load()
Dim cdb As DAO.Database, rst As DAO.Recordset
Dim wshNet As Object '' WshNetwork
Set wshNet = CreateObject("Wscript.Network")
Set cdb = CurrentDb
Set rst = cdb.OpenRecordset("SELECT * FROM ConnectionLog", dbOpenDynaset)
rst.FindFirst "ComputerName=""" & wshNet.computerName & """"
If rst.NoMatch Then
rst.AddNew
rst!computerName = wshNet.computerName
Else
rst.Edit
End If
rst!userName = wshNet.userName
rst.Update
Set wshNet = Nothing
End Sub
Finally, the following form in the back-end database listed [its best guess at] the current connections
It is a "continuous forms" form whose Record Source is
SELECT CurrentConnections.computerName, ConnectionLog.userName
FROM CurrentConnections LEFT JOIN ConnectionLog
ON CurrentConnections.computerName = ConnectionLog.computerName
ORDER BY ConnectionLog.userName;
and the code-behind is simply
Private Sub Form_Load()
UpdateFormData
End Sub
Private Sub cmdRefresh_Click()
UpdateFormData
End Sub
Private Sub UpdateFormData()
GetCurrentlyConnectedMachines
Me.Requery
End Sub
Easy. Open the .ldb file in notepad (or any text editor) and you can see the machine names.
RE: How might I see who is using my Access database?
•E.g: There is someone with an Access database opened and it created the .ldb file, I would like to see a list of who opened that database (it could be more than one person).
Just happened across this while looking for something else, and I thought I might share what I do for this. Note that this assumes that the host computer (the computer on which the database file resides) uses file sharing to provide access to the file.
You will need to be on the host computer, or have authority to connect to that machine.
click Start
right-click My Computer and select Manage
if you're not on the host computer, right-click 'Computer Management' and enter the host's name
Expand 'Shared Folders' and click on 'Open Files'
At the right is the list of currently open files with the username for each current user
I agree with Gord's Original answer. I used this code on my database, it seems that there is a way around computers not being taken out of CurrentConnections upon exit of the DB.
I placed this on my main menu form because it is always open until the user exits. I used the unload event on my form to get this to work, and it works awesome! Here is my code
p.s. Ignore SetWarnings I just have that on so the user doesn't have to click through prompts.
Private Sub Form_Unload(Cancel As Integer)
Dim wshNet As Object
Dim deleteSQL As String
Set wshNet = CreateObject("WScript.Network")
DoCmd.SetWarnings False
deleteSQL = "DELETE tblCurrentConnections.* " & _
"FROM tblCurrentConnections WHERE[computerName] = '" & wshNet.computerName & "';"
DoCmd.RunSQL deleteSQL
DoCmd.SetWarnings True
End Sub
Does anyone know how to retrieve the seed and increment of an auto increment field (aka COUNTER, or "AutoNumber" in the Access UI) in a Jet (MS Access) database?
I have enumerated all properties of the corresponding DAO.Field object, ADO.Field object, and ADOX.Column object, and have not been able to identify anything. Any method is acceptable, including whacky hacks of the MSys* tables or native method calls.
Background:
In Jet SQL, you can create an auto-incrementing column with a custom seed and increment with the DDL data type clause COUNTER(seed, increment), as in:
CREATE TABLE ODD_INCREMENTER (
ID_COL COUNTER(-52098, 42)
, TEXT_COL VARCHAR(50)
)
Which creates the following table (some data added for demonstration):
You can use ADOX
Dim cat As New ADOX.Catalog
Dim tbl As ADOX.Table
Dim col As ADOX.Column
Set cat.ActiveConnection = CurrentProject.Connection
Set tbl = cat.Tables("Table1")
Set col = tbl.Columns("AKey")
'Next autonumber
lngSeed = col.Properties("Seed")
Allen Browne has a fairly detailed reference: http://allenbrowne.com/ser-40.html
Consider using late binding instead of adding a reference.
You can get the increment in addition to the seed by inspecting its property.
Public Sub SeedAndIncrement(ByVal pTable As String, _
ByVal pAutonumField As String)
Dim cat As Object
Dim objColumn As Object
Set cat = CreateObject("ADOX.Catalog")
Set cat.ActiveConnection = CurrentProject.Connection
Set objColumn = cat.Tables(pTable).Columns(pAutonumField)
Debug.Print "Seed: " & objColumn.Properties("Seed")
Debug.Print "Increment: " & objColumn.Properties("Increment")
Set objColumn = Nothing
Set cat = Nothing
End Sub
So I navigated to the following MSDN Resource Page that addresses how to use ADO objects. My problem is that I cannot get it to work.
What I am trying to do is open a CSV file and read it line-by-line, then create SQL INSERT statements to insert the records into an existing Table in Access 2010. I have tried to find an easier method of doing this, but this appears to be my only option. doing this with the included tools, but so far, I haven't had any luck.
The main issue here is that I have CSV files with inconsistent headings. I want to import 5 files into the same table, but each file will be different depending on which fields contained data. Those fields with no data in them were ignored during the extract. This is why I can't use something like DoCmd.TransferText.
So, now I need to create a script that will open the text file, read the headers in the first line and create a SQL INSERT statement dependent on the configuration of that particular file.
I have a feeling that I have a good handle on how to appraoch the issue, but no matter what I try, I can't seem to get things working using ADO.
Could anyone explain how I can achieve this? My sticking point is getting the Access DB to receive information from the CSV files via ADO.
Instead of reading the CSV file line-by-line, then doing something with each line, I think you should open the file as an ADO recordset. And open a DAO recordset for your Access destination table.
You can then iterate through the fields in each row of the ADO recordset and add their values into a new row of the DAO recordset. As long as the destination table includes fields with the same names and compatible data types as the CSV fields, this can be fairly smooth.
Public Sub Addikt()
#If ProjectStatus = "DEV" Then
' needs reference for Microsoft ActiveX Data Objects
Dim cn As ADODB.Connection
Dim fld As ADODB.Field
Dim rs As ADODB.Recordset
Set cn = New ADODB.Connection
Set rs = New ADODB.Recordset
#Else ' assume PROD
Const adCmdText As Long = 1
Const adLockReadOnly As Long = 1
Const adOpenForwardOnly As Long = 0
Dim cn As Object
Dim fld As Object
Dim rs As Object
Set cn = CreateObject("ADODB.Connection")
Set rs = CreateObject("ADODB.Recordset")
#End If
Const cstrDestination As String = "tblMaster"
Const cstrFile As String = "temp.csv"
Const cstrFolder As String = "C:\share\Access"
Dim db As DAO.Database
Dim rsDao As DAO.Recordset
Dim strConnectionString As String
Dim strName As String
Dim strSelect As String
strConnectionString = "Provider=" & _
CurrentProject.Connection.Provider & _
";Data Source=" & cstrFolder & Chr(92) & _
";Extended Properties='text;HDR=YES;FMT=Delimited'"
'Debug.Print strConnectionString
cn.Open strConnectionString
strSelect = "SELECT * FROM " & cstrFile
rs.Open strSelect, cn, adOpenForwardOnly, _
adLockReadOnly, adCmdText
Set db = CurrentDb
Set rsDao = db.OpenRecordset(cstrDestination, _
dbOpenTable, dbAppendOnly + dbFailOnError)
Do While Not rs.EOF
rsDao.AddNew
For Each fld In rs.Fields
strName = fld.Name
rsDao.Fields(strName) = rs.Fields(strName).value
Next fld
rsDao.Update
rs.MoveNext
Loop
rsDao.Close
Set rsDao = Nothing
Set db = Nothing
rs.Close
Set rs = Nothing
cn.Close
Set cn = Nothing
End Sub
This is the Declarations section of the module where I saved that procedure:
Option Compare Database
Option Explicit
#Const ProjectStatus = "DEV" '"DEV" or "PROD"
Change the values for these constants to test it on your system:
Const cstrDestination As String = "tblMaster"
Const cstrFile As String = "temp.csv"
Const cstrFolder As String = "C:\share\Access"
Note the folder name does not include a trailing backslash.
You will want to adapt that procedure to pass the file name as a parameter instead of leaving it as a constant. And, if your CSV files are stored in more than one directory, you will want to pass the folder name as a parameter, too.
Hopefully showing you how to do it for one file will be enough and you can then take it from here to handle all your CSV files. Also consider adding error handling.
I think the previous answer is unnecessarily complicated. All you need to do it send an XMLHTTP request. That fetches the CSV data as a string, which can then be parsed into a table.
I've posted a VBA subroutine that does that in an answer to a similar question. Documentation of where I found the techniques is in the code comments. I've run it in Access 2019, and it works.
Summary: why might Docmd.Rename on a table result in tables that don't change name over a Link from another DB?
I'm trying to fix up an old database that needs TLC. Part of this is deleting lots of unused cruft, amongst which are some tables. The first part if a VBA procedure that calls DoCmd.Rename on these tables, renaming with DELETE_ prepended.
The "deletes" appear to work fine - but when I try to reference tables from another DB using the Linked Table manager, no renames have happened at all. If I go back and load that DB, the table names are changed.
Is it best to use TableDefs().Name to rename? Is that a better method? I'd assumed an "official" way like Rename would be better.
I'm using Access 2007 on Win7/64. The files are in MDB format.
Do you wish to rename the tables in the linked database? If so, you can use OpenDatabase to reference the linked Access database. You might try something on the lines of:
Dim dbLink As DAO.Database
Dim dbCurr As DAO.Database
Dim ws As DAO.Workspace
Dim rst As DAO.Recordset
Dim tdf As TableDef
Set ws = DBEngine.Workspaces(0)
Set dbCurr = CurrentDb
For Each tdf In dbCurr.TableDefs
sConn = tdf.Connect
sSource = tdf.SourceTableName
sName = tdf.Name
If InStr(sConn, ";DATABASE=") > 0 Then
strdb = Mid(sConn, InStr(sConn, ";DATABASE=") + 10)
If InStr(sConn, "PWD") > 0 Then
sPWD = Left(sConn, InStr(sConn, ";DATABASE="))
Else
sPWD = vbNullString
End If
If Dir(strdb) <> vbNullString Then
Set dbLink = ws.OpenDatabase(strdb, False, False, sPWD)
dbLink.TableDefs(sSource).Name = "DELETE_" & sSource
End If
End If
Next