In considering a 1NF failure, no repeating groups of elements, what if you wanted to have a set limit on the number of a repeating group?
For example, you want a student to only have 3 phone numbers listed. No more. Would having a table as follows be considered a 1NF failure?
Student 1 Phone1 Phone2 Phone3 Sally 111-1111 222-2222 333-3333 John 555-5555 999-9999 NULL
You would be creating a limit. Is this acceptable, efficient database design?
Would it be better to put phone numbers in a separate table, as 1NF failures call for? How would you create the limit of 3 numbers per user if it were in separate table?
No, it is not normalized. You will have wasted space in your table when there are null values, and if you want to do things like search for a particular phone number, you’ll have to search all three columns. Instead use a separate table (StudentPhoneNumbers, for example) that stores them. If you want to limit it to three, use a trigger to prevent more than three per student.
1NF bans repeating lists in a row. Your design violates this, and so would the following design:
Student Phones 'John D' '555-5555, 666-6666, 777-7777' 'Sally S' '111-1111, 222-2222'
The following design would violate 2NF, because the only primary key is
Name, Phone, but the
Address attribute does not depend on the
Name Phone Address 'John D' '555-5555' '1 Square Village' 'John D' '666-6666' '1 Square Village' 'John D' '777-7777' '1 Square Village' 'Sally S' '111-1111' '999 Flash City' 'Sally S' '222-2222' '999 Flash City'
The next design would violate 3NF, because AreaName does not depend on Name, but only on Area:
Name Area Phone AreaName 'John D' '555' '5555' '111name' 'John D' '666' '6666' '666name' 'John D' '777' '7777' '777name' 'Sally S' '111' '1111' '111name' 'Sally S' '222' '2222' '222name'
Even if your design violates 1NF, it’s an excellent choice. The complexity of adding a
PhoneNumber table is hardly ever justified.
Think about how hard an update to a customer becomes if you conform to 1NF. The numbers would be in a separate table. So if someone submits a form with an updated list of phone numbers, how would you change the database? First you’d have to retrieve the existing list of numbers. Then you’d have to compare them to the submitted list. Then you’d have to delete or insert rows based on the difference. One heck of a complex solution.
If you stick to your solution, you can just update the three columns. The saved time can be spend on real features! Or even writing long answers on Stack Overflow.
Your relation variable (relvar) indeed violates 1NF but perhaps not for the reason you are expecting: it is the presence of the null that violates 1NF. If you think your relvar contains a repeating group, think again.
First normal form, or simply “normalized”, is the minimum requirement for the relational model. To quote Chris Date:
by definition, a null isn’t a value. It follows that: A “type” that
contains a null isn’t a type (because types contain values); A “tuple”
that contains a null isn’t a tuple (because tuples contain values); A
“relation” that contains a null isn’t a relation (because relations
contain tuples, and tuples don’t contain nulls). In fact, nulls
violate the most fundamental relational principle of all—viz., The
Information Principle. The net of all this is that if nulls are
present, then we’re certainly not talking about the relational model
(I don’t know what we are talking about, but it’s not the relational
model); the entire edifice crumbles, and all bets are off.
The point about repeating groups and 1NF is a tricky one to explain and I won’t try. Instead, I urge you to read Facts and Fallacies about First Normal Form, specifically the section “The ambiguity of Repeating Groups”.
Assuming the null was eliminated, the relvar would satisfy 1NF but note we would need further information (e.g. keys) to determine whether it would also satisfy higher normal forms.
user1122200, let’s supose that your database design grows. And you need to assign certain data to each phone number (like phone location: ‘house’, ‘work’, …). In this case you will longed a phone table. Also, supose that you need to find students from phone number (like pizza hut or taxis services when someone calls), it is more easy a query in a well normalized design that this query:
select * from students where Phone1 = '91112223' or Phone2 = '91112223' or Phone3 = '91112223'
How would you create the limit of 3 numbers per user if it were in separate table?
I assume a student may have zero, one two or three phone numbers.
If your SQL product supported Full SQL-92:
CREATE TABLE Students ( student_name VARCHAR(20) NOT NULL UNIQUE ); CREATE TABLE StudentPhonebook ( student_name VARCHAR(20) NOT NULL REFERENCES Students (student_name), phone_number CHAR(8) NOT NULL CHECK (phone_number LIKE '[0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]'), UNIQUE (student_name, phone_number) ); CREATE ASSERTION students_max_three_phone_numbers CHECK ( NOT EXISTS ( SELECT * FROM ( SELECT student_name, COUNT(*) AS tally FROM StudentPhonebook GROUP BY student_name ) AS DT1 WHERE tally > 3 ) );
MySQL does not support
CHECK of any flavour and no SQL product supports
CREATE ASSERTION so the above constraints must presumably be written using procedural code e.g. triggers.
Out of interest, if your SQL product supported row-level
CHECK constraints (as most do), one can use an
occurrence attribute with a
BETWEEN 1 AND 3 constraint then include this attribute in a key constraint e.g.
CREATE TABLE StudentPhonebook ( student_name VARCHAR(20) NOT NULL REFERENCES Students (student_name), phone_number CHAR(8) NOT NULL CHECK (phone_number LIKE '[0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]'), occurrence INTEGER DEFAULT 1 NOT NULL CHECK (occurrence BETWEEN 1 AND 3), UNIQUE (student_name, phone_number), UNIQUE (student_name, occurrence) );