This has the 'sql' tag for some reason, so here's how I'd do it in SQL (note there is referential integiry throughout and no NULL
able columns):
CREATE TABLE Countries
(
country_code CHAR(3) NOT NULL UNIQUE
);
CREATE TABLE Cities
(
city_name VARCHAR(20) NOT NULL,
country_code CHAR(3) NOT NULL
REFERENCES Countries (country_code),
UNIQUE (country_code, city_name)
);
CREATE TABLE Users
(
username CHAR(8) NOT NULL UNIQUE,
country_code CHAR(3) NOT NULL,
UNIQUE (country_code, username)
);
CREATE TABLE UsersCountries
(
username CHAR(8) NOT NULL UNIQUE,
country_code CHAR(3) NOT NULL,
FOREIGN KEY (country_code, username)
REFERENCES Users (country_code, username),
city_name VARCHAR(20) NOT NULL,
FOREIGN KEY (country_code, city_name)
REFERENCES Cities (country_code, city_name)
);
Test data:
INSERT INTO Countries (country_code) VALUES
('ITL'),
('ESP');
INSERT INTO Cities (city_name, country_code)
VALUES
('Roma', 'ITL'),
('Naples', 'ITL'),
('Barcelona', 'ESP'),
('Madrid', 'ESP');
INSERT INTO Users (username, country_code) VALUES
('00000001', 'ESP'),
('00000002', 'ESP'),
('00000003', 'ITL'),
('00000004', 'ITL');
INSERT INTO UsersCountries (username, city_name, country_code)
VALUES
('00000002', 'Madrid', 'ESP'),
('00000004', 'Roma', 'ITL');
To be fair, most SQL coders will not have an aversion to using a NULL
able column and will prefer all user's details to appear in one table. Assuming your SQL product (correctly) does not treat NULL
as a value (for example MS SQL Server does not but MS Access does) then the following will work and is equivalent to the above structure (i.e. again referential integiry throughout despite the existence of NULL
able columns):
CREATE TABLE Users
(
username CHAR(8) NOT NULL UNIQUE,
city_name VARCHAR(20),
country_code CHAR(3) NOT NULL
REFERENCES Countries (country_code),
FOREIGN KEY (country_code, city_name)
REFERENCES Cities (country_code, city_name)
);
INSERT INTO Users (username, city_name, country_code) VALUES
('00000001', NULL, 'ESP'),
('00000002', 'Madrid', 'ESP'),
('00000003', NULL, 'ITL'),
('00000004', 'Roma', 'ITL');