Are these database schemes correct? - postgresql

I am making a polling system where each poll has multiple choices and a user can make multiple choices. And I would like to know if these schemes were correct
Namely:
A guild cannot have two polls with the same id (Composite key)
A poll cannot have multiple choices with the same id (Composite key)
A user cannot make the same choice several times on a poll (Composite key)
CREATE TABLE IF NOT EXISTS guilds_polls
(
guild_id BIGINT NOT NULL,
poll_id varchar(8) NOT NULL,
PRIMARY KEY (guild_id, poll_id),
FOREIGN KEY (guild_id) REFERENCES guilds (id) ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS guilds_polls_choices
(
guild_id BIGINT NOT NULL,
poll_id varchar(8) NOT NULL,
choice_id INT NOT NULL,
choice VARCHAR(255) NOT NULL,
PRIMARY KEY (guild_id, poll_id, choice_id),
FOREIGN KEY (guild_id, poll_id) REFERENCES guilds_polls (guild_id, poll_id) ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS guilds_polls_user_choices
(
guild_id BIGINT NOT NULL,
poll_id varchar(8) NOT NULL,
choice_id BIGINT NOT NULL,
user_id BIGINT NOT NULL,
PRIMARY KEY (guild_id, poll_id, user_id, choice_id),
FOREIGN KEY (guild_id, poll_id) REFERENCES guilds_polls (guild_id, poll_id) ON DELETE CASCADE
);

LGTM.
But there is a small improvement we could make. Polls and choices is actually a n:n relation. That means multiple polls can have the same choice (A lot of polls are probably going to have the choices "no" and "yes") and one poll can have multiple choices (obviously). In your schema, you modeled it as a 1:n relation (every poll has multiple choices, but every choice has only one poll).
The solution would be to create a separate table Choices and then introduce a connecting table, which glues the choices to the polls. This way you have one table more, but a lot less duplicate data:
CREATE TABLE IF NOT EXISTS guilds_polls
(
guild_id BIGINT NOT NULL,
poll_id varchar(8) NOT NULL,
PRIMARY KEY (guild_id, poll_id),
FOREIGN KEY (guild_id) REFERENCES guilds (id) ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS guilds_polls_choices
(
guild_id BIGINT NOT NULL,
poll_id varchar(8) NOT NULL,
choice_id INT NOT NULL,
PRIMARY KEY (guild_id, poll_id, choice_id),
FOREIGN KEY (guild_id, poll_id) REFERENCES guilds_polls (guild_id, poll_id) ON DELETE CASCADE,
FOREIGN KEY (choice_id) REFERENCES choices (choice_id) ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS choices
(
choice_id INT NOT NULL,
choice VARCHAR(255) NOT NULL,
PRIMARY KEY (choice_id),
);
CREATE TABLE IF NOT EXISTS guilds_polls_user_choices
(
guild_id BIGINT NOT NULL,
poll_id varchar(8) NOT NULL,
choice_id BIGINT NOT NULL,
user_id BIGINT NOT NULL,
PRIMARY KEY (guild_id, poll_id, user_id, choice_id),
FOREIGN KEY (guild_id, poll_id) REFERENCES guilds_polls (guild_id, poll_id) ON DELETE CASCADE,
-- you also forgot this foreign key:
FOREIGN KEY (choice_id) REFERENCES choices (choice_id) ON DELETE CASCADE
);
Disclaimer: not a PostgreSQL expert, so the syntax could be off.
Database modeling relies heavily on normalization (https://en.wikipedia.org/wiki/Database_normalization). Every database should follow the first 3 Normal Forms.

Related

Homegraph concept database schema permissions

I'm trying to make schema for my PostgreSQL database basing on the Homegraph concept, however I'm not really sure about correctness of current schema and whether user should have per-structure access or per-device.
I can't find any real-world example of Homegraph concept implementation, and Google seem to not provide such information.
CREATE EXTENSION hstore;
CREATE TABLE users (
id CHAR(32) NOT NULL,
username TEXT NOT NULL,
email TEXT UNIQUE NOT NULL,
password_hash TEXT NOT NULL,
PRIMARY KEY (
id
)
);
CREATE TABLE user_structures (
structure_id CHAR(32) REFERENCES structures (id) ON DELETE CASCADE,
user_id CHAR(32) REFERENCES users (id) ON DELETE CASCADE,
manager BOOL NOT NULL,
PRIMARY KEY (
structure_id,
user_id,
)
);
CREATE TABLE structures (
id CHAR(32) UNIQUE NOT NULL,
label TEXT NOT NULL,
PRIMARY KEY (
id
)
);
CREATE TABLE rooms (
id CHAR(32) UNIQUE NOT NULL,
structure_id CHAR(32) REFERENCES structures (id) ON DELETE CASCADE,
label TEXT NOT NULL,
PRIMARY KEY (
room_id
)
);
CREATE TABLE devices (
id CHAR(32) UNIQUE NOT NULL,
room_id CHAR(32) REFERENCES room (id) ON DELETE CASCADE,
password_hash TEXT NOT NULL,
type TEXT NOT NULL,
traits TEXT[] NOT NULL,
name TEXT NOT NULL,
will_push_state BOOL NOT NULL,
model TEXT NOT NULL,
hw_version TEXT NOT NULL,
sw_version TEXT NOT NULL,
attributes hstore NOT NULL,
PRIMARY KEY (
id,
structure_id
)
);
With regard to the devices table, there is a canonical protobuf that defines each of the fields and their types.
Beyond that, the implementation of the HomeGraph schema is an internal definition within Google's smart home platform and its structure shouldn't matter from a developer perspective.

Error Code: 1822. Failed to add the foreign key constraint. Missing index for constraint 'questions_ibfk_1' in the referenced table 'category'

cannot add foreign key constraint to table
create table users
(
user_id int auto_increment primary key not null,
username varchar(50) unique null ,
email varchar(50) unique ,
passwords varchar(50) not null,
login_status boolean not null
);
create table category (
category_id int primary key not null,
category_name varchar(50) not null
);
create table answers (
id_answer int auto_increment primary key not null,
answer boolean not null
);
create table questions (
question_id int primary key not null,
category_name varchar(50) not null,
content varchar(50) not null ,
foreign key (category_name) references category (category_name)
);
You get this error because there's no index on category_name in the category table. Change that CREATE statement as follows:
create table category (
category_id int primary key not null,
category_name varchar(50) not null,
KEY category_name_index (category_name)
);
From the docs (8.0 version, but the statement is true for older versions):
MySQL requires indexes on foreign keys and referenced keys so that foreign key checks can be fast and not require a table scan. In the referencing table, there must be an index where the foreign key columns are listed as the first columns in the same order. Such an index is created on the referencing table automatically if it does not exist. This index might be silently dropped later, if you create another index that can be used to enforce the foreign key constraint. index_name, if given, is used as described previously.
Also, you're using a varchar(50) as your foreign key, which is not usually a great idea for a variety of reasons. You probably want to use a numeric value, such as category_id, instead.

Find cause of error "constraint xxxx is not a foreign key constraint" in Postgresql

I have defined these tables:
CREATE TABLE "public".category (id BIGSERIAL NOT NULL, name varchar(255) NOT NULL, PRIMARY KEY (id));
CREATE UNIQUE INDEX category_name ON "public".category (name);
CREATE TABLE "public".clusters (id BIGSERIAL NOT NULL, name varchar(255) NOT NULL, PRIMARY KEY (id));
CREATE INDEX clusters_name ON "public".clusters (name);
CREATE TABLE "public".keywords (id BIGSERIAL NOT NULL, text varchar(255) NOT NULL, category_id int8 NOT NULL, top_results int4, cluster_id int8, month_requests int4, click_cost float8, PRIMARY KEY (id));
CREATE INDEX keywords_text ON "public".keywords (text);
ALTER TABLE "public".keywords ADD CONSTRAINT FKkeywords488682 FOREIGN KEY (cluster_id) REFERENCES "public".clusters (id);
ALTER TABLE "public".keywords ADD CONSTRAINT FKkeywords446526 FOREIGN KEY (category_id) REFERENCES "public".category (id) ON UPDATE CASCADE ON DELETE CASCADE;
added one record to category table:
INSERT INTO "public".category(id, name) VALUES (1, 'Test');
And now when I try to add record to keyword table
insert into "public"."keywords" ( "category_id", "text") values ( 1, 'testkey')
I got error:
ERROR: constraint 16959 is not a foreign key constraint
When I do
select * FROM pg_constraint;
I can't see constraint with this id. I can't understand what is the cause of this problem.

'there is no unique constraint matching given keys for referenced table' where i dont want something to be unique

In Postgres I have written the following code:
CREATE TABLE students (
student_id BIGINT PRIMARY KEY,
first_name VARCHAR(15),
surname VARCHAR(35),
enrollment_year INT);
CREATE TABLE teachers (
bsn BIGINT PRIMARY KEY,
first_name VARCHAR(15),
surname VARCHAR(35),
salary REAL,
scale INT,
CONSTRAINT salary CHECK(salary < (25 * scale) AND salary > (20 * scale))
);
CREATE TABLE course (
course_code VARCHAR(10) PRIMARY KEY,
course_name VARCHAR(20) UNIQUE,
study_points INT);
CREATE TABLE study_program (
course_name VARCHAR(20) PRIMARY KEY,
level VARCHAR(15),
duration VARCHAR(10),
FOREIGN KEY(course_name) REFERENCES course(course_name));
CREATE TABLE assignment (
assignment_code VARCHAR(10) UNIQUE,
course_name VARCHAR(20),
PRIMARY KEY(assignment_code, course_name),
FOREIGN KEY(course_name) REFERENCES course(course_name)
);
CREATE TABLE records (
student_id BIGINT,
course_name VARCHAR(20),
PRIMARY KEY(student_id, course_name),
FOREIGN KEY(student_id) REFERENCES students(student_id),
FOREIGN KEY(course_name) REFERENCES course(course_name));
CREATE TABLE make (
student_id BIGINT,
assignment_code VARCHAR(20),
PRIMARY KEY(student_id, assignment_code),
FOREIGN KEY(student_id) REFERENCES students(student_id),
FOREIGN KEY(assignment_code) REFERENCES assignment(assignment_code));
CREATE TABLE prerequisit (
assignment_code VARCHAR(20),
course_name VARCHAR(20),
PRIMARY KEY(assignment_code, course_name),
FOREIGN KEY(assignment_code) REFERENCES assignment(assignment_code),
FOREIGN KEY(course_name) REFERENCES course(course_name)
);
CREATE TABLE records_2 (
assignment_code VARCHAR(20),
course_name VARCHAR(20),
bsn BIGINT,
mandatory BOOLEAN,
year INT,
week INT,
PRIMARY KEY(assignment_code, course_name),
FOREIGN KEY(assignment_code) REFERENCES assignment(assignment_code),
FOREIGN KEY(course_name) REFERENCES course(course_name)
);
CREATE TABLE designes (
course_code VARCHAR(15),
bsn BIGINT,
study_points INT,
course_name VARCHAR(20),
PRIMARY KEY(course_code, bsn),
FOREIGN KEY(course_code) REFERENCES course(course_code),
FOREIGN KEY(bsn) REFERENCES teachers(bsn),
FOREIGN KEY(study_points) REFERENCES course(study_points),
FOREIGN KEY(course_name) REFERENCES course(course_name)
);
CREATE TABLE reviews (
bsn BIGINT PRIMARY KEY,
course_code VARCHAR(15),
study_points INT,
course_name VARCHAR(20),
FOREIGN KEY(bsn) REFERENCES teachers(bsn),
FOREIGN KEY(course_code) REFERENCES course(course_code),
FOREIGN KEY(study_points) REFERENCES course(study_points),
FOREIGN KEY(course_name) REFERENCES course(course_name)
);
The error I get is
ERROR: there is no unique constraint matching given keys for referenced table "course"
This error is fixed by adding the UNIQUE constraint to study points, however I do not want study points to be unique because I need different courses to have the same amount of study points.
Referencing study_points makes no sense whatsoever. In fact referencing three different columns of a single table makes no sense whatsoever.
You only need FOREIGN KEY(course_code) REFERENCES course(course_code) and you don't need the columns course_name and study_points in the reviews table as they are implicitly defined through the foreign key to the course table.
Which means the table should look something like this:
CREATE TABLE reviews
(
bsn BIGINT PRIMARY KEY,
course_code VARCHAR(15),
FOREIGN KEY(bsn) REFERENCES teachers(bsn),
FOREIGN KEY(course_code) REFERENCES course(course_code)
);
Which then begs the question: what is that table supposed to do? You can only have a single row per teacher in there because bsn is the primary key (thus can't be repeated). So that is a one-to-one (or one-to-zero/one) relationship. Which in turn means you could move the column course_code to the teachers table, e.g. as reviewed_course.
However, I guess you meant to create a many-to-many relationship between teachers and course which then should be something like this:
CREATE TABLE reviews
(
bsn BIGINT,
course_code VARCHAR(15),
primary key (bsn, course_code),
FOREIGN KEY(bsn) REFERENCES teachers(bsn),
FOREIGN KEY(course_code) REFERENCES course(course_code)
);
That makes the combination of bsn and course_code unique. Which means each teacher can review each course exactly once, but a teacher can review multiple courses and a single course can be reviewed by multiple teachers.

Many to many to many relationships with EF4 and Code First

I've got a problem I can't figure out how to express with CTP5 of Code First and EF4.
EDIT: Added my old schema at the bottom that I'd like to replicate through CF
Here's my specific scenario:
A Team is an abstract concept; it should exist with a specific set of players, in a specific division, in a specific season.
A concrete example of this in action from the NFL:
1996 (season) AFC Central (division) Houston Oilers (team)
1997 (season) AFC Central (division) Tennessee Oilers (team)
1999 (season) AFC Central (division) Tennessee Titans (team)
2002 (season) AFC South (division) Tennessee Titans (team)
These are all the same team. I want to be able to do the following:
// Titans team id = 17
var myTeam = myContext.Teams.Single(t => t.Id == 17)
// display players
foreach (var p in myTeam.Seasons[1999].Players)
{
// Do something with the p here
}
// Display current division
Response.Write(myTeam.Seasons[2002].Division.Name);
I'm not sure of the specific query syntax within an ICollection member variable of myTeam.Seasons, but the concept should be the same none the less.
Can anyone shed some light on how you'd express this concept through CF in EF4 CF CTP5?
How would you express this through Code First?
Current SQL tables
CREATE TABLE dbo.Season
(
Id INT IDENTITY(1,1) NOT NULL,
LeagueId INT NOT NULL,
[Name] NVARCHAR(50) NOT NULL,
[Year] CHAR(4) NOT NULL,
PrevSeasonId INT NULL
) ON [PRIMARY];
// Primary key
ALTER TABLE dbo.Season WITH NOCHECK ADD
CONSTRAINT PK_Season PRIMARY KEY NONCLUSTERED (Id) ON [PRIMARY];
CREATE TABLE dbo.Division
(
Id INT IDENTITY(1,1) NOT NULL,
DefaultName NVARCHAR(50) NOT NULL
) ON [PRIMARY];
// Primary key
ALTER TABLE dbo.Division WITH NOCHECK ADD
CONSTRAINT PK_Division PRIMARY KEY NONCLUSTERED (Id) ON [PRIMARY];
// Key Relation Table
CREATE TABLE dbo.DivisionsInSeason
(
DivisionId INT NOT NULL,
SeasonId INT NOT NULL,
DefaultName NVARCHAR(50) NOT NULL,
Commissioner UNIQUEIDENTIFIER NOT NULL,
ParentDivId INT NULL
) ON [PRIMARY];
// Primary Key
ALTER TABLE dbo.DivisionsInSeason WITH NOCHECK ADD
CONSTRAINT PK_DivisionsInSeason PRIMARY KEY NONCLUSTERED (DivisionId, SeasonId) ON [PRIMARY] ;
// Foreign Keys
ALTER TABLE dbo.DivisionsInSeason WITH CHECK ADD
CONSTRAINT FK_DivisionsInSeason_Division FOREIGN KEY(DivisionId) REFERENCES dbo.Division(Id),
CONSTRAINT FK_DivisionsInSeason_Season FOREIGN KEY(SeasonId) REFERENCES dbo.Season(Id),
CONSTRAINT FK_DivisionsInSeason_User FOREIGN KEY(Commissioner) REFERENCES dbo.[User](Id);
CREATE TABLE dbo.Team
(
Id INT IDENTITY(1,1) NOT NULL,
DefaultName NVARCHAR(50) NOT NULL,
DefShortName NCHAR(3) NULL
) ON [PRIMARY];
// Primary key
ALTER TABLE dbo.Team WITH NOCHECK ADD
CONSTRAINT PK_Team PRIMARY KEY NONCLUSTERED (Id) ON [PRIMARY] ;
// Key relationship table
CREATE TABLE dbo.TeamsInDivision
(
TeamId INT NOT NULL,
DivisionId INT NOT NULL,
SeasonId INT NOT NULL,
GeneralManager UNIQUEIDENTIFIER NOT NULL,
Name NVARCHAR(50) NOT NULL,
ShortName NCHAR(3) NULL
) ON [PRIMARY];
// Check Constraints
ALTER TABLE dbo.TeamsInDivision ADD
CONSTRAINT PK_TeamsInDivision PRIMARY KEY NONCLUSTERED (TeamId, DivisionId, SeasonId) ON [PRIMARY];
// Foreign Keys
ALTER TABLE dbo.TeamsInDivision WITH CHECK ADD
CONSTRAINT FK_TeamsInDivision_Team FOREIGN KEY(TeamId) REFERENCES dbo.Team(Id),
CONSTRAINT FK_TeamsInDivision_Division FOREIGN KEY(DivisionId) REFERENCES dbo.Division(Id),
CONSTRAINT FK_TeamsInDivision_Season FOREIGN KEY(SeasonId) REFERENCES dbo.Season(Id),
CONSTRAINT FK_TeamsInDivision_User FOREIGN KEY(GeneralManager) REFERENCES dbo.[User](Id);
Maybe this could be usefull
http://blogs.microsoft.co.il/blogs/gilf/archive/2011/08/01/creating-a-many-to-many-mapping-using-code-first.aspx