How to avoid duplicate entries in access from vb6? - ms-access

I am using Vb6 and Access 2007. I am adding records to the access table name "subjectcode" from vb6. The details of subjectcode table are below.
Subjectcode table : Heading(Degree,Branch,Year1,Year2,Semester,Subjectcode,Subjectname,Theory_Practical, Major_Allied_Elective) values (Bsc,computerscience,2001,2004,1,RACS1,Vb6 programming,Theory,Major)
Note :The primary key in the above table is Degree,Branch,Year1,Year2,Semester,Subjectcode
And the code i used to add entry to the access table from vb6 are given below :
If degree = "" Or branch1 = "" Or year1 = "" Or year2 = "" Or semester = "" Or subcode.Text = "" Or subname.Text = "" Or theory.Text = "" Or major.Text = "" Then
MsgBox "Fields can't be empty ! All are mandatory!"
Else
rs.Open "select * from subjectcode", con, 1, 3
rs.AddNew
rs!degree = degree
rs!branch = branch1
rs!year1 = year1
rs!year2 = year2
rs!semester = semester
rs!Subjectcode = subcode.Text
rs!Subjectname = subname.Text
rs!Theory_Practical = theory.Text
rs!Major_Allied_Elective = major.Text
rs.Update
MsgBox "Successfully Saved !", vbOKOnly + vbInformation, "info"
rs.Close
End If
And the screenshot of that Add form of vb6 is here: http://tinypic.com/r/w7c7if/6
The record is added when the same entry is not exist. And if the record is already exist it should say "Record Already exists" and i don't know how to do that. Could you guys give me idea please.

Write a save method to save your record, and a method to query the database and check for an existing record before you save the data.
Public Sub SaveSubjectCode(ByVal vDegree As String, ByVal vBranch As String, ByVal vYear1 As Integer, ByVal vYear2 As Integer, ByVal vSemester As Integer, ByVal Subjectcode...)
If (DoesRecordExist(vDegree, vBranch, vYear1, vYear2, vSemester, vSubjectcode) = True Then
' Warn the user
MessageBox("I'm sorry Dave I can't do that. The record already exists.")
Else
' Save the record
End If
End Sub
Private Function DoesRecordExist(ByVal vDegree as String, ByVal vBranch As String, ByVal vYear1 As Integer, ByVal vYear2 As Long, ByVal vSemester As Integer, ByVal vSubjectcode As String) As Boolean
RecordSet = query 'Query the database for the existing record
If RecordSet.BOF And RecordSet.EOF Then
DoesRecordExist = False
Else
DoesRecordExist = True
End If
End Function
Also, you want to avoid the the Select * query that selects every record unless you really need it because it is likely to be slow, and get slower as the number of records grow. If you want to get a recordset just to use to add a new record you can include a Where clause that does not return any records "select * from subjectcode WHERE 1 = 2, con, adOpenKeyset, adLockOptimistic

When you create a field in a table you specify whether duplicates are allowed or not. Then an offending add or update raises an exception.
The SQL DML looks like:
ALTER TABLE tblCustomers
ADD CONSTRAINT CustomerNames UNIQUE
([Last Name], [First Name])
And there are other powerful features such as check contraints as well:
ALTER TABLE tblInvoices
ADD CONSTRAINT CheckAmount
CHECK (Amount > 0)
One advantage of constraints is that your database's integrity is not held hostage by rogue applications that may fail to do chatty pre-qualification queries (or fail to implement them properly). Another is the performance improvement over chatty techniques and the fact that when there are multiple updaters the database can change between pre-qual query and update.

Related

Access ComboBox binding and Selected Value

I am trying to create a simple form for entering data. I have two tables, jobs and reports. The report table refers to a job with a one to many (one job, many reports). When browsing through the reports I want the combo box that lists all of the jobs to show the corresponding job as the selected value. This is easy in a .NET environment, but I'm not understanding how to set this up in the property sheet for the combobox. My ComboBox record source is from a query:
SELECT Jobs.UID, Jobs.Projectcode, Jobs.Projectname, Jobs.Owner, Jobs.Contractor
FROM Jobs
ORDER BY Jobs.[Projectcode];
And the form is based on a query that joins the tables:
SELECT Report.ID, Report.ReportNumber, Report.ReportDate, Report.Temperature, Report.Weather, Report.Progress, Report.PeopleatOAC, Report.Trades, Jobs.UID, Jobs.Projectname, Jobs.Owner, Jobs.Contractor
FROM Report
INNER JOIN Jobs ON Jobs.UID = Report.JobID
UNION ALL SELECT Report.ID, Report.ReportNumber, Report.ReportDate,
Report.Temperature, Report.Weather, Report.Progress, Report.PeopleatOAC,
Report.Trades, Jobs.UID, Jobs.Projectname, Jobs.Owner, Jobs.Contractor
FROM Report
LEFT JOIN Jobs ON Jobs.UID = Report.JobID WHERE (((Report.JobID) Is Null))
ORDER BY Report.ID;
The way I have this set up, a report can have a null job field. So I want to be able to select a job from the combo box to update the report table AND I want the combo box to reflect the correct job if the current record has a jobID associated with it. Is this possible?
What I have implemented is a tie to the Current event for the form that sets the combobox value (or clears it) depending on the JobID. As well as an tied to the changed event for the combobox to update the database with a selection. This works well enough but VBA feels so limiting compared to C#, WPF, and MVVM.
For anyone stumbling accross this with a similar question here are the 2 VBA functions:
Private Sub Form_Current()
Dim JobID As Integer
Dim i As Integer
Dim TempVal As Variant
Dim TestVal As Integer
TempVal = Me.JobID.Value
If Me.JobID.Value <> Empty Then
JobID = Me.JobID.Value
Else: JobID = -2
End If
With Me.JobCodeCombo
If (JobID >= 0) Then
For i = 0 To .ListCount - 1
TestVal = .Column(0, i)
If .Column(0, i) = JobID Then
.Value = .ItemData(i)
Exit For
End If
Next
Else
Me.JobCodeCombo = Null
End If
End With
End Sub
Private Sub JobCodeCombo_Change()
Dim ReportID As Long
Dim JobID As Long
Dim dbs As DAO.Database
Dim qdfUpdateJobID As DAO.QueryDef
Dim CurrentRecord As Long
CurrentRecord = Me.CurrentRecord
Set dbs = CurrentDb
Set qdfUpdateJobID = dbs.QueryDefs("UpdateReportWithJobID")
ReportID = Me.ReportID.Value
JobID = Me.JobCodeCombo.Column(0)
qdfUpdateJobID.Parameters(0).Value = JobID
qdfUpdateJobID.Parameters(1).Value = ReportID
qdfUpdateJobID.Execute
qdfUpdateJobID.Close
DoCmd.Save acForm, "Form1"
Me.Requery
DoCmd.GoToRecord acDataForm, "Form1", acGoTo, CurrentRecord
End Sub
The query called from the second function is a simple update query in my access file that has two parameters:
PARAMETERS [P1] Long, [P2] Long;
UPDATE Report SET JobID = P1
WHERE [ID] = P2;

Access - custom "The changes you requested to the table were not succesfull..."

I have set Duplicates(No) to one of my table's ID. Then I created a form with Combobox to select all records from that table and to store selected record in a field of joined table (ID foreign key). If I try to add another record with same ID, Access prompts me with a warning message "The changes you requested to the table were not succesfull...etc).
What I want is to remove this message and create a custom one. Here is what I tried:
Private Sub Form_BeforeUpdate(Cancel As Integer)
Dim sWhere As String
sWhere = "[ID_Table]=" & Me.Combo5
'
If DCount("*", "Table1", sWhere) > 0 Then
Cancel = True
Me.Undo
MsgBox " Duplicate"
End If
End Sub
This code executes If I select whatever value from combobox. Here are my tables:
Table1
ID_Table(PK)
Field1
Field2 etc.
JoinTable
ID_Join(PK)
ID_Table1(FK)
ID_Table2(FK)
ID Field that has property Duplicates(No) is in Table1 (PK). How can I get this working correctly ?
I figured out, what a stupid mistake :
Instead of
If DCount("*", "Table1", sWhere) > 0 Then
I had to change to
If DCount("*", "JoinTable", sWhere) > 0 Then
Code didn't work because I was referencing to wrong Table. Problem solved, thanks for response Matt.

DLookup Or DMax To Find Value

I am sure this is fairly simple put I am having trouble getting started on this. I use a Form to invoice clients which includes the field [Billing_Month]. What I'm looking to accomplish is this. When I create a new invoice, the [Billing_Month] will look to the last invoice created (use [Invoice_#] with DMax?), and populate the value from that that invoices [Billing_Month]
I have thought to use: Billing_Month = DMax ("Billing_Month", "frmInvoices"), but this doesn't specifically get me the last invoice, it would just look for the highest Billing_Month, which is a text field.
I have thought to use: Billing_Month = DLookup ("Billing_Month", "frmInvoices"), But this doesn't get me the last invoice to pull from.
I'd use a custom function for this - assuming the underlying table is called tblInvoices:
Function GetBillingMonthOfLatestInvoice()
Const SQL = "SELECT TOP 1 Billing_Month FROM tblInvoices ORDER BY [Invoice_#] DESC"
Dim RS AS DAO.Recordset
Set RS = CurrentDb.OpenRecordset(SQL)
If RS.EOF Then
GetBillingMonthOfLatestInvoice = Null
Else
GetBillingMonthOfLatestInvoice = RS(0)
End If
End Function
Update
The above code can be generalised to return other related fields like so:
Function GetValueForLatestInvoice(FieldToLookUp As String)
Dim RS As DAO.Recordset, SQL As String
SQL = "SELECT TOP 1 " + FieldToLookUp + " FROM tblInvoices ORDER BY [Invoice_#] DESC"
Set RS = CurrentDb.OpenRecordset(SQL)
If RS.EOF Then
GetValueForLatestInvoice = Null
Else
GetValueForLatestInvoice = RS(0)
End If
End Function
To use, copy the code to a new standard module, then for each relevant text box on the form, set its Default Value property in the Properties window to something like this:
=GetValueForLatestInvoice("Billing_Month")
That would be for the text box holding the billing month value; for the one holding the billing year, you would use
=GetValueForLatestInvoice("Billing_Year")
You can use a combination of both DLookup() and DMax() like so:
DLookup("Billing_Month","tblInvoices","[Invoice_#]=" & DMax("[Invoice_#]","tblInvoices"))

Variable scope using VB in MS Access

I have not used VB much but as far as I can tell scope works the same as in C#. The problem is, I'm using VB in MS Access so I am unsure if the rules are a bit different (though I assume they are not). The following code shows values being assigned to variables which have only been declared within the function parameters. Specifically looking at PurchaseOrderID, I am unsure as to how it is retaining its assigned value for use in the function proceeding it.
Function Create(SupplierID As Long, EmployeeID As Long, OrderID As Long, PurchaseOrderID As Long) As Boolean
Dim rsw As New RecordsetWrapper
If rsw.OpenRecordset("Purchase Orders") Then
With rsw.Recordset
.AddNew
![Supplier ID] = SupplierID
If EmployeeID > 0 Then
![Created By] = EmployeeID
![Creation Date] = Now()
![Submitted By] = EmployeeID
![Submitted Date] = Now()
![Status ID] = Submitted_PurchaseOrder
End If
If OrderID > 0 Then
![Notes] = InsertString(PurchaseGeneratedBasedOnOrder, CStr(OrderID))
End If
If rsw.Update Then
.Bookmark = .LastModified
PurchaseOrderID = ![Purchase Order ID]
Create = True
End If
End With
End If
End Function
Function CreateLineItem(PurchaseOrderID As Long, ProductID As Long, UnitCost As Long, Quantity As Long) As Boolean
Dim rsw As New RecordsetWrapper
If rsw.OpenRecordset("Purchase Order Details") Then
With rsw.Recordset
.AddNew
![Purchase Order ID] = PurchaseOrderID
![Product ID] = ProductID
![Quantity] = Quantity
![Unit Cost] = UnitCost
CreateLineItem = rsw.Update
End With
End If
End Function
Can someone give me some insight on this?
In VBA, how you call a procedure can be important.
"Even if a called procedure has declared its parameters as ByRef, you
can force those to be ByVal by enclosing each argument within
parentheses."
-- http://www.cpearson.com/excel/byrefbyval.aspx
This is true of VBA in all Office applications. For example, let us say you have two procedures:
Sub SubByVal(ByVal Total As Integer)
Total = 50
End Sub
Sub SubByRef(ByRef Total As Integer)
Total = 50
End Sub
And you run a few tests:
Dim Total As Integer
Total = 100
These three versions work as expected and Total is equal to 100
Call SubByVal(Total)
SubByVal (Total)
SubByVal Total
These two work as expected and Total is equal to 50
Call SubByRef(Total)
SubByRef Total
However, in this version, in spite of calling ByRef, Total is equal to 100, because it is forced to ByVal by the parentheses.
SubByRef (Total)
Perhaps this is best handled by types. Something like this:
Type PurchaseSet
PurchaseOrderID As Long
OrderCreated as boolean
End Type
Function Create(SupplierID As Long, EmployeeID As Long, OrderID As Long ) As PurchaseSet
Dim rsw As New RecordsetWrapper
Dim ps as PurchaseSet
ps.OrderCreated = false
If rsw.OpenRecordset("Purchase Orders") Then
With rsw.Recordset
.AddNew
![Supplier ID] = SupplierID
If EmployeeID > 0 Then
![Created By] = EmployeeID
![Creation Date] = Now()
![Submitted By] = EmployeeID
![Submitted Date] = Now()
![Status ID] = Submitted_PurchaseOrder
End If
If OrderID > 0 Then
![Notes] = InsertString(PurchaseGeneratedBasedOnOrder, CStr(OrderID))
End If
If rsw.Update Then
.Bookmark = .LastModified
ps.PurchaseOrderID = ![Purchase Order ID]
ps.OrderCreated = True
End If
End With
End If
Create = ps
End Function
You can pass arguments to a VBA procedure by value or by reference. If you don't specify either ByVal or ByRef in the procedure's declaration, it defaults to ByRef. So the following two declarations are equivalent ...
Function DoSomething(PurchaseOrderID As Long) AS Boolean
Function DoSomething(ByRef PurchaseOrderID As Long) AS Boolean
The key here is that ByRef allows changes to the argument within the procedure to be transmitted back to the caller.

MS-Access - how to find the name of the composite primary key and associated fields

I have a bunch of Access databases, each of which has several tables. Each table has a composite primary key, with varying fields comprising that key.
How can I, for a given table, get the name of the primary key and the names of the fields which are used in it? I.e., what's the SQL to do that? (I need to use raw SQL rather than Visual Basic)
BTW I know that I can open the tables in the Access GUI and see the primary keys there but I need to automate the process so that I can modify the primary keys.
thanks!
max
Here is an Access VBA function that uses ADOX to get the primary Key columns.
Private Function getPrimaryKeyFields(ByRef strFieldNames() As String) As Integer
On Error GoTo HandleErr
Dim intReturn As Integer
'just get the primary key field here.
Dim idx As ADOX.Index
Dim Table As ADOX.Table
Dim col As ADOX.Column
Dim cat As New ADOX.Catalog
Set cat.ActiveConnection = CurrentProject.Connection
Set Table = cat.Tables(mTableName)
Set idx = Table.Indexes("PrimaryKey")
ReDim strFieldNames(idx.Columns.Count)
Dim intCount As Integer
intCount = 0
For Each col In idx.Columns
strFieldNames(intCount) = col.Name
intCount = intCount + 1
Next
intReturn = intCount
Set idx = Nothing
Set Table = Nothing
Set col = Nothing
Set cat = Nothing
ExitHere:
getPrimaryKeyFields = intReturn
Exit Function
HandleErr:
Select Case Err.Number
Case Else
'put some error handling here.
Resume ExitHere
End Select
' End Error handling block.
End Function
Pass in a string array and it is filled in with the field names. Number of fields is returned by the function. Hope this helps.
Seth
Have a look at the ADOX Object Model. Specifically the Key Object:
With the properties and collections of
a Key object, you can:
Identify the key with the Name property.
Determine whether the key is primary, foreign, or unique with the
Type property.
Access the database columns of the key with the Columns collection.
Specify the name of the related table with the RelatedTable property.
Determine the action performed on deletion or update of a primary key
with the DeleteRule and UpdateRule
properties.
Also, How to list the primary key columns in an Access table
Update: I think the question originally said programmatically: If you want to use TSQL then you need to query the Hidden System Tables