Roberto Perez wrote: > At 12:38 PM 11/11/04, Ian Skinner wrote: > >>I don't know how unique the url's are in your data, but if any of them >>are repeated you would probably want an "URL" table. > > Actually, that is correct. Some URL's may be good for more than 1 word. I > was just thinking of updating them manually one by one, but if there's a > better way, I'm all ears :-)
I'd say this is somewhere where the redundancy is harmless, and where going with just two tables would be just fine. However, I don't know the exact mechanics of your app, so I can't tell. But let's continue regardless... >>And your select query would become something like this: >>SELECT >> WORD, URL >>FROM >> WORD_TBL, URL_TBL,W ORD_URL_JOIN >>WHERE >> URL_TBL.ID = WORD_URL_JOIN.URL_ID >>AND WORD_URL_JOIN.WORD_ID = WORD_TBL.ID >>AND WORD_TBL.ID = <cfqueryparam value="#myWordVariable#" >>CFSQLTYPE="SQL_DB_CHAR"> > > I'm not familiar with joins, so these questions may sound silly, but here > they are: You probably know more than you realise. > - in the last line, shouldn't I have "WORD_TBL.WORD" instead of > "WORD_TBL.ID"? Because the user will type a word, that should match the > actual word stored under that record ID. You're right. > - I understand the use of #myWordVariable#, which would come from the text > entry field in the form used to search the dictionary. But, what would > "cfqueryparam" and "cfsqltype" do? In other words, are they required, or > just "good-to-have"? You should take a read of what it says on LiveDocs about CFQUERYPARAM. Its use isn't required, but it's a really good idea if you don't want to end up with serious amounts of egg on your face. The naive way of passing values into queries is just to dump them in verbatim, as follows: SELECT user_id FROM users WHERE user_name = '#FORM.username#' AND user_password = MD5('#FORM.password#') But there are serious problems with this. Firstly, it's harder for the DBMS to identify common patterns in the queries if you do this as it might not be able to identify common execution plans. If the DBMS does execution plan caching, building queries like this can damage potential performance. But YMMV, and this dependant on the internal mechanics of the DBMS as to whether it yields an improvement or not, but it rarely if ever degrades performance. Secondly, and more importantly, is the risk of SQL injection. Say somebody enters the following text into the "username" field of the form: blah';UPDATE users SET user_password = MD5('cracked') WHERE user_name = 'admin'; SELECT user_id FROM users WHERE user_name = 'admin which will change the admin password to 'cracked'. Or even subtler: admin';-- which will (depending on how the DBMS handles SQL errors) log the user in directly as the 'admin' user. Frightened? You should be. This is one of the most common website exploits out there. CFQUERYPARAM was specifically added because of this exploit. CFQUERYPARAM--I'm simplifying a bit here--ensures that any special characters are escaped so that SQL injections are defeated. It also validates that type of the data passed in in different ways such as whether it's numeric or a string, whether it's short enough to fit in the field (for text--memos and (var)char--fields: you need to specify a maximum length in the tag), and a host of other things. The above query, secured using CFQUERYPARAM would read something like: SELECT user_id FROM users WHERE user_name = <cfqueryparam value="#FORM.username#" cfsqltype="CF_SQL_VARCHAR" maxlength="32"> AND user_password = MD5(<cfqueryparam value="#FORM.password#" cfsqltype="CF_SQL_VARCHAR" maxlength="32">) The "cfsqltype" attributes specifies the type of the value. For instance, if you wanted to get all the articles by such-and-such an author, and you have the id that's associated with them, you'd do something like the following: SELECT article_id, title, body, date_posted FROM articles WHERE author_id = <cfqueryparam value="#URL.author_id#" cfsqltype="CF_SQL_INTEGER"> If you ended up passing in "fred" as the value of "URL.author_id", CFQUERYPARAM would catch this and throw an error (which is what you want). Really, read up on it and start using it. > - any reason why, in the "WHERE" statements, you used "URL_TBL.ID" first > (i.e., before the "=" sign), but then you used a similar expression > ("WORD_TBL.ID") second (i.e., after the "=" sign) in the next part? I'm going to rewrite Ian's code because I find using "id" as the name of primary keys confusing myself. The code is for MySQL, but should work just fine on Access. Here's the schema: CREATE TABLE words ( word_id INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY, word VARCHAR(64) NOT NULL, -- Because you'll be doing lookups on "word", and it must be unique. -- Relational purists would say that "word" itself should be the -- key, but I don't think anybody on this list fits into that -- category. ;-) UNIQUE (word) ); CREATE TABLE links ( link_id INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY, link VARCHAR(255) NOT NULL ); -- I'm not putting a primary key on this table because it doesn't need -- one. Seriously. This is a link table, after all, so if there's any -- primary key, it's word_id and link_id together. link_id isn't indexed -- because I doubt there's ever going to be a time you'll be doing -- lookups for all the words associated with a particular link. CREATE TABLE word_links ( word_id INTEGER NOT NULL, link_id INTEGER NOT NULL UNIQUE (word_id, link_id), -- For when you're looking up links from a word. Essential. INDEX (word_id) ); With this schema, the lookup query is: SELECT link FROM words AS w JOIN word_links AS wl ON w.word_id = wl.word_id JOIN links AS l ON wl.link_id = l.link_id WHERE w.word = <cfqueryparam value="#FORM.word#" maxlength="32" cfsqltype="CF_SQL_VARCHAR"> This looks up the word in the "words" table, getting its id. Then it joins to the "word_links" table to get the ids of all the links associated with that word through the "word_links" table. Finally, it joins to the "links" table on the link ids found, giving the links. End of lesson. :-) K. -- Keith Gaughan, Developer Digital Crew Ltd., Pembroke House, Pembroke Street, Cork, Ireland http://digital-crew.com/ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~| Special thanks to the CF Community Suite Gold Sponsor - CFHosting.net http://www.cfhosting.net Message: http://www.houseoffusion.com/lists.cfm/link=i:4:184129 Archives: http://www.houseoffusion.com/cf_lists/threads.cfm/4 Subscription: http://www.houseoffusion.com/lists.cfm/link=s:4 Unsubscribe: http://www.houseoffusion.com/cf_lists/unsubscribe.cfm?user=11502.10531.4 Donations & Support: http://www.houseoffusion.com/tiny.cfm/54