Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

Database Management Systems (DBMS)  Module 3 – Conceptual design with the ER model

ER model: entities, attributes and key attributes

Find entities and attributes in a written brief, tell composite, multivalued and derived attributes apart, pick keys and read Chen, crow's-foot and UML.

  • Beginner
  • 25 minutes
  • Examples run with sql.js 1.14.2
  • By MySmartCoPilot
Free: 25 minutes

What you will learn

  • Identify entities, entity sets and attributes in a written requirement
  • Classify attributes as simple or composite, single- or multivalued, stored or derived
  • Choose key attributes and explain why derived and multivalued attributes need special handling
  • Read the same design in Chen, crow's-foot and UML class notation

Before you start

What you’ll learn · Before you start

What you will learn

  • Identify entities, entity sets and attributes in a written requirement
  • Classify attributes as simple or composite, single- or multivalued, stored or derived
  • Choose key attributes and explain why derived and multivalued attributes need special handling
  • Read the same design in Chen, crow's-foot and UML class notation

Before you start

On this page

An entity is a thing a database must record and tell apart from every other thing of its kind: a resident of a hostel, a room, a booking. Things of one kind form an entity set, and the facts kept about each of them are its attributes, such as a resident’s name and date of birth. One attribute, or a few together, must pick out exactly one entity of the set; that is a key attribute, such as a roll number. The entity-relationship (ER) model, which Peter Chen proposed in his 1976 paper, describes data in these terms before any table exists, so a wrong decision costs a redrawn box rather than a data migration. This lesson reads a short brief, finds its entities and attributes, sorts the attributes into composite, multivalued and derived ones, picks the keys, and draws the result in the three notations you will meet in books, exams and design tools: Chen, crow’s foot and UML.

Entities and entity sets

Chen’s paper treats an entity as anything that can be distinctly identified: a particular person, company or event. Entities are grouped into entity sets such as EMPLOYEE or PROJECT, and every entity set comes with a test that says whether a given thing belongs to it. The paper also allows entity sets to overlap: a male person belongs both to MALE-PERSON and to PERSON, an idea the module returns to with specialisation.

Everyday usage is looser. People say “the Resident entity” when they mean the entity set, and the word “entity type” appears in many books for the same idea. What matters is the difference between the two levels: a box labelled RESIDENT stands for the set, and each resident, which becomes one row once the design is turned into tables, is an entity.

To find the entity sets in a written requirement, ask three questions of every noun in it:

  1. Does the brief ask you to record facts about it? A hostel has a name, a warden and facilities, so it is a candidate. A city in a home address has nothing recorded about it but its name, so it stays a value.
  2. Must its instances be told apart? Each room is a separate thing with its own beds; two rooms with the same number in different hostels are still two rooms.
  3. Does it take part in relationships? Residents are allotted to rooms, and rooms belong to hostels.

A noun that answers no to all three is an attribute value. Nouns that name the system itself, such as “the office” or “the college”, are usually its context rather than entities.

The hostel brief

The rest of this lesson, and its quiz, work from this brief:

Brief

A college runs several hostels. For each hostel the office records its name, its warden and the facilities it offers, such as a gym or a laundry. Each room has a number that is unique within its hostel, a floor and a number of beds. Each resident is a student with a roll number (the college’s student number), an email address, a first and a last name, a date of birth, one or more phone numbers and a home address made of a street, a city and a postcode. When a resident is allotted a room, the office records the date. Its reports show each resident’s age on the day of the allotment and the number of free beds in every room.

The three questions give three entity sets: HOSTEL, ROOM and RESIDENT. The allotment connects a resident to a room and carries a date, which makes it a relationship, the subject of the next lesson. The warden is a value here, because the brief records only a name; if it also asked for each warden’s phone number and duty hours, the warden would become an entity of its own. That is the general rule: something becomes an entity when the design needs to say more about it than one value.

Attributes, domains and NULL

An attribute gives each entity a value from a domain, the set of values it may take. Chen defined an attribute as a function from an entity set into a value set, or into a combination of value sets: in his example, NAME maps each person to a pair of values, one from FIRST-NAME and one from LAST-NAME. The domains you choose here become column types and CHECK constraints later. A floor might be a whole number from 0 to 20, a roll number text in a fixed pattern such as H24-017.

Sometimes an entity has no value for an attribute, for one of two different reasons:

  • not applicable: a resident with one phone has no second phone number;
  • unknown: a resident has a postcode, but it was left blank on the form.

SQL stores both as NULL and cannot tell them apart. When the difference matters, the design has to show it another way, and the multivalued phone attribute below is an example: once phone numbers sit in their own table, “no second phone” is simply a missing row.

Simple or composite, single- or multivalued, stored or derived

Every attribute answers three separate questions, and each answer changes how it is drawn and how it becomes columns.

Composite or simple? A composite attribute has parts that are used on their own. The brief needs the resident’s name in two parts, since lists are sorted by last name and letters greet the first name, and the address in three, since reports count residents per city. An attribute is composite only when the requirement uses its parts: a phone number has a country code inside it, yet it stays simple until something needs the code separately.

Single-valued or multivalued? A multivalued attribute holds several values for one entity at the same time, like a resident’s phone numbers or a hostel’s facilities. A value that changes over time is a different matter: a room’s number of beds can change, but at any moment there is one, so it is single-valued, and keeping its old values is a question of history, which the case study at the end of this module takes up.

Stored or derived? A derived attribute can be computed from other data. The full name comes from the first and last names, the age at allotment from the date of birth and the allotment date, and the free beds of a room from its beds and the residents allotted to it. Designs compute derived values instead of storing them, because a stored copy goes stale the moment one of its inputs changes. Store one only for a reason you can name: the computation is too slow to repeat, or the value records a moment that its inputs can no longer reproduce, such as the price charged on an old invoice after the list price has changed.

Attributes of the brief What kind they are
Roll number, email, date of birth, floor, number of beds, warden Simple, single-valued and stored
Name (first, last) and home address (street, city, postcode) Composite
Phone numbers and facilities Multivalued
Full name, age at allotment and free beds Derived

The questions really are independent. A list of addresses would be both composite and multivalued, and an age is simple, single-valued and derived.

Key attributes

A key attribute has a value that no two entities of the set may share, so it identifies one entity. Chen notation underlines it. Some entity sets have several candidates: roll number and email address are both unique for every resident, and the design picks one as the primary key and keeps the other as an alternate key. Some keys need several attributes together. A room number repeats across hostels, so a room is identified by its hostel’s name and its room number together; the third lesson of this module explains why a key that is unique only within its owner is called a partial key.

A key is a rule about every entity the set could ever hold, not an observation about today’s data. Data can show that something is not a key, never that it is one:

Unique today, not a key SQL · sqlite/keys_and_data.sql
-- Which attributes of these residents are unique right now?
CREATE TABLE resident (
  roll_no    TEXT PRIMARY KEY,
  first_name TEXT NOT NULL,
  last_name  TEXT NOT NULL,
  email      TEXT NOT NULL UNIQUE,
  birth_date TEXT NOT NULL
);
INSERT INTO resident VALUES
  ('H24-017', 'Meera', 'Iyer',  'meera.iyer@college.example',  '2006-09-14'),
  ('H24-031', 'Arjun', 'Bose',  'arjun.bose@college.example',  '2007-01-30'),
  ('H24-044', 'Tara',  'Iyer',  'tara.iyer@college.example',   '2006-09-14'),
  ('H24-052', 'Kabir', 'Sethi', 'kabir.sethi@college.example', '2005-12-02');

SELECT count(*)                                   AS residents,
       count(DISTINCT roll_no)                    AS roll_numbers,
       count(DISTINCT email)                      AS emails,
       count(DISTINCT first_name || ' ' || last_name) AS full_names,
       count(DISTINCT birth_date)                 AS birth_dates
FROM resident;

-- The full names happen to be unique today. A new resident shows they were never a key.
INSERT INTO resident VALUES ('H25-003', 'Meera', 'Iyer', 'meera.iyer2@college.example', '2007-03-21');
SELECT count(*) AS residents, count(DISTINCT first_name || ' ' || last_name) AS full_names FROM resident;

-- The declared keys refuse a second row with the same value.
INSERT INTO resident VALUES ('H25-003', 'Ravi', 'Nair', 'ravi.nair@college.example', '2006-05-05');
INSERT INTO resident VALUES ('H25-009', 'Ravi', 'Nair', 'arjun.bose@college.example', '2006-05-05');

Output (exit status 1)

┌───────────┬──────────────┬────────┬────────────┬─────────────┐
│ residents │ roll_numbers │ emails │ full_names │ birth_dates │
├───────────┼──────────────┼────────┼────────────┼─────────────┤
│ 4         │ 4            │ 4      │ 4          │ 3           │
└───────────┴──────────────┴────────┴────────────┴─────────────┘
┌───────────┬────────────┐
│ residents │ full_names │
├───────────┼────────────┤
│ 5         │ 4          │
└───────────┴────────────┘

Printed as an error (standard error)

Runtime error near line 27: UNIQUE constraint failed: resident.roll_no
Runtime error near line 28: UNIQUE constraint failed: resident.email

Recorded with sql.js 1.14.2 (SQLite 3.49.1) on macOS 26 arm64. To run it yourself: sqlite3 -box :memory: < keys_and_data.sql

On the first four rows, the full names were as unique as the roll numbers. A fifth resident with a common name broke that, while the declared keys kept working: the database refused a second resident with an existing roll number and another with an existing email address. The birth dates were never candidates; two residents already shared one.

Two kinds of attribute make poor keys, for reasons worth stating:

  • Derived attributes change when their inputs change. An age goes up every year, and a key that changes forces a change in every row that refers to it.
  • Multivalued attributes have no single value per entity. A resident with two phone numbers has no one phone number to identify them by, and a number can pass from one person to another.

An attribute that can be NULL makes no primary key either: an entity without a value would have nothing to be identified by.

Derived attributes in SQL

When the design becomes tables, a derived attribute is computed in one of three places: in the query that needs it, in a view, or in a generated column, a column whose value the database computes from the other columns of the same row. SQLite has generated columns of two kinds: a VIRTUAL one, the default, is computed whenever it is read, and a STORED one is computed when the row is written and kept in the file.

The full name as a generated column SQL · sqlite/derived_columns.sql
-- A resident of the hostel brief: the key, the two parts of the composite name, and two derived values.
CREATE TABLE resident (
  roll_no    TEXT PRIMARY KEY,
  first_name TEXT NOT NULL,
  last_name  TEXT NOT NULL,
  birth_date TEXT NOT NULL,   -- stored: age is computed from it when a report needs it
  full_name  TEXT GENERATED ALWAYS AS (first_name || ' ' || last_name),               -- VIRTUAL: computed on read
  sort_name  TEXT GENERATED ALWAYS AS (lower(last_name || ', ' || first_name)) STORED  -- computed on write
);

INSERT INTO resident (roll_no, first_name, last_name, birth_date)
VALUES ('H24-017', 'Meera', 'Iyer', '2006-09-14'),
       ('H24-031', 'Arjun', 'Bose', '2007-01-30');

SELECT roll_no, full_name, sort_name FROM resident ORDER BY sort_name;

-- The catalogue tells the kinds apart: hidden is 2 for a VIRTUAL column and 3 for a STORED one.
SELECT name, hidden FROM pragma_table_xinfo('resident');

-- A derived value is computed, never written.
UPDATE resident SET full_name = 'M. Iyer' WHERE roll_no = 'H24-017';

Output (exit status 1)

┌─────────┬────────────┬─────────────┐
│ roll_no │ full_name  │  sort_name  │
├─────────┼────────────┼─────────────┤
│ H24-031 │ Arjun Bose │ bose, arjun │
│ H24-017 │ Meera Iyer │ iyer, meera │
└─────────┴────────────┴─────────────┘
┌────────────┬────────┐
│    name    │ hidden │
├────────────┼────────┤
│ roll_no    │ 0      │
│ first_name │ 0      │
│ last_name  │ 0      │
│ birth_date │ 0      │
│ full_name  │ 2      │
│ sort_name  │ 3      │
└────────────┴────────┘

Printed as an error (standard error)

Parse error near line 21: cannot UPDATE generated column "full_name"

Recorded with sql.js 1.14.2 (SQLite 3.49.1) on macOS 26 arm64. To run it yourself: sqlite3 -box :memory: < derived_columns.sql

pragma_table_xinfo shows what the plain pragma_table_info leaves out: its hidden column is 2 for the virtual full_name and 3 for the stored sort_name. The last statement shows that a derived value cannot be written directly; the database refused the change, and full_name changes only when first_name or last_name does.

Why age cannot be a generated column

A generated column may use only its own row and functions that always give the same result for the same input. Today’s date is neither, so an age computed from the current date breaks the rule:

An age computed from today's date SQL · sqlite/age_from_today.sql
-- Age is derived from birth_date and today's date. Can it be a generated column?
CREATE TABLE resident (
  roll_no    TEXT PRIMARY KEY,
  birth_date TEXT NOT NULL,
  age        INTEGER GENERATED ALWAYS AS (
               CAST(strftime('%Y', 'now') AS INTEGER) - CAST(strftime('%Y', birth_date) AS INTEGER)
             )
);
SELECT 'the table was created' AS step;

INSERT INTO resident (roll_no, birth_date) VALUES ('H24-017', '2006-09-14');
SELECT count(*) AS rows_stored FROM resident;

Output (exit status 1)

┌───────────────────────┐
│         step          │
├───────────────────────┤
│ the table was created │
└───────────────────────┘
┌─────────────┐
│ rows_stored │
├─────────────┤
│ 0           │
└─────────────┘

Printed as an error (standard error)

Runtime error near line 11: non-deterministic use of strftime() in a generated column

Recorded with sql.js 1.14.2 (SQLite 3.49.1) on macOS 26 arm64. To run it yourself: sqlite3 -box :memory: < age_from_today.sql

SQLite accepted the definition and refused the first row. Its date and time functions count as deterministic until they are given 'now', and in a generated column they raise an error when that happens, so the problem appears at the first INSERT rather than at CREATE TABLE. Compute the age in the query instead, from birth_date and the date it is needed for, which is what the exercise below asks you to do.

Version note

PostgreSQL 18 behaves the same way in substance: its generated columns are virtual unless declared STORED, and a generation expression may use only immutable functions, so an age based on the current date is refused there too.

Derived from other rows: a view

The free beds of a room depend on rows of another table, the residents allotted to the room, and a generated column may not read other rows at all:

Free beds: a view, not a column SQL · sqlite/free_beds.sql
-- Free beds depend on other rows (the residents allotted to the room), not on the room's own row.
CREATE TABLE room (
  hostel  TEXT NOT NULL,
  room_no TEXT NOT NULL,
  beds    INTEGER NOT NULL CHECK (beds > 0),
  PRIMARY KEY (hostel, room_no)
);
CREATE TABLE allotment (
  roll_no TEXT PRIMARY KEY,
  hostel  TEXT NOT NULL,
  room_no TEXT NOT NULL,
  FOREIGN KEY (hostel, room_no) REFERENCES room (hostel, room_no)
);
INSERT INTO room VALUES ('Kaveri', '101', 3), ('Kaveri', '102', 2), ('Ganga', '101', 4);
INSERT INTO allotment VALUES ('H24-017', 'Kaveri', '101'), ('H24-031', 'Kaveri', '101'), ('H24-044', 'Ganga', '101');

-- A generated column may only read its own row: this one is refused.
ALTER TABLE room ADD COLUMN free_beds INTEGER GENERATED ALWAYS AS (
  beds - (SELECT count(*) FROM allotment AS a WHERE a.hostel = hostel AND a.room_no = room_no)
);

-- A view computes the derived attribute from both tables every time it is read.
CREATE VIEW room_free_beds AS
SELECT r.hostel, r.room_no, r.beds, r.beds - count(a.roll_no) AS free_beds
FROM room AS r
LEFT JOIN allotment AS a ON a.hostel = r.hostel AND a.room_no = r.room_no
GROUP BY r.hostel, r.room_no, r.beds;

SELECT * FROM room_free_beds ORDER BY hostel, room_no;

Output (exit status 1)

┌────────┬─────────┬──────┬───────────┐
│ hostel │ room_no │ beds │ free_beds │
├────────┼─────────┼──────┼───────────┤
│ Ganga  │ 101     │ 4    │ 3         │
│ Kaveri │ 101     │ 3    │ 1         │
│ Kaveri │ 102     │ 2    │ 2         │
└────────┴─────────┴──────┴───────────┘

Printed as an error (standard error)

Runtime error near line 18: error in table room after add column: subqueries prohibited in generated columns

Recorded with sql.js 1.14.2 (SQLite 3.49.1) on macOS 26 arm64. To run it yourself: sqlite3 -box :memory: < free_beds.sql

SQLite refused the column (“subqueries prohibited in generated columns”), and the view computed the same value from both tables each time it was read. Room 102 of Kaveri, with no residents, still appears, because the view joins with LEFT JOIN and counts only matching residents.

One design, three notations

ER diagrams are drawn in several notations. They change the picture, not the meaning, and every exam question or design document should say which one it uses.

Chen notation, named after the model’s author, draws an entity set as a rectangle and each attribute as an oval joined to it by a line. In the form most books use, the key attribute is underlined, a composite attribute has the ovals of its parts hanging from it, a multivalued attribute has a double outline and a derived attribute a dashed one.

Chen notation: RESIDENT with the key roll_no, a composite name, a birth date, a derived age and a multivalued phone.RESIDENTphoneagebirth_datenamefirstlastroll_no

The Resident entity in Chen notation

Text description of the diagram

A rectangle labelled RESIDENT stands for the entity set. Lines join it to five ovals, one for each attribute.

  1. roll_no, with its label underlined: the key attribute.
  2. name, a composite attribute: two more ovals, first and last, hang from it.
  3. birth_date, a plain stored attribute.
  4. age, drawn with a dashed outline: a derived attribute, computed from birth_date.
  5. phone, drawn with a double outline: a multivalued attribute, since a resident can have several phone numbers.

Crow’s foot diagrams draw each entity as a box listing its attributes, usually one per line with the key marked, and draw relationships as lines whose ends show how many entities take part: a bar for one, a ring for zero, a three-pronged “crow’s foot” for many. They are drawn close to the tables, so a multivalued attribute becomes an entity of its own joined by a one-to-many line, and derived attributes are left out or marked as computed.

UML class diagrams describe the same entity set as a class with properties. A slash before a name marks a derived property, a multiplicity in brackets says how many values a property has ([1..*] is one or more), and {id} marks the properties that identify an object.

The Resident design in crow's foot, as RESIDENT and RESIDENT_PHONE tables, and as a UML class with /age and phone [1..*].Crow's footUML classRESIDENT_PHONEroll_noTEXTPK, FKphoneTEXTPKRESIDENTroll_noTEXTPKfirst_nameTEXTlast_nameTEXTbirth_dateDATEResident+roll_noString {id}+nameName+birth_dateDate+/ageInteger+phoneString [1..*]same design

The same design in crow's foot and UML

Text description of the diagram

The figure has two boxes, one above the other, joined by a dashed arrow labelled "same design".

1. Crow's foot. Two table-like boxes. RESIDENT lists roll_no (primary key), first_name, last_name and birth_date. RESIDENT_PHONE lists roll_no (primary key and foreign key) and phone (primary key). A line joins the two roll_no rows: at the RESIDENT end it shows "one and only one", at the RESIDENT_PHONE end "one or many". The derived age is not drawn. 2. UML class. One class box named Resident with five properties: roll_no of type String marked {id}, name of type Name, birth_date of type Date, /age of type Integer (the slash marks it as derived), and phone of type String with the multiplicity [1..*], meaning one or more values.

The three drawings carry the same facts: roll number as the key, a two-part name, one or more phone numbers, a stored date of birth and an age derived from it. Crow’s foot already shows the phone numbers as the table they will become; the lesson on mapping ER diagrams to tables does that step for every kind of attribute.

SQL to ER Diagram Paste the CREATE TABLE statements of this lesson's examples and see them drawn as a crow's-foot diagram.

The ER model in GATE CS

In the GATE 2027 CS paper, the ER model is the first topic the Databases section lists. A question may describe a design in words or draw it, in any of the notations above, so read it for the notation and the conventions it states before you classify a single attribute.

Interview questions

What is the difference between a composite, a multivalued and a derived attribute? A composite attribute is made of parts that are useful on their own, such as a name made of a first and a last name. A multivalued attribute holds several values for one entity at the same time, such as a resident’s phone numbers. A derived attribute is computed from other data instead of being recorded, such as an age computed from a date of birth. The three are independent: a list of addresses would be both composite and multivalued. In tables, a composite attribute becomes one column per part, a multivalued one gets a table of its own, and a derived one is computed in a query, a view or a generated column.

Key takeaways

  • An entity is one thing a database tells apart from others of its kind; an entity set is all of them; attributes are the facts recorded about each.
  • A noun in a brief becomes an entity when the design records more about it than one value; otherwise it is an attribute value.
  • Ask three separate questions of every attribute: composite or simple, single- or multivalued, stored or derived.
  • A key must hold for every entity the set could ever contain: data can disprove a key but never prove one. Derived, multivalued and nullable attributes make poor keys.
  • Compute derived values in a query, a view or a generated column; a generated column may read only its own row and deterministic functions, so an age based on today’s date and a count over other rows both need another place.
  • Chen, crow’s foot and UML draw the same design differently; always say which notation you use.

Exercise

Exercise · Easy · SQL

Compute each resident's age on the day of allotment

The hostel office never stores an age: age is a derived attribute, so the database keeps each resident's birth_date and the date of every room allotment, and the reports compute the age from them. Two tables hold the data, with every date written as text in the form YYYY-MM-DD:

- resident (roll_no, first_name, birth_date) - allotment (roll_no, hostel, room_no, allotted_on), one row per allotment (a resident who moved rooms has several).

Write a query that returns, for every allotment, the columns roll_no, allotted_on and age_at_allotment: the number of whole years the resident had completed on allotted_on. A birthday counts as reached when the month and day of allotted_on, written MM-DD, are the same as or later than those of birth_date. By that rule a resident born on 02-29 turns a year older on 03-01 in years without a leap day.

The starter query only subtracts the years, which is a common mistake: run it and see which tests fail. The order of the rows does not matter.

Starter code · age_at_allotment.sql

-- Subtracting the years gives the right age only after that year's birthday.
SELECT a.roll_no,
       a.allotted_on,
       CAST(strftime('%Y', a.allotted_on) AS INTEGER)
         - CAST(strftime('%Y', r.birth_date) AS INTEGER) AS age_at_allotment
FROM allotment AS a
JOIN resident AS r ON r.roll_no = a.roll_no;
The sample tests · tests.yaml
# Sample tests: each one builds the two tables, runs your query and compares the rows it returns (in any order).
tests:
  - name: a birthday already passed, one still to come and one on the very day
    setup: |
      CREATE TABLE resident (roll_no TEXT PRIMARY KEY, first_name TEXT NOT NULL, birth_date TEXT NOT NULL);
      CREATE TABLE allotment (roll_no TEXT NOT NULL REFERENCES resident, hostel TEXT NOT NULL, room_no TEXT NOT NULL,
                              allotted_on TEXT NOT NULL, PRIMARY KEY (roll_no, allotted_on));
      INSERT INTO resident VALUES ('H24-017', 'Meera', '2006-09-14'), ('H24-031', 'Arjun', '2006-01-30'),
                                  ('H24-044', 'Tara', '2006-07-01');
      INSERT INTO allotment VALUES ('H24-017', 'Kaveri', '101', '2024-07-01'),
                                   ('H24-031', 'Kaveri', '101', '2024-07-01'),
                                   ('H24-044', 'Ganga', '214', '2024-07-01');
    expected:
      columns: [roll_no, allotted_on, age_at_allotment]
      rows:
        - [H24-017, '2024-07-01', 17]
        - [H24-031, '2024-07-01', 18]
        - [H24-044, '2024-07-01', 18]
  - name: a leap-day birthday, before, after and on the day
    setup: |
      CREATE TABLE resident (roll_no TEXT PRIMARY KEY, first_name TEXT NOT NULL, birth_date TEXT NOT NULL);
      CREATE TABLE allotment (roll_no TEXT NOT NULL REFERENCES resident, hostel TEXT NOT NULL, room_no TEXT NOT NULL,
                              allotted_on TEXT NOT NULL, PRIMARY KEY (roll_no, allotted_on));
      INSERT INTO resident VALUES ('H25-008', 'Ira', '2008-02-29');
      INSERT INTO allotment VALUES ('H25-008', 'Ganga', '108', '2025-02-28'),
                                   ('H25-008', 'Ganga', '110', '2025-03-01'),
                                   ('H25-008', 'Ganga', '112', '2028-02-29');
    expected:
      columns: [roll_no, allotted_on, age_at_allotment]
      rows:
        - [H25-008, '2025-02-28', 16]
        - [H25-008, '2025-03-01', 17]
        - [H25-008, '2028-02-29', 20]
  - name: the last and the first day of a year
    setup: |
      CREATE TABLE resident (roll_no TEXT PRIMARY KEY, first_name TEXT NOT NULL, birth_date TEXT NOT NULL);
      CREATE TABLE allotment (roll_no TEXT NOT NULL REFERENCES resident, hostel TEXT NOT NULL, room_no TEXT NOT NULL,
                              allotted_on TEXT NOT NULL, PRIMARY KEY (roll_no, allotted_on));
      INSERT INTO resident VALUES ('H23-090', 'Dev', '2005-12-31'), ('H23-091', 'Noor', '2006-01-01');
      INSERT INTO allotment VALUES ('H23-090', 'Kaveri', '305', '2023-12-30'),
                                   ('H23-090', 'Kaveri', '306', '2023-12-31'),
                                   ('H23-091', 'Kaveri', '307', '2023-12-31');
    expected:
      columns: [roll_no, allotted_on, age_at_allotment]
      rows:
        - [H23-090, '2023-12-30', 17]
        - [H23-090, '2023-12-31', 18]
        - [H23-091, '2023-12-31', 17]
A hint

Start from the difference of the years, then take 1 away when the birthday has not come yet in the year of the allotment. strftime('%m-%d', d) gives the month and day of a date as text such as 07-01, and text in that form sorts in calendar order, so < compares two of them correctly. In SQLite a comparison is itself a number, 1 when it holds and 0 when it does not, so you can subtract it directly.

The sample tests run on this device, in your browser (sql.js): nothing is sent to mysmartcopilot.com. The first run downloads SQLite (about 0.7 MB), which is kept for the next runs. A check in your browser is feedback for you, not proof that the code is right for every input.

Check yourself

7 questions about this lesson. Every answer and why it is right is on the page, behind “Show the answer”. Your score stays in this browser.

  1. Question 1 of 7 In the hostel brief, which of these attributes are multivalued?

    Choose every answer that is right.

    Show the answer to question 1

    Answer:

    • The facilities a hostel offers
    • A resident's phone numbers

    One resident can give several phone numbers and one hostel can offer several facilities at the same time, so both are multivalued. A room has one number of beds, a resident one date of birth and a hostel one warden: each of those can change, but at any moment there is a single value.

  2. Question 2 of 7 Which of these attributes are derived, so that a design computes them instead of storing them?

    Choose every answer that is right.

    Show the answer to question 2

    Answer:

    • The number of free beds in a room
    • A resident's full name
    • A resident's age on the day of allotment

    The age comes from the date of birth and the allotment date, the free beds from the room's beds and the residents allotted to it, and the full name from the first and last names. The number of beds and the date of birth are facts nobody can compute, so they are stored.

  3. Question 3 of 7 Which of these attributes are composite, made of parts the brief needs separately?

    Choose every answer that is right.

    Show the answer to question 3

    Answer:

    • A resident's home address (street, city and postcode)
    • A resident's name (first and last name)

    The brief lists the parts of the address and of the name, and reports use them on their own (residents per city, lists sorted by last name). A room number such as 214 is one value, even if its first digit happens to be the floor: the brief records the floor as an attribute of its own.

  4. Question 4 of 7 Room numbers are unique only within a hostel. Which attributes together identify one room?

    Choose one answer.

    Show the answer to question 4

    Answer: The hostel's name and the room number

    Two hostels can both have a room 101, so the room number alone repeats. Adding the hostel's name makes the pair unique. The floor or the number of beds would not help: two hostels can each have a room 101 on the first floor with three beds.

  5. Question 5 of 7 A resident's second phone number is stored as NULL. What does that NULL tell you?

    Choose one answer.

    Show the answer to question 5

    Answer: Either that the resident has no second phone or that nobody knows it; the NULL alone cannot say which

    SQL has one NULL for "not applicable" and "unknown". When the difference matters, the design has to record it another way: with phone numbers in a table of their own, a resident without a second phone simply has one row.

  6. Question 6 of 7 A UML class lists the property /age. What does the slash mean?

    Choose one answer.

    Show the answer to question 6

    Answer: The property is derived from other data

    In UML a slash before a property's name marks it as derived. A multiplicity such as [0..1] or [1..*] says how many values a property has, and {id} marks the properties that identify an object.

  7. Question 7 of 7 Every full name in today's resident table is unique. Can the full name be the key?

    Choose one answer.

    Show the answer to question 7

    Answer: No; a key must stay unique for every resident the hostel could ever have, and two people can share a name

    Data can show that something is not a key, never that it is one. keys_and_data.sql found four distinct full names among four residents, and the very next resident repeated one; the roll number stays unique because the college issues each one once.

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.