Preventing Duplicate Key Entries
Tom Woodward
This article offers a simple user-defined function that will check for
duplicate key values at the time of data entry.

I like format files.  They're easy to create using the work surface in dBASE
IV, look attractive and are easy to use.  The only down side to them is that
they're not a program.  I can't, for example, write a duplication- checking
routine into them.  But I can get them to use a UDF that checks for
duplicates.

Let's assume that I'm an employer and I keep my employee records in (what
else?) dBASE IV.  Each record has a field called EmpNo in which I store the
employee number.  Hopefully, each employee will have a unique number, but, as
it happens, I'm a terrible typist and occasionally I put in the wrong number
when entering a new employee.  Unfortunately, that number may be one that has
already been assigned.  After such an occurrence, I'm stuck with two or more
employees with the same number, causing numerous calls from my payroll
department.  The solution is to check for the existence of that number while
I'm entering the new employees.

The next problem is that I don't want to write a data entry screen; I'd like
to stick with the format file I created.  So the solution is to write a
user-defined function (UDF) which I can incorporate into my format file.

My initial draft of the UDF looks like this (the file name for the function
should have the same name as the function itself):


* Chk4Dupl.PRG
FUNCTION Chk4Dupl
	SET ORDER TO EmpNo
	SEEK EmpNo

	IF EOF()
		SET ORDER TO
		RETURN .T.
	ELSE
		SET ORDER TO 
		RETURN .F.
	ENDIF
* EOF: Chk4Dupl.PRG

I compile this by typing "Compile Chk4Dup" from the dot prompt.
Now I use the MODIFY SCREEN command to edit the format file (or CREATE SCREEN
if I'm making a new one) and modify the field EmpNo by selecting Fields:
Modify field (or pressing the F5 key), and type in "Chk4Dupl()" as the Edit
options: Accept value when option.  Now I can save the screen and give it a
try.

When I try this out, I find that the function is, in fact, finding duplicates
and notifying me of this.  However, it also tells me that unique entries are
duplicates too!  This is because as soon as I enter a value into the format
screen, it is stored into the database (at least temporarily), even though it
has not yet been validated.  And if you think about it, where else could it
possibly be stored?

What I have to do is to temporarily delete the record we are currently
entering.  Now, don't panic!  When a record is deleted using the DELETE
command, it's really only flagged for deletion; the record still exists
within the database.  However, if SET DELETED is ON, the deleted record will
not appear in the database, and any attempt to find it will be unsuccessful.
(If SET DELETED has its default value of OFF, the record still appears as
normal, but the word Del appears in the status bar if SET STATUS is ON,
appears in the scoreboard if SET STATUS is OFF, and doesn't appear at all,
but is still flagged internally if SET STATUS and SET SCOREBOARD are both
OFF.) 

Another thing I overlooked in my first version was the fact that a SEEK will
move the record pointer.  We need to store the current position in the
database to a variable so that it can be returned to later.  The key field
value also needs to stored to a variable, so that after the record has been
DELETED,  the value can still be used for the SEEK.

Finally, the other additional steps return to the original position, RECALL
the record to clear the deletion flag and RELEASE the local variables.  Here
is the revised UDF:


* Program...: Chk4Dupl.PRG 
* Author....: Tom Woodward 
* Date......: July 10, 1989 
* Notes.....: UDF to check for duplicate entries.   
*             The DELETED option in dBASE IV must
*             be SET ON in order to work properly.
*             The default for SET DELETED is OFF.
FUNCTION Chk4Dupl.PRG
	m_Return = RECNO()
	m_Emp = EmpNo
	DELETE
	SET ORDER TO EmpNo
	SEEK m_Emp

   IF EOF()
		SET ORDER TO
		GO m_Return
		RECALL
		RELEASE m_Return, m_Emp
		RETURN .T.
	ELSE
		SET ORDER TO
		GO m_Return
		RECALL
		RELEASE m_Return, m_Emp
		RETURN .F.
	ENDIF
* EOF: Chk4Dupl.PRG

The UDF is now going to work the way it should, making duplicate record key
entries impossible. 	s
