Extracting values in table using VBA code - ms-access

I'm looking for a LM500 in a table "tblEngineFamilies" with two columns. table has 6 rows
Col 1: EFID
Col 2: EF
Below is the code i'm working out
Public Sub TestTableSearch()
Dim rstTable As Recordset
Dim rst1 As Recordset
Set rstTable = CurrentDb.OpenRecordset("tblEngineFamilies")
rstTable.Filter = "EF = 'LM500'"
Error msg: Operation is not supported for this type of object.

For such simple tasks, use DLookup:
FoundEFID = DLookup("[EFID]", "tblEngineFamilies", "[EF] = 'LM500'")

Related

Invalid field Data type when use TableDef.CreateField Method VBA [duplicate]

This question already has answers here:
How do I create a decimal field in Access with Alter Table?
(3 answers)
Closed 6 years ago.
I am attempting to add a series of fields to a Access 2016 Table, but keep encountering the error:
Runtime error '3259'
Invalid field data type
I originally specified the data type as dbNumeric but changed it to dbDecimal to see if that made a difference. The solution given here for CreateFields did not solve my problem, though I did not try the SQL. It did not. Here is the code:
Sub BOD_Variables()
Dim myDBS
Dim myTable As TableDef
Dim myTableName As String
myTableName = "BOD_Data"
Set myDBS = CurrentDb
Set myTable = myDBS.TableDefs(myTableName)
Dim myField As Field
Dim myVariableNames As Variant
myVariableNames = Array("Blank_4_SampleVol", ... "BOD_ Concentration _OUT")
Dim iCount As Integer
For iCount = LBound(myVariableNames) To UBound(myVariableNames)
Debug.Print myVariableNames(iCount)
Set myField = myTable.CreateField(myVariableNames(iCount), dbDecimal) 'Originally specified dbNumeric for data type.
myTable.Fields.Append myField
Next
End Sub
I attempted to replace the call to the Array(index) with:
Set myField = myTable.CreateField("Blank_4_SampleVol", dbNumeric)
Still get the same error.
I tried specifying the length as discussed here, but that did not correct problem. Documentation on CreateField says it ignores field length when field type is dbNumberic.
Any ideas of what I am missing? Thanks in advance.
While it remains true that DAO does not seem to expose the required properties to create a Decimal field, even if we try using a DAO.Field2 object, the following ADOX code does create a Decimal field (tested with Access 2010):
Option Compare Database
Option Explicit
Sub AddNewDecimalField()
Dim cat As New ADOX.Catalog
cat.ActiveConnection = CurrentProject.Connection
Dim tbl As ADOX.Table
Set tbl = cat.Tables("MyTable")
Dim col As New ADOX.Column
col.Name = "MyNewDecimalField"
col.Type = adNumeric ' note: not adDecimal
col.Precision = 18
col.NumericScale = 8
tbl.Columns.Append col
End Sub
Or, we could just use a DDL query ...
CurrentProject.Connection.Execute _
"ALTER TABLE MyTable ADD COLUMN MyNewDecimalField DECIMAL(18,8)"
... as Andre suggests in his answer.
Follow the link in your linked question, and read the footnote #7 for DECIMAL:
[7] Not available in the Access query interface or DAO. Use ADO to Execute the DDL query statement.
So you'd use something like this:
strSQL = "ALTER TABLE myTable ADD COLUMN " & myVariableNames(iCount) & " DECIMAL (20,6);"
CurrentProject.Connection.Execute strSQL
Decimal fields have had (see comment below) their issues, see Avoid Using Decimal Field Size in Microsoft Access Tables
or http://allenbrowne.com/bug-08.html
Long or Double or Currency may be better options.

insert data to a record already saved to a table

I have a recordset that is missing one field for each record and would like to add some data from a form by looking up certain criteria. I used a select query to get data onto the form in the first place and tried to reverse the assigning of values but it doesnt work as it says Run-time error 3027 'the Database or Object is Read only.' I think this is because I ran a select query to get the information but how do I input the data to the same records. the code I used is below -
Private Sub CmdAppend_Click()
Dim dbsNorthwind As dao.Database
Dim rstAmend As dao.Recordset
Dim qdfAmend As dao.QueryDef
Dim n As Integer
Set dbsNorthwind = CurrentDb
Set qdfAmend = dbsNorthwind.QueryDefs("Get_Questions_NTL")
qdfAmend.Parameters(0) = [Forms]![TeamLeader]![ComClientNotFin]
qdfAmend.Parameters(1) = [Forms]![TeamLeader]![ComDateSelect]
Set rstAmend = qdfAmend.OpenRecordset(dbOpenDynaset)
n = 0
rstAmend.MoveFirst
Do Until rstAmend.EOF
n = n + 1
rstAmend.Fields("ManagerID") = Form.Controls("SC" & n).Value
rstAmend.MoveNext
Loop
End Sub
You'd have to use the .Edit and .Update methods of the recordset object to update records. You received the error because you are trying to assign a value to a read only property.

Exporting an array of custom objects into Access table

I have a timesheet system in excel with 3 rows (standard time, overtime, double time) for each of our (100+) employees, and one column for each cost code on the site. This ends up being a giant matrix, most of which is empty. My solution is to basically create an employee datatype which stores the employee information and hours for a single cost code.
Public Type Employee
Name As String
Trade(1 To 3) As String
EmpNum As Long
Comment As String
AddOns(1 To 3) As Single
Allowance(1 To 3) As Single
Contract As Long
CostCode As Long
STHours As Single
OTHours As Single
DTHours As Single
WorkDate As Date
End Type
I can process the spreadsheet and organize the information in excel as an array of employee-type objects, but I'm not familiar with how to export this into Access, and most questions relate to exporting from excel cells to Access. I can obviously put these objects into cells on another worksheet and do it that way, but it seems like there should be a better way.
Currently my best guess is something like this:
Insert data form Excel to Access 2010 using VBA
but then I'd be making 100+ updates to the table for each export.
Is there an efficient way to create a table object in VBA, populate it with the array information, and then append it to the end of my table in Access in a single update?
Thanks.
-Sean
The easiest way is to create a table link in Access. Table links look like tables in the rest of Access, but the data is stored externally. The data could be inside another Access database, or inside a SQL Server database, or what have you.
In particular, the data can be in an Excel spreadsheet. Define a table in Excel that contains the data in the format that's right for your Access application. Then build a table link in Access that links back to the table you defined in Excel.
When you update the Excel table, the updated results will automatically appear the next time you reference the table link in Access.
thanks for the help from everyone ... I just wanted to share what I came up with for a solution. I ended up building a function to insert one object into the database ... copied and modified from the interwebs. Code below, cheers!
Public Function InsertTimeRecord(EmpData As Employee) As Boolean
Dim SaveTime As Date
Dim db As DAO.Database
Dim rs As DAO.Recordset
'//Database Location
Const DB_LOCATION = "C:\access\KMP Tracker.mdb"
'//If errors occur the function will exit with a return value of false (insertion failed)
On Error GoTo ErrHandler:
'//Table has a datecreated/datemodified timestamp for each record
SaveTime = Now
'//Open Database
If db Is Nothing Then
Set db = DAO.Workspaces(0).OpenDatabase("C:\access\KMP Tracker.mdb") 'Removed DB_LOCATION
End If
'//Open Table
If rs Is Nothing Then
Set rs = db.OpenRecordset("Timesheet Data", dbOpenDynaset)
End If
'//Create a new record
With rs
.AddNew
![EmpName] = EmpData.Name
![Trade1] = EmpData.Trade(1)
![Trade2] = EmpData.Trade(2)
![Trade3] = EmpData.Trade(3)
![EmpNum] = EmpData.EmpNum
![Comment] = EmpData.Comment
![AddOns1] = EmpData.AddOns(1)
![AddOns2] = EmpData.AddOns(2)
![AddOns3] = EmpData.AddOns(3)
![Allowance1] = EmpData.Allowance(1)
![Allowance2] = EmpData.Allowance(2)
![Allowance3] = EmpData.Allowance(3)
![Contract] = EmpData.Contract
![CostCode] = EmpData.CostCode
![STHours] = EmpData.STHours
![OTHours] = EmpData.OTHours
![DTHours] = EmpData.DTHours
![WorkDate] = EmpData.WorkDate
![DateSubmitted] = SaveTime
'//Insert Record into Database
.Update
InsertMachineHoursRecord = True '//SUCCESSFUL INSERTION
End With
'//Note that we use recordset in this example, but equally effective
'// is to create an update query command text and simply run the update query:
'// (INSERT INTO Table (Field1, Field2) VALUES (Value1, Value2);
'//Make sure we have closed the database
My_Exit:
rs.Close
Set rs = Nothing
db.Close
Set db = Nothing
Exit Function
ErrHandler:
MsgBox Err.Description
Resume My_Exit
End Function

Jet (MS Access) Get seed and increment of COUNTER column

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

grab linq to sql record by primarykey without knowing its type

how can i grab a record (and eventually delete it) using linq2sql without knowing the type at compile time?
so far i've got
Sub Delete(ByVal RecordType As String, ByVal ID As Integer)
Dim dummy = Activator.CreateInstance(MyAssembly, RecordType).Unwrap
Dim tbl = GetTable(dummy.GetType)
tbl.DeleteOnSubmit(dummy)
End Sub
but of course the dummy is not an actual record, its just a dummy
i don't want to use direct sql (or executecommand) as there's business logic going on at deletion in the datacontext partial class
can this be done somehow?
thank you very much!
EDIT
in response to striplinwarior, i edited my code to:
Sub Delete(ByVal RecordType As ObjectType, ByVal ID As Integer)
Dim dummy = Activator.CreateInstance(ObjectType.Account.GetType.Assembly.FullName, RecordType.ToString).Unwrap
SetObjProperty(dummy, PrimaryKeyField(RecordType), ID)
Dim tbl = GetTable(dummy.GetType)
tbl.Attach(dummy)
tbl.DeleteOnSubmit(dummy)
SubmitChanges()
End Sub
this does fire off the deletion code correclty, but also seems to try to add the record first to the db, as i get a sqlexception that some "not null" fields are empty, which i guess is true about the dummy record, as the only thing this has is the primarykey, else is all empty. so i tried the other code u posted (something i anyways always wanted to have) and that works excellent!
hers my current code:
Function LoadRecord(ByVal RecordType As String, ByVal RecordID As Integer) As Object
Dim dummy = Activator.CreateInstance(AssemblyName, RecordType).Unwrap
Dim rowType = dummy.GetType
Dim eParam = Expression.Parameter(rowType, "e")
Dim idm = rowType.GetProperty(PrimaryKeyField(RecordType))
Dim lambda = Expression.Lambda(Expression.Equal(Expression.MakeMemberAccess(eParam, idm), Expression.Constant(RecordID)), eParam)
Dim firstMethod = GetType(Queryable).GetMethods().[Single](Function(m) m.Name = "Single" AndAlso m.GetParameters().Count() = 2).MakeGenericMethod(rowType)
Dim tbl = GetTable(rowType)
Dim obj = firstMethod.Invoke(Nothing, New Object() {tbl, lambda})
Return obj
End Function
Sub Delete(ByVal RecordType As String, ByVal RecordID As Integer)
Dim obj = LoadRecord(RecordType, RecordID)
Dim tbl = GetTable(obj.GetType)
tbl.DeleteOnSubmit(obj)
SubmitChanges()
End Sub
Thank You
The only way I can think of is to use the model information from your database mapping to figure out which member represents the primary key:
Dim primaryKey = (From t In db.Mapping.GetTables() _
Where t.RowType.Type = tableType _
Let keyMember = (From dm In t.RowType.DataMembers where dm.IsPrimaryKey).FirstOrDefault() _
Select keyMember.Member.Name).First()
(I'm using LinqPad here: I assume typical LINQ to SQL models have this mapping information available.)
Then use reflection to set the value of that key member on the dummy item you've created. After that, you need to attach the dummy to the table before trying to delete it, passing false as a second parameter to tell LINQ to SQL that you don't actually want to update the object using its current values, but that it should track changes from here on.
tbl.Attach(dummy, false)
tbl.DeleteOnSubmit(dummy)
db.SubmitChanges()
Does that make sense?
Edit
When you're only deleting an object, you don't necessarily have to get the record from the database. If you set the ID value of the object and then attach it to the context (as shown above), LINQ to SQL will treat it as if it were retrieved from the database. At that point, calling DeleteOnSubmit should tell the context to construct a DELETE statement in SQL based on that object's primary key value.
However, if you need to retrieve the object for some purpose other than deletion, you'll need to construct an expression to represent the query for that object. So, for example, if you were writing the query manually, you would say something like:
Dim obj = tbl.First(Function(e) e.Id = ID)
So to dynamically build the lambda expression inside the parentheses, you might do something like this:
Dim eParam = Expression.Parameter(rowType, "e")
Dim lambda = Expression.Lambda(Expression.Equal(Expression.MakeMemberAccess(eParam, idMember), Expression.Constant(ID)), eParam)
Then you would need to use reflection to invoke the generic First method:
Dim firstMethod = GetType(Queryable).GetMethods().[Single](Function(m) m.Name = "Single" AndAlso m.GetParameters().Count() = 2).MakeGenericMethod(rowType)
Dim obj = firstMethod.Invoke(Nothing, New Object() {tbl, lambda})