4.8 Finding Records with Duplicate Keys

 

An RMSfile may contain more than one record with the same key. Dfindnk(C-3) returns successive records with the same key as the previous dfindk or dfindnk call. When no more records exist with the same key, -1 is returned. This function is useful for retrieving details for a master record, where the key of the detail record is the same as the master record or the detail key is stored in the master record. To do this, your program first calls dfindk to retrieve the first detail record and successively calls dfindnk until -1 is returned. If, in the cycle of dfindnk calls, the program decides to start looking somewhere else (a new key) or to start at the beginning of the key again, just call dfindk may be used. Your program can read as many of the records with dfindnk as it needs up through the last record. The following example illustrates dfindnk by reading the subscriber file and finding all of the subscriptions for each subscriber.

#include <stdio.h>

#include <cbase/dtypes.h>

#include <cbase/dirio.h>

#include "sub.h"

#include "script.h"

#include "mag.h"

int linecount = 0;

main (argc, argv)

int argc;

char *argv[];

{

long recno;

char *magdescr;

if ((sub = dlopen ("sub", "r")) == NULL) {

puts (derrmsg());

fatal ("can't open subscriber file\n");

}

if (drlist (sublist, sub) < 0L) {

puts (derrmsg());

fatal ("bad field list for subscriber file\n");

}

if ((script = dlopen ("script", "r")) == NULL) {

puts (derrmsg());

fatal ("can't open subscription file\n");

}

if (drlist (scriptlist, script) < 0L) {

puts (derrmsg());

fatal ("bad field list for subscription file\n");

}

if ((mag = dlopen ("mag", "r")) == NULL) {

puts (derrmsg());

fatal ("can't open magazine file\n");

}

if (drlist (maglist, mag) < 0L) {

puts (derrmsg());

fatal ("bad field list for magazine file\n");

}

/* loop through the subscriber file */

recno = dfind (&subbuf, sub);

while (recno > 0L) {

/* process subscriber record */

do_master (&subbuf);

/* get first subscription record */

strncpy (scriptbuf.subscriber,

subbuf.subscriber,

sizeof (scriptbuf.subscriber));

recno = dfindk (&scriptbuf, script);

/* process each subscription record */

while (recno > 0L) {

/* look up title for magazine

subscription */

strncpy (magbuf.magazine,

scriptbuf.magazine,

sizeof (magbuf.magazine));

if (dfindk (&magbuf, mag) < 0)

magdescr = "No such magazine"

else

magdescr = magbuf.title;

/* process subscription record */

do_details (&scriptbuf, magdescr);

/* get next subscription */

recno = dfindnk (&scriptbuf, script);

}

/* get next subscriber record */

recno = dfindn (&subbuf, sub);

}

fatal(NULL);

}

do_master (sb)

SubRec *sb;

{

headings();

printf ("\n%s %-35s\n", sb->subscriber,

sb->name);

linecount += 2;;

footings();

}

do_details (sc, t)

ScriptRec *sc;

char *t;

{

headings();

printf ("%-14s%-36s %3d %s\n", "", t,

sc->issues, datetoa (sc->started));

linecount++;

footings();

}

headings()

{

if (linecount != 0)

return;

printf ("\n Subscription List\n");

printf ("Subscriber %-36s Issues Started\n\n", "Title");

linecount = 4;

}

footings()

{

if (linecount <= 63)

return;

printf ("\f");

linecount = 0;

}

fatal (p)

char *p;

{

if (p)

printf (p);

if (sub != NULL) {

dclose (sub);

sub = NULL;

}

if (script != NULL) {

dclose (script);

script = NULL;

}

if (mag != NULL) {

dclose (mag);

mag = NULL;

}

if (p)

exit (1);

else

exit (0);

}

Like sequential file reading, a program can read duplicate keys in reverse order: (last key to first). The function dfindlk(C-3) returns the last record matching the key contents stored in the user record. This function is analogous to the dfindk function. Like dfindk, the program must place the key values in the user record prior to calling dfindlk. The record returned matches the last record that would be returned by calling dfindk and repeatedly calling dfindnk. A sample call to dfindlk is:

recno = dfindlk (&scriptbuf, sfp);

When a key search pattern has been established with dfindk or dfindlk, the function dfindpk(C-3) can be used to find a previous matching record. Typically, dfindlk would be used to find the last matching record and dfindpk would be called repeatedly to find previous matching keys. When all of the matching keys have been found, dfindpk returns -1. A sample call to dfindpk is:

recno = dfindpk (&scriptbuf, sfp);

After calling dfindk or dfindlk, it is permissible to mix calls to dfindnk and dfindpk provided that the previous function call returned a valid record. If not, the functions return an error: "previous call canceled sequence". This error indicates that the functions dfindnk and dfindpk are reading a sequence of keyed records. When an error is encountered while reading records in this sequence (beginning of key values, end of key values, etc.), RMS abandons the current sequence. Your program must call dfindk or dfindlk to restart the sequence.