Skip to content

DBAzine.com

Sections
Personal tools
You are here: Home » Of Interest » Articles of Interest » Keyword Searches
Seeking new owner for this high-traffic DBAzine.com site.
Tap into the potential of this DBA community to expand your business! Interested? Contact us today.
Who Are You?
I am a:
Mainframe True Believer
Distributed Fast-tracker

[ Results | Polls ]
Votes : 3558
 

Keyword Searches

by Joe Celko

Here is a short problem that you might like to play with. You are given a table with a document number and a keyword that someone extracted as descriptive of that document. This is the way that many professional organizations access journal articles. We can declare a simple version of this table.

CREATE TABLE Documents
(document_id INTEGER NOT NULL,
 key_word VARCHAR(25) NOT NULL,
 PRIMARY KEY (document_id, key_word));

Your assignment is to write a general searching query in SQL. You are given a list of words that the document must have and a list of words which the document must NOT have.

We need a table for the list of words which we want to find:

CREATE TABLE SearchList
(word VARCHAR(25) NOT NULL PRIMARY KEY);

And we need another table for the words that will exclude a document.

CREATE TABLE ExcludeList
(word VARCHAR(25) NOT NULL PRIMARY KEY);

Breaking the problem down into two parts, excluding a document is easy.

 CREATE TABLE ExcludeList
(word VARCHAR(25) NOT NULL PRIMARY KEY);

Breaking the problem down into two parts, excluding a document is easy.

 SELECT DISTINCT document_id
   FROM Documents AS D1
  WHERE NOT EXISTS
        (SELECT *
           FROM ExcludeList AS E1
          WHERE E1.word = D1.key_word);

This says that you want only the documents that have no matches in the excluded word list. You might want to make the WHERE clause in the subquery expression more general by using a LIKE predicate or similar expression, like this.

WHERE E1.word LIKE D1.key_word || '%'
   OR E1.word LIKE '%' || D1.key_word
   OR D1.key_word LIKE E1.word || '%'
   OR D1.key_word LIKE '%' || E1.word

This would give you a very forgiving matching criteria. That is not a good idea when you are excluding documents. When you wanted to get rid "Smith" is does not follow that you also wanted to get rid of "Smithsonian" as well.

For this example, Let you agree that equality is the right matching criteria, to keep the code simple.

Put that solution aside for a minute and move on to the other part of the problem; finding documents that have all the words you have in your search list.

The first attempt to combine both of these queries is:

 SELECT D1.document_id
   FROM Documents AS D1
  WHERE EXISTS
        (SELECT *
           FROM SearchList AS S1
          WHERE S1.word = D1.key_word);
    AND NOT EXISTS
        (SELECT *
           FROM ExcludeList AS E1
          WHERE E1.word = D1.key_word);

This answer is wrong. It will pick documents with any search word, not all search words. It does remove a document when it finds any of the exclude words. What do you do when a word is in both the search and the exclude lists? This predicate has made the decision that exclusion overrides the search list. The is probably reasonable, but it was not in the specifications. Another thing the specification did not tell us is what happens when a document has all the search words and some extras? Do we look only for an exact match, or can a document have more keywords?

Fortunately, the operation of picking the documents that contain all the search words is known as Relational Division. It was one of the original operators that Ted Codd proposed in his papers on relational database theory. Here is one way to code this operation in SQL.

 SELECT D1.document_id
   FROM Documents AS D1, SearchList AS S1
  WHERE D1.key_word = S1.word
  GROUP BY D1.document_id
 HAVING COUNT(D1.word)
        >= (SELECT COUNT(word) FROM SearchList);

What this does is map the search list to the document's key word list and if the search list is the same size as the mapping, you have a match. If you need a mental model of what is happening, imagine that a librarian is sticking Post-It notes on the documents that have each search word. When she has used all of the Post-It notes on one document, it is a match. If you want an exact match, change the >= to = in the HAVING clause.

Now we are ready to combine the two lists into one query. This will remove a document which contains any exclude word and accept a document with all (or more) of the search words.

 SELECT D1.document_id
   FROM Documents AS D1, SearchList AS S1
  WHERE D1.key_word = S1.word
    AND NOT EXISTS
        (SELECT *
           FROM ExcludeList AS E1
          WHERE E1.word = D1.key_word)
  GROUP BY D1.document_id
 HAVING COUNT(D1.word)
        >= (SELECT COUNT(word)
             FROM SearchList);

The trick is in seeing that there is an order of execution to the steps in process. If the exclude list is long, then this will filter out a lot of documents before doing the GROUP BY and the relational division.

--

Joe Celko was a member of the ANSI X3H2 Database Standards Committee and helped write the SQL-92 standards. He is the author of over 450 magazine columns and four books, the best known of which is SQL for Smarties (Morgan-Kaufmann Publishers, 1999). He is the Vice President of RDBMS at North Face Learning in Salt Lake City.


Contributors : Joe Celko
Last modified 2005-04-20 10:32 AM
Transaction Management
Reduce downtime and increase repeat sales by improving end-user experience.
Free White Paper
Database Recovery
Feeling the increased demands on data protection and storage requirements?
Download Free Report!
 
 

Powered by Plone