24

I am trying to create a column in a table that's a foreign key, but in MySQL that's more difficult than it should be. It would require me to go back and make certain changes to an already-in-use table. So I wonder, how necessary is it for MySQL to be sure that a certain value is appropriate? Couldn't I just do that with a language like PHP, which I'm using to access this database anyway?

Similarly with NOT NULL. If I only access this database with PHP, couldn't I simply have PHP ensure that no null value is entered?

Why should I use MySQL to do enforce these constraints, when I could just do it with PHP?


I realize that NOT NULL is a very stupid part to neglect for the above reasons. But MySQL doesn't enforce foreign keys without a serious degree of monkeying around.

In your opinion, would it still be bad to use the "fake" foreign keys, and simply check if the values to be entered are matched in other tables, with PHP?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
stalepretzel
  • 15,543
  • 22
  • 76
  • 91
  • 1
    Trying to compensate in PHP leads to madness. MySQL is very, very tolerant of slipstreamed DDL changes. – dkretz Dec 19 '08 at 22:45

15 Answers15

56

You are going to make mistakes with PHP, 100% guaranteed. PHP is procedural. What you want are declarative constraints. You want to tell the entire stack: "These are the constraints on the data, and these constraints cannot be violated." You don't want to much around with "Step 1 ... Step 2 ... Step 3 ... Step 432 ...", as your method of enforcing constraints on data, because

  • you're going to get it wrong
  • when you change it later, you will forget what you did now
  • nobody else will know all of these implicit constraints like you know them now, and that includes your future self
  • it takes a lot of code to enforce constraints properly and all the time - the database server has this code already, but are you prepared to write it?

The question should actually be worded, "Why should I use PHP to enforce these constraints, when I could just do it with MySQL?"

Adam Bellaire
  • 108,003
  • 19
  • 148
  • 163
yfeldblum
  • 65,165
  • 12
  • 129
  • 169
  • 1
    How do you interpret the exception thrown into a meaningful error message (without adding more code than you gained in the first place)? And do you disbelieve in validation in the client? – dkretz Dec 19 '08 at 22:02
  • 3
    That's not what this is about, validate all you want, but if you've forgotten anything you're not risking the integrity of your data, just a crappy error for the user. – ninesided Dec 19 '08 at 22:50
  • 1
    Security is implemented in layers, and so should data integrity. client validation can be bypassed, then the PHP should validate that. Database integrity constraints should catch the rest. – Calyth Dec 19 '08 at 23:21
  • 4
    If you want to catch errors, figure out what's wrong, and display meaningful information to the user, then by all means *also* implement checking and validation in PHP. But if that's the *only* place you implement it, you *will* get it wrong. – yfeldblum Dec 20 '08 at 14:37
  • 1
    I disagree to some extent, you should have the constraints in BOTH locations. In code and in the database, but another reason to put the constraints in the DB is because you may have another application some day that goes against the same database, one that you may not trust entirely. – justin.m.chase Dec 22 '08 at 06:10
  • Yes, you should have constraints in the database, as well as write your code to enforce these constraints at the application level too, over and above their being enforced by the database server. But, you will get the code-level enforcement wrong, because it is hard; so put constraints in your db. – yfeldblum Jan 03 '09 at 12:27
18

You can't "just" do it with PHP for the same reason that programmers "just" can't write bug-free code. It's harder than you think. Especially if you think it's not that hard.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
recursive
  • 83,943
  • 34
  • 151
  • 241
  • Well .. if you cannot write down some bullet-proof insert\update code, you should concentrate on gaming software instead of database-management programs – Philippe Grondier Dec 19 '08 at 22:45
  • 14
    Gaming software is probably harder. In any case, I'll be out of a job as soon as the world discovers Philippe Grondier's bug-free code. – recursive Dec 19 '08 at 23:02
8

If you can swear for the life of you that nothing will ever access the DB though any other means then your (of course bug-free) PHP page, then doing it with PHP alone will be fine.

Since real-world scenarios always contain some uncertainty, it is good to have the DB server watching the integrity of your data.

For simple databases, referential integrity constraints might not be an absolute requirement, but a nice-to-have. The more complex the application gets, the more benefit can you draw from them. Planning them in early makes your life easier later.

Additionally, referential integrity does it's part in forcing you to design the database in a more by-the-book manner, because not every dirty hack is possible anymore. This is also a good thing.

Tomalak
  • 332,285
  • 67
  • 532
  • 628
2

They are quite important. You don't want to define your model entirely through PHP. What if there is a bug in your PHP code? You could easily have null'ed columns where your business rules state you should not. By defining it at the database level, you at least get that check for free. You're going to really hate it when there are bugs in your PHP or if any other tool ever uses your database. You're just asking for problem, IMHO.

Be advised, this is the very short version of the story.

BobbyShaftoe
  • 28,337
  • 7
  • 52
  • 74
2

It's important to implement constraints in the database because it's impossible to predict the future! You just never know when your requirements will change.

Also consider the possibility that you may have multiple developers working on the same application. You may know what all the constraints are, but a junior developer may not. With constraints on the database, the junior developer's code will generate an error, and he'll know that something needs to be fixed. Without the constraints, the code may not fail, and the data could get corrupt.

George Mastros
  • 24,112
  • 4
  • 51
  • 59
2

I'm usually in favor of declaring constraints in the database. Arguments for constraints:

  • Declarative code is easier to make bug-free than Imperative code. Constraints are enforced even if app code contains bugs.
  • Supports the "Don't Repeat Yourself" principle, if you have multiple applications or code modules accessing the same database and you need business rules to be enforced uniformly. If you need to change the constraint, you can do it in one place, even if you have many apps.
  • Enforces data integrity even when people try to bypass the application, using ad hoc query tools to tinker with the database.
  • Enforces consistency which means that you can always be certain the data is in a valid state before and after any data update. If you don't use constraints, you may need to run periodic queries to check for broken references and clean them up.
  • You can model cascading update/delete easily with constraints. Doing the same thing in application code is complex and inefficient, cannot apply changes atomically (though using transaction isolation is recommended), and is susceptible to bugs.
  • Constraints help databases be more self-documenting, just as column names and SQL data types help.

Arguments against constraints:

  • More complex business rules cannot be modeled by declarative constraints, so you have to implement some in application space anyway. Given that, why not implement all business rules in one place (your app) and in the same language? This makes it easier to debug, test, and track code revisions.
  • Constraints often involve indexes, which incur some amount of overhead during inserts/updates. On the other hand, even if you don't declare a constraint, you probably need an index anyway, because the column may be used in search criteria or join conditions frequently.
  • Constraints can complicate your attempts to "clean up" mistakes in the data.
  • In your current project, the incompatibility of MyISAM vs. InnoDB with respect to referential constraints is causing some grief.
Bill Karwin
  • 538,548
  • 86
  • 673
  • 828
1

Even if your PHP code is perfectly bug-free, it may stop mid-script (out of memory error, segfault in some library, etc), leaving half-inserted stuff in the database, hence the importance of using InnoDB and transactions.

Same for constraints, of course you should have proper form validation, and database constraints behind it to catch bugs.

Database constraints are easy to specify, finding bugs in the application is hard, and even harder without constraints.

My experience has been that improperly constrained databases, and anything that uses MyISAM, WILL have inconssitent data after a few months of use, and it is very hard to find where it came from.

bobflux
  • 11,123
  • 3
  • 27
  • 27
  • +1 Exactly right! Furthermore when you end up writing a script to repair the damage, it can inadvertently do MORE damage without the protection of constraints. – Dave Jan 08 '14 at 18:08
1

The most important thing about using NOT NULL to me, is more the documentation part. When I return to the project after a few months I forget which columns it is acceptable to have nulls in. If the column says NOT NULL, then I know I will never ever have to deal with potential null values from it. And if it allows null, then I know for sure I have to deal with them.

The other thing is, as others have noted: You may miss something somewhere, and cleaning up data sucks, or may be entirely impossible. It's better to know for sure that all data in your database is consistent.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Tom Jelen
  • 2,559
  • 1
  • 24
  • 23
1

Enabling these constraints in MySQL takes almost zero time. If they save you from even a single bug due to faulty PHP or other code, isn't that worth it?

Keep in mind that the sorts of bugs you'll save yourself from can be rather nasty. Finding and fixing the bug itself may not be hard; the nasty part is that once you've fixed the bug you'll be left with a bunch of faulty data that may not even be salvageable.

I wouldn't even approach this problem from the "well, something other than PHP might access your data someday" angle. That's true, but even more important in my mind are the the headaches, time (money) and data loss that you can save yourself simply by adding a few simple constraints.

John Rose
  • 1,943
  • 2
  • 18
  • 29
  • I've asked people how to enable foreign keys in MySQL, and it is rather difficult. Both tables must be InnoDB, which I am told is a bad idea. Also, the table that's being referenced has already been created, and is holding data. Can anyone help? – stalepretzel Dec 19 '08 at 22:06
  • 1
    Why is it a bad idea? Anyway, "ALTER TABLE tablename ENGINE=InnoDB" can convert a populated table from MyISAM to InnoDB. Although it takes time depending on the amount of data. – Bill Karwin Dec 19 '08 at 23:11
1

Use the database for structural data integrity, and use the BR layer for the rest. And catch errors as early as possible. They work together.

With luck, when your code as matured, you won't experience databse RI errors; and you can proudly announce yourself to be the first.

dkretz
  • 37,399
  • 13
  • 80
  • 138
1

Having your data tier enforce data consistency through constraints helps to ensure your data remains consistent and provides cheap runtime bug checking within your application.

If you think constraints are not worthwhile you either have a small/non mission critical system or you are passing up a huge opportunity to improve the quality of your system. This cannot be understated.

Choices include: choosing a different RDBMS, reinvent your own metadata system or manually manage constraints. Manual management in queries without a metadata system quickly becomes infeasible to maintain and audit properly as schema/system complexity grows and unecessarily complicates an evolving schema.

My recommendation is to choose a different RDBMS.

Consistency checking is much harder than you may think. For example MySQL uses transactional read consistency which means the values you are checking against may not be the same values in the scope of another transaction. Consistency scemantics for concurrent access are very very difficult to get right if not bound directly to the data tier.

When all is said and done, even with a modest amount of effort put into manual checking, the likely outcome is that one would still be able to drive a truck through the corner cases you have failed to consider or committed an error in forming.

On your NOT NULL question... The obvious data field requirements are a good starting point. Here are a couple of other things to consider when defining column nullability.

It provides a guarantee that can very helpful when writing queries. Various joins may use NULL conditions to show a non-match of a table row separate from a NULL value that cannot be assumed if the condition allows nulls. (If NULLs are allowed, a match can mean either the row did not match or the row did match but the column value is null.)

The use of NOT NULL also helps define the rules for simpler queries matching values. Since you cannot say "WHEN value1 = value2" if both value1 and value2 are NULL the result of the evaluation is still false.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Einstein
  • 4,450
  • 1
  • 23
  • 20
0

Implement default values and constraints at the database level; rules that will result in acceptable data to any consuming application. This insulates you from integrity issues.

Then, implement better default values and constraints at the application level. If you are prohibited technically (access to APIs external to the database) from implementing a constraint, or generating a default value at the database level, the application is where you'll need to do it. This will result in a better default value. However a segfault, or general failure of the application will not result in unacceptable data being persisted.

Dan Lugg
  • 20,192
  • 19
  • 110
  • 174
0
  1. I don't think you can be certain that your database will only be accessed by PHP and if so, by developers who will use it to respect those constraints for the entire lifecyle of your database.

  2. If you include these constraints in your schema, then one can get a good idea of how the data is used and related by investigating your schema. If you only put all that in the code, then someone would have to look in both the database and the PHP code.

But shouldn't that stuff be in the design documentation, data dictionary, and logical database design?

Yes, but these documents are notorious for getting out of date and stale. I know you would never allow that to happen, but some people who have experience with projects with less discipline may assume this about your project, and want to consult the actual code and schema rather than documentation.

JohnMcG
  • 8,709
  • 6
  • 42
  • 49
0

I highly appreciate your question, as I am deeply convinced that default-value rules should be implemented on the code-side, not on the database-side, and this for a very simple reason: when users are the one that initiate database changes (INSERTS, SELECTS and UPDATES), these changes shall integrate all business rules, and default values are basically business rules:

  • There is no invoice without invoice number
  • There is no invoice line without a quantity, and 0 or nulls are not acceptable
  • There is no incoming mail without date of reception
  • etc

We have decided a few years ago to get rid of all these "database-side" artefacts like "not null", "(do not) allow empty strings", and other "default value" tricks, and it works perfectly. Arguments in favor of the default value mainly refer to a kind of "security" principle ("do it on the database side because you will forget to to it on the code side / your language is not made for that/it's easier to do it on the database side") that does not make any sense once you have chosen not to implement any default value on the database side: just check that your business rules are properly implemented while debugging.

For the last 2 years, nobody in the team ever thought of declaring a default value in a table. I guess that our younger trainee does not even know about something that is called "default value".

EDIT: rereading some of the answers here, my final comment would be: do it on any side, either DB or code, but make your choice and do it on one side only! There is nothing more dangerous than having such controls on both sides, because eventually (1) you'll never know if both sides are really implementing the same rule, meaning that (2) checking the rules will mean checking both sides, which can really become a mess! The worst situation is of course when one part of the job is done on the database side (ie the rules that were identified when the database was created) and the other part (ie the newly identitified rules) done on the client side ... nightmare ....

Philippe Grondier
  • 10,900
  • 3
  • 33
  • 72
  • Unfortunately, your younger trainee is going to be sadly uninformed of typical operating procedures when he moves on to another employer. Another answer here mentioned; "*What if you plug another application into this database?*"; unless you can share **all** the necessary libraries, then you've goofed up. – Dan Lugg Mar 23 '13 at 13:36
0

I'm afraid this is a religious topic.

From a puristic point-of-view, you want the database to do the referential integrity. This is ideal when you have a multiplicity of applications accessing the database, because the constraints are in one place. Unfortunately, the real world is not ideal.

If you have to enforce some sort of referential integrity, in my experience, your application will need to know how to do this. This is regardless of whether it is the final arbiter, or the database checks it as well. And even if the database does do the referential integrity, then the application has to know what to do if the database rejects an update, because referential integrity would be violated...

As a sidenote, setting up MySQL to support foreign key constraints is a bit of a process because you need to shift to InnoDB. If you do just that, you can get a lot of performance back by setting innodb_flush_log_at_tx_commit to 1. But it probably would be better if you can instead re-engineer your site to be transaction-aware. Then you get two benefits of InnoDB.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
staticsan
  • 29,935
  • 4
  • 60
  • 73