VerySimple Developer Blog
Technical Tips, Tricks and Rants.

Archive for October, 2006

 
Oct
23
Filed Under (Javascript) by Jason on 23-10-2006

If you happen to work with a designer that uses DreamWeaver, you’ve probably seen mm_menu.js. It the “engine” file that DreamWeaver includes to make dropdown menus work. One obnoxious thing about this file is that the mouse cursor for FireFox still looks like an I bar instead of the normal hand/pointer icon.

If you’re like me, you have absolutely zero interest in debugging this javascript or any of the machine generated garbage that comes with it. Today is your lucky day because attached to this post is a replacement for mm_menu.js that you can just swap out with the old one and ba-da-bing, FireFox shows the cursor correctly. I read about this somewhere which gave me the idea and probably some of the code too. Unfortunately I forget where that is so I can’t link to the reference source. Anyway, if you care, do a diff on files if you want to see the difference, which is very minor.

Download mm_menu.js Patched for FireFox

 

 
Oct
22
Filed Under (MySQL) by Jason on 22-10-2006

MySQL Error Number 1005
Can’t create table ‘.\mydb\#sql-328_45.frm’ (errno: 150)

If you get this error while trying to create a foreign key, it can be pretty frustrating. The error about not being able to create a .frm file seems like it would be some kind of OS file permission error or something but this is not the case. This error has been reported as a bug on the MySQL developer list for ages, but it is actually just a misleading error message.

In every case this is due to something about the relationship that MySQL doesn’t like. Unfortunately it doesn’t specify what the exact issue is. Here is a running list of causes that people have reported for the dreaded errno 150. I’ve tried to put them in order based on the frequency that I hear about a particular cause.

You may want to start by running the MySQL command “SHOW INNODB STATUS” immediately after receiving the error. This command displays log info and error details. (Thanks Jonathan for the tip)

Known Causes:

  1. The two key fields type and/or size doesn’t match exactly. For example, if one is INT(10) the key field needs to be INT(10) as well and not INT(11) or TINYINT. You may want to confirm the field size using SHOW CREATE TABLE because Query Browser will sometimes visually show just INTEGER for both INT(10) and INT(11). You should also check that one is not SIGNED and the other is UNSIGNED. They both need to be exactly the same. (More about signed vs unsigned here).
  2. One of the key field that you are trying to reference does not have an index and/or is not a primary key. If one of the fields in the relationship is not a primary key, you must create an index for that field. (thanks to Venkatesh and Erichero and Terminally Incoherent for this tip)
  3. The foreign key name is a duplicate of an already existing key. Check that the name of your foreign key is unique within your database. Just add a few random characters to the end of your key name to test for this. (Thanks to Niels for this tip)
  4. One or both of your tables is a MyISAM table. In order to use foreign keys, the tables must both be InnoDB. (Actually, if both tables are MyISAM then you won’t get an error message - it just won’t create the key.) In Query Browser, you can specify the table type.
  5. You have specified a cascade ON DELETE SET NULL, but the relevant key field is set to NOT NULL.  You can fix this by either changing your cascade or setting the field to allow NULL values. (Thanks to Sammy and J Jammin)
  6. Make sure that the Charset and Collate options are the same both at the table level as well as individual field level for the key columns. (Thanks to FRR for this tip)
  7. You have a default value (ie default=0) on your foreign key column (Thanks to Omar for the tip)
  8. One of the fields in the relationship is part of a combination key & does not have it’s own index. You must create a separate index for the key field in order to use it in a constraint. (Thanks to Alex for this tip)
  9. You have a syntax error in your ALTER statement (Thanks to Christian for the tip)

The MySQL documentation includes a page explaining requirements for foreign keys. Though they don’t specifically indicate it, these are all potential causes of errno 150. If you still haven’t solved your problem you may want to check there for deeper technical explainations.If you run into this error and find that it’s caused by something else, please leave a comment and I’ll add it to the list.

 

 
Oct
22
Filed Under (Announcements, MySQL) by Jason on 22-10-2006

A lot of people might classify this as a “newbie” piece. But, I would say that I’ve seen inefficient data usage plenty of times due to just laziness. Some people might even say “premature optimization is the root of all evil” and just make field sizes way bigger than they need. I like to try to keep my DB lean-and-mean when possible, though. Everyone, myself included, gets lazy once in a while when cranking out some prototype code.

These tips are not necessarily MySQL specific, but I’m using MySQL syntax as an example for the sake of simplicity.

SIGNED vs UNSIGNED

This is probably the easiest habit to practice because you have nothing to lose and everything to gain by using SIGNED /UNSIGNED data types in the right place. There’s a simple question you can ask - will this field ever contain a negative value? If the answer is no, then you want an UNSIGNED data type.

A common mistake that I see is a primary key that is an auto-increment INT starting at zero, yet the type is SIGNED. An UNSIGNED INT can go from 0 to 4294967295 (aprox 4 billion). A SIGNED INT , on the other hand, starts at -2147483648 and goes to 2147483648 (aprox 2 billion). If you use an auto-increment starting at 0, you’ll never touch any of those negative numbers and your Id column will top out at around 2 billion.

Use a Smaller ID Data Type for Small Tables

You don’t usually know how many records your application will eventually store. You don’t want to use too small a data type in the event that your app gets popular. You might tend to use INT for your primary key column just out of habit.

In some situations, though, you do have a pretty good idea of how many records you’ll have and so you don’t need to use an INT as the primary key. For tables that contain things like permissions, system settings, account types, etc you may know that you will never need a huge number of records. You may not even create a user interface to access these tables or insert new records.

In cases like this, I find an UNSIGNED TINYINT is perfect. This will hold values from 0 to 255. Sometimes 255 is more than enough, but still gives you room to grow. You don’t want to do this if you expect to get close to this limit of course. If the data is strictly internal and there’s no user interface, the developer is usually in control of these records.

A lot of people use UNSIGNED INT as their primary key which as I mentioned earlier goes up to over 4 billion records. That is a lot of records! An UNSIGNED MEDIUMINT goes up to 16 million records which is still more than most tables need. An UNSIGNED SMALLINT goes up to 65 thousand records. You don’t want to run out of space, but you can try to optimize a bit for the best performance. Keep in mind that your primary key is the field that will be theoretically used the most for record lookups, so the more efficient you can make it the faster your app will run.

Read more about numerical datatypes on the MySQL site…

Know the Difference Between CHAR and VARCHAR

CHAR and VARCHAR both hold string/text data. However they work a little differently because CHAR is like a fixed-size array that is always the same size, where as VARCHAR will expand and contract to fit the data.

The CHAR type is useful if you have a good idea of the size that the field will be. A product code that is always 10 digits, for example, is a great candidate for a CHAR data type. The negative side of the CHAR type is that it will use up the disk space whether or not you need it. For example, if you have a CHAR(25) but your record only uses up 5 characters, MySQL will still use up all 25 on disk.

The VARCHAR is convenient because you can set it to 25, but if you only store 5 characters, the VARCHAR will only use the space it needs. Two tradeoffs for this convenience are performance and fragmentation. In particular, when you update a VARCHAR field, you might outgrow the space that was originally allocated. Your data becomes fragmented so that one record may be spread out across the DB file instead of in one nice, continuous chunk. So as time goes by, your access to the VARCHAR data will require more processing power.

MySQL provides a solution in the way of the OPTIMIZE TABLE statement. This basically will de-fragment your table VARCHAR fields. If you are using VARCHARS and doing a lot of updating, you should make sure to optimize the tables periodically. This is something that can be automated through cron or Scheduled Tasks so that you don’t forget.

In case you think it’s a simple choice, keep in mind that table size and performance are closely related. So, it follows that the VARCHAR would deliver performance benefits due to it being the smallest possible size. That would offset the performance benefit that the consistency of CHAR delivers. My rule of thumb is to use CHAR if you have a pretty good idea of the field size.

More reading is available on the MySQL site, however I find the user comments on the end of this page more interesting than the official documentation.

Don’t mix “SELECT * FROM” with TEXT/BLOB fields

TEXT and BLOB fields are extremently useful for storing large datatypes. File uploads, XML data, long text content, etc usually require one of these types of fields. Unfortunately, these fields can make a table grow large quickly. Storing binary file data for example can make a DB table enormous.

Depending on your application, this may just be the best way to store this type of data. However, if you don’t need this data on every query, then don’t select it. Here’s an example:

select * from file_upload where fu_owner_id = ‘1′

What we’re doing here is grabbing the full row contents for the file uploads, though we may not need all of that data. You can imagine that this query might select all of the files that have been uploaded by a specific user for us to show in a datagrid, dropdown list or whatever. The issue here is that you rarely need to have the *contents* of the files when you are displaying them in a list. If possible, this might be better:

select fu_id, fu_name from file_upload where fu_owner_id = ‘1′

In this case we have the primary key (fu_id) and the file name (fu_name). We can show this in the list Now, if the user wants to download or see the contents of the file, you can grab it using the primary key:

select fu_name, fu_data from file_upload where fu_id = ‘55′

An alternative approach is to use a dedicated table with only a primary key and the BLOB/TEXT field. Any other tables that need to store large values can simply have a reference to that table’s primary key instead. If you only do lookups from the dedicated table by it’s primary key, then you’ll avoid any full table scans.

Read The Fine Manual

The MySQL site has plenty of information describing the various data types. The little bit of time and effort that you put into tuning your database will pay off as your application grows.

More reading on MySQL 5 data types can be found here: http://dev.mysql.com/doc/refman/5.0/en/data-types.html

 

Close
  • Social Web

NOTE: Email is disabled

E-mail It