How to create a required many-to-many relationship in SQLite? - sqlite

Suppose I have a schema like this:
CREATE TABLE Artist (
ArtistID INTEGER NOT NULL,
ArtistName TEXT NOT NULL,
PRIMARY KEY (ArtistID)
);
CREATE TABLE Song (
SongID INTEGER NOT NULL,
SongTitle TEXT NOT NULL,
PRIMARY KEY (SongID)
);
CREATE TABLE SongArtist (
SongID INTEGER NOT NULL,
ArtistID INTEGER NOT NULL,
PRIMARY KEY (SongID, ArtistID),
FOREIGN KEY (SongID) REFERENCES Song(SongID),
FOREIGN KEY (ArtistID) REFERENCES Artist(ArtistID)
);
By defining a column as NOT NULL I can semantically say that having a value in it is required. How would I make a many-to-many relationship required, but only in one direction?
In this situation, what I mean is this: How can I say that a Song row must have at least one Artist row associated with it through the SongArtist join table? If I were to represent a song as the JSON object below, this would be equivalent to saying that the songArtistIds array must have a length of 1 or higher.
{
songId: 745194,
songTitle: "Title",
songArtistIds: [523214]
}
However, an Artist row need not be associated with any Song row necessarily. An artist can have 0 or more songs, but a song must have 1 or more artists. How can I enforce this in SQLite? Also, if the answer is that I cannot do this in SQLite, then what alternative do I have for an embedded application?

Related

Trigger is not created in sqlite

I'm building a database for a library management system, so I have created three tables: books, members and borrow(describe the relation between the two tables).
for each book I store in the books table, I store it's quantity.
for each member that want to borrow a book, I store his id and the book id in the borrow table.
for every time a member want to borrow a book, I want to check that borrowed quantity is equal or less than the quantity stored for that book in the books table(in case it was more it will rise an error and will not accept the new data) , so I tried to achieve this using a trigger
the problem is that when I try to run the trigger it's not created, and it does not even give an error message, when I try even to see the trigger that is created in the database using the command:(select name from sqlite_master where type = 'trigger';) it does not show any thing
here is the code:
CREATE TABLE books(
book_id INTEGER CHECK (book_id>0999) PRIMARY KEY AUTOINCREMENT,
book_title VARCHAR(20) NOT NULL,
author_name VARCHAR(20),
quantity INT NOT NULL,
genre VARCHAR(20) NOT NULL,
book_place VARCHAR(20) NOT NULL,
UNIQUE(book_title,author_name)
);
CREATE TABLE members(
member_id INTEGER PRIMARY KEY AUTOINCREMENT CHECK(member_id<1000) ,
member_name VARCHAR(20) NOT NULL,
member_phone TEXT NOT NULL UNIQUE
CHECK (LENGTH(member_phone)==11 AND member_phone NOT GLOB '*[^0-9]*'
AND (SUBSTR(member_phone,1,3)=='010' OR SUBSTR(member_phone,1,3)=='011'
OR SUBSTR(member_phone,1,3)=='012' OR SUBSTR(member_phone,1,3)=='015' )),
sub_startDate TEXT NOT NULL CHECK(sub_startDate IS DATE(sub_startDate)),
sub_endDate TEXT NOT NULL CHECK(sub_endDate IS DATE(sub_endDate))
);
CREATE TABLE borrow(
member_id INTEGER NOT NULL,
book_id INTEGER NOT NULL,
borrowed_date TEXT NOT NULL CHECK(borrowed_date IS DATE(borrowed_date)),
return_date TEXT NOT NULL CHECK (return_date IS DATE(return_date)),
FOREIGN KEY(member_id) REFERENCES members(member_id) ON DELETE CASCADE ,
FOREIGN KEY(book_id) REFERENCES books(book_id) ON DELETE CASCADE,
PRIMARY KEY(member_id,book_id)
);
CREATE TRIGGER not_enough_copies
BEFORE INSERT, UPDATE
ON borrow
WHEN
(SELECT((SELECT
COUNT(*)
FROM borrow
WHERE book_id=NEW.book_id)
NOT BETWEEN 1 AND
(SELECT quantity FROM books WHERE books.book_id==NEW.book_id)))
BEGIN
RAISE(ABORT,'ERROR!..This book is not available in the library right now')
END;

How to configure periods dynamically according to number of persons?

I'm making a database for my expense tracker/splitter (offers possibility to add/edit/remove Transactions). Depending on number of Persons, when you add a new transaction you must specify who paid and to what amount. Then it creates Payments row for each person within Transactions.
You must configure each person's salary then it will calculate the pro-rata for each person for unpaid expenses (IsPayed flag). Once every person is ready to settle they must create a period (start and end date). All transactions between those dates will be tagged as paid (IsPayed = true) and the application will assume they are settle and frozen in time. There will be a FK between Transaction and Period to specify from which period a transaction was paid.
I want to add a relation between Period(Id) and SalaryConfiguration(IdGroup); it would specify SalaryConfiguration for said period, but I can't since IdGroup is not a primary key. I do not know how a period can be dynamic on the number of persons and specific configurations. How can I link SalaryConfiguration and Periods?
Database DDL:
CREATE TABLE "Transactions" (
"Id" INTEGER NOT NULL UNIQUE,
"FkIdCategories" INTEGER NOT NULL,
"Date" TEXT NOT NULL,
"Name" TEXT NOT NULL,
"Description" TEXT,
"Cost" INTEGER NOT NULL,
"IsPayed" INTEGER NOT NULL DEFAULT 0,
PRIMARY KEY("Id" AUTOINCREMENT),
CONSTRAINT "Fk_Categories_Transactions" FOREIGN KEY("FkIdCategories") REFERENCES "Categories"("Id")
);
CREATE TABLE "Categories" (
"Id" INTEGER NOT NULL UNIQUE,
"Name" TEXT NOT NULL UNIQUE,
PRIMARY KEY("Id" AUTOINCREMENT)
);
CREATE TABLE "Payments" (
"Id" INTEGER NOT NULL UNIQUE,
"FkIdPerson" INTEGER NOT NULL,
"FkIdTransaction" INTEGER NOT NULL,
"Amount" INTEGER NOT NULL,
CONSTRAINT "Fk_Persons_Payments" FOREIGN KEY("FkIdPerson") REFERENCES "Persons"("Id"),
CONSTRAINT "Fk_Transactions_Payments" FOREIGN KEY("FkIdTransaction") REFERENCES "Transactions"("Id"),
PRIMARY KEY("Id" AUTOINCREMENT)
);
CREATE TABLE "Persons" (
"Id" INTEGER NOT NULL UNIQUE,
"Name" TEXT NOT NULL,
PRIMARY KEY("Id" AUTOINCREMENT)
);
CREATE TABLE "SalaryConfigurations" (
"Id" INTEGER NOT NULL UNIQUE,
"IdGroup" INTEGER NOT NULL,
"FkIdPerson" INTEGER NOT NULL,
"Salary" INTEGER NOT NULL,
"SaveDate" TEXT NOT NULL,
CONSTRAINT "Fk_Persons_SalaryConfiguration" FOREIGN KEY("FkIdPerson") REFERENCES "Persons"("Id"),
PRIMARY KEY("Id" AUTOINCREMENT)
);
CREATE TABLE "Periods" (
"Id" INTEGER NOT NULL UNIQUE,
"StartDate" TEXT NOT NULL,
"EndDate" TEXT NOT NULL,
"FkIDSalaryConfigurationGroup" INTEGER NOT NULL,
PRIMARY KEY("Id" AUTOINCREMENT)
);
You can link the two tables together with another table that contains SalaryConfigurations(Id) and Periods(Id).

I need to create tables with "one to zero or to one" relationships in sqlite. This is what I have so far, is it right?

So I have this so far, I have to bind these two tables (one-to-zero or to-one relationship)
DROP TABLE if exists Person;
CREATE TABLE Person(
ID int NOT NULL,
Name varchar (40),
PRIMARY KEY(ID));
DROP TABLE if exists Pass;
CREATE TABLE Pass(
ID int REFERENCES Person,
Owner int,
PRIMARY KEY(ID),
FOREIGN KEY(Owner) REFERENCES Person(ID));
I'm going to use a different child table definition, because the
Pass table in your question doesn't make any sense; it doesn't
contain any information of its own.
CREATE TABLE Person (
ID INTEGER PRIMARY KEY,
Name VARCHAR(40) NOT NULL,
Dept INT REFERENCES Department(DeptID);
);
CREATE TABLE Department (
DeptID INTEGER PRIMARY KEY,
Name VARCHAR(120) NOT NULL
);
The Dept column of the Person table has a 1 : 0 or 1 relationship
to the Department table, because each row in the Person table
(1) can contain either zero or one (0 or 1) references to a row in
the Department table (the Dept column can be NULL).
Meaning that a person can be assigned to a department or not, but if
they are, they can only be assigned to 1 department.

Whats the proper way to structure a SQLite DB with items and lists of items to create them [duplicate]

Can anyone explain how to implement one-to-one, one-to-many and many-to-many relationships while designing tables with some examples?
One-to-one: Use a foreign key to the referenced table:
student: student_id, first_name, last_name, address_id
address: address_id, address, city, zipcode, student_id # you can have a
# "link back" if you need
You must also put a unique constraint on the foreign key column (addess.student_id) to prevent multiple rows in the child table (address) from relating to the same row in the referenced table (student).
One-to-many: Use a foreign key on the many side of the relationship linking back to the "one" side:
teachers: teacher_id, first_name, last_name # the "one" side
classes: class_id, class_name, teacher_id # the "many" side
Many-to-many: Use a junction table (example):
student: student_id, first_name, last_name
classes: class_id, name, teacher_id
student_classes: class_id, student_id # the junction table
Example queries:
-- Getting all students for a class:
SELECT s.student_id, last_name
FROM student_classes sc
INNER JOIN students s ON s.student_id = sc.student_id
WHERE sc.class_id = X
-- Getting all classes for a student:
SELECT c.class_id, name
FROM student_classes sc
INNER JOIN classes c ON c.class_id = sc.class_id
WHERE sc.student_id = Y
Here are some real-world examples of the types of relationships:
One-to-one (1:1)
A relationship is one-to-one if and only if one record from table A is related to a maximum of one record in table B.
To establish a one-to-one relationship, the primary key of table B (with no orphan record) must be the secondary key of table A (with orphan records).
For example:
CREATE TABLE Gov(
GID number(6) PRIMARY KEY,
Name varchar2(25),
Address varchar2(30),
TermBegin date,
TermEnd date
);
CREATE TABLE State(
SID number(3) PRIMARY KEY,
StateName varchar2(15),
Population number(10),
SGID Number(4) REFERENCES Gov(GID),
CONSTRAINT GOV_SDID UNIQUE (SGID)
);
INSERT INTO gov(GID, Name, Address, TermBegin)
values(110, 'Bob', '123 Any St', '1-Jan-2009');
INSERT INTO STATE values(111, 'Virginia', 2000000, 110);
One-to-many (1:M)
A relationship is one-to-many if and only if one record from table A is
related to one or more records in table B. However, one record in table B cannot be related to more than one record in table A.
To establish a one-to-many relationship, the primary key of table A (the "one" table) must be the secondary key of table B (the "many" table).
For example:
CREATE TABLE Vendor(
VendorNumber number(4) PRIMARY KEY,
Name varchar2(20),
Address varchar2(20),
City varchar2(15),
Street varchar2(2),
ZipCode varchar2(10),
Contact varchar2(16),
PhoneNumber varchar2(12),
Status varchar2(8),
StampDate date
);
CREATE TABLE Inventory(
Item varchar2(6) PRIMARY KEY,
Description varchar2(30),
CurrentQuantity number(4) NOT NULL,
VendorNumber number(2) REFERENCES Vendor(VendorNumber),
ReorderQuantity number(3) NOT NULL
);
Many-to-many (M:M)
A relationship is many-to-many if and only if one record from table A is related to one or more records in table B and vice-versa.
To establish a many-to-many relationship, create a third table called "ClassStudentRelation" which will have the primary keys of both table A and table B.
CREATE TABLE Class(
ClassID varchar2(10) PRIMARY KEY,
Title varchar2(30),
Instructor varchar2(30),
Day varchar2(15),
Time varchar2(10)
);
CREATE TABLE Student(
StudentID varchar2(15) PRIMARY KEY,
Name varchar2(35),
Major varchar2(35),
ClassYear varchar2(10),
Status varchar2(10)
);
CREATE TABLE ClassStudentRelation(
StudentID varchar2(15) NOT NULL,
ClassID varchar2(14) NOT NULL,
FOREIGN KEY (StudentID) REFERENCES Student(StudentID),
FOREIGN KEY (ClassID) REFERENCES Class(ClassID),
UNIQUE (StudentID, ClassID)
);
One-to-many
The one-to-many table relationship looks as follows:
In a relational database system, a one-to-many table relationship links two tables based on a Foreign Key column in the child which references the Primary Key of the parent table row.
In the table diagram above, the post_id column in the post_comment table has a Foreign Key relationship with the post table id Primary Key column:
ALTER TABLE
post_comment
ADD CONSTRAINT
fk_post_comment_post_id
FOREIGN KEY (post_id) REFERENCES post
One-to-one
The one-to-one table relationship looks as follows:
In a relational database system, a one-to-one table relationship links two tables based on a Primary Key column in the child which is also a Foreign Key referencing the Primary Key of the parent table row.
Therefore, we can say that the child table shares the Primary Key with the parent table.
In the table diagram above, the id column in the post_details table has also a Foreign Key relationship with the post table id Primary Key column:
ALTER TABLE
post_details
ADD CONSTRAINT
fk_post_details_id
FOREIGN KEY (id) REFERENCES post
Many-to-many
The many-to-many table relationship looks as follows:
In a relational database system, a many-to-many table relationship links two parent tables via a child table which contains two Foreign Key columns referencing the Primary Key columns of the two parent tables.
In the table diagram above, the post_id column in the post_tag table has also a Foreign Key relationship with the post table id Primary Key column:
ALTER TABLE
post_tag
ADD CONSTRAINT
fk_post_tag_post_id
FOREIGN KEY (post_id) REFERENCES post
And, the tag_id column in the post_tag table has a Foreign Key relationship with the tag table id Primary Key column:
ALTER TABLE
post_tag
ADD CONSTRAINT
fk_post_tag_tag_id
FOREIGN KEY (tag_id) REFERENCES tag
One to one (1-1) relationship:
This is relationship between primary & foreign key (primary key relating to foreign key only one record). this is one to one relationship.
One to Many (1-M) relationship:
This is also relationship between primary & foreign keys relationships but here primary key relating to multiple records (i.e. Table A have book info and Table B have multiple publishers of one book).
Many to Many (M-M): Many to many includes two dimensions, explained fully as below with sample.
-- This table will hold our phone calls.
CREATE TABLE dbo.PhoneCalls
(
ID INT IDENTITY(1, 1) NOT NULL,
CallTime DATETIME NOT NULL DEFAULT GETDATE(),
CallerPhoneNumber CHAR(10) NOT NULL
)
-- This table will hold our "tickets" (or cases).
CREATE TABLE dbo.Tickets
(
ID INT IDENTITY(1, 1) NOT NULL,
CreatedTime DATETIME NOT NULL DEFAULT GETDATE(),
Subject VARCHAR(250) NOT NULL,
Notes VARCHAR(8000) NOT NULL,
Completed BIT NOT NULL DEFAULT 0
)
-- This table will link a phone call with a ticket.
CREATE TABLE dbo.PhoneCalls_Tickets
(
PhoneCallID INT NOT NULL,
TicketID INT NOT NULL
)

Make own like system for various content

There are three types of content in my database. They are Songs, Albums and Playlists. Albums and Playlists are just collections of songs. And I want to let the user put like for each of them. I made table with columns
LikeId UserId SongId PlaylistId AlbumId
for storing likes. For example if user puts like to song, I put song's id into SongId column and user's id into UserId column. Other columns will be null. It's working good,but I don't like this solution because it's not normalized.
So I want to ask if there are better solutions for this.
You should just create 3 tables - one for User paired with each of Playlist, Song, and Album. They'd look something like:
CREATE TABLE PlaylistLikes
(
UserID INT NOT NULL,
PlaylistID INT NOT NULL,
PRIMARY KEY (UserID, PlaylistID),
FOREIGN KEY (UserID) REFERENCES Users (UserID),
FOREIGN KEY (PlaylistID) REFERENCES Playlists (PlaylistID)
);
CREATE TABLE SongLikes
(
UserID INT NOT NULL,
SongID INT NOT NULL,
PRIMARY KEY (UserID, SongID),
FOREIGN KEY (UserID) REFERENCES Users (UserID),
FOREIGN KEY (SongID) REFERENCES Songs (SongID)
);
CREATE TABLE AlbumLikes
(
UserID INT NOT NULL,
AlbumID INT NOT NULL,
PRIMARY KEY (UserID, AlbumID),
FOREIGN KEY (UserID) REFERENCES Users (UserID),
FOREIGN KEY (AlbumID) REFERENCES Albums (AlbumID)
);
Here, having both columns in the primary key prevents the user from liking the song/playlist/album more than once (unless you want that to be available - then remove it or maybe keep track of that in a 'number of likes' column).
You should avoid putting all 3 different types of likes in the same table - different tables should be used to represent different things. You want to avoid "One True Lookup Table" - here's one answer detailing why: OTLT
If you want to query against all 3 tables, you can create a view which is the result of a UNION between the 3 tables.
How about
LikeId UserId LikeType TargetId
Where LikeType can be "Song", "Playlist" or "Album" ?
Your solution is fine. It has the nice feature that you can set up explicit foreign key relationships to the other tables. In addition, you can verify that exactly one of the values is set by adding a check constraint:
check ((case when SongId is null then 0 else 1 end) +
(case when AlbumId is null then 0 else 1 end) +
(case when PlayListId is null then 0 else 1 end)
) = 1
There is an overhead incurred, of storing NULL values for all three. This is fairly minimal for three values.
You can even add a computed column to get which value is stored:
WhichId = (case when SongId is not null then 'Song'
when AlbumId is not null then 'Album'
when PlayListId is not null then 'PlayList
end);
As a glutton for punishment, I would use three tables: UserLikesSongs, UserLikesPlaylists and UserLikesAlbums. Each contains a UserId and an appropriate reference to one of the other tables: Songs, Albums or Playlists.
This also allows adding additional type-specific information. Perhaps Albums will support a favorite track in the future.
You can always use UNION to combine data from the various entity types.

Resources