How should I structure my simple database? - sql

I am creating a database (for data from an online game) in which I need to store Players and their Villages. Each Player has at least 1 village. All Villages are unique and every Village has exactly 1 owner(Player).
It is probably important to mention, that I intend to create a new table/tables every day since the online game in question releases an updated game world data file once every 24hours. Once a table is created, I will never have to make changes in it, I will only need to read data from it.
I came up with 2 solutions for now, but I dont know what the right approach is.
Solution1:
CREATE TYPE village_t AS (
id INTEGER,
x INTEGER,
y INTEGER,
village_id INTEGER,
village_name VARCHAR,
pop INTEGER,
region VARCHAR
);
CREATE TABLE players (
tribe INTEGER NOT NULL,
user_id INTEGER PRIMARY KEY,
user_name VARCHAR(30) UNIQUE NOT NULL,
aliance_id INTEGER NOT NULL,
aliance_name VARCHAR(30) NOT NULL,
villages village_t[]
);
Solution2:
CREATE TABLE players (
tribe INTEGER NOT NULL,
user_id INTEGER PRIMARY KEY,
user_name VARCHAR(30) UNIQUE NOT NULL,
aliance_id INTEGER NOT NULL,
aliance_name VARCHAR(30) NOT NULL
);
CREATE TABLE villages (
id INTEGER NOT NULL,
x INTEGER NOT NULL,
y INTEGER NOT NULL,
village_id INTEGER NOT NULL,
village_name VARCHAR NOT NULL,
user_id INTEGER,
CONSTRAINT fk_con FOREIGN KEY(user_id) REFERENCES players(user_id),
pop INTEGER NOT NULL,
region VARCHAR
);
CREATE INDEX idx ON villages(user_id);

You have a 1-n relationship. Each village ("n") has one owner ("1"). Your second solution is the canonical way of storing this information in a database.
Your first solution does not even enforce the notion that a village as one owner, so I'm not sure why you are considering it. The second imposes this condition as part of the data model.
Your second solution has set up the recommend foreign key and indexes for what you describe, by the way, so it looks fine.

If you need to read village data frequently, Solution 1 is not recommended. This is because the list of village ids design removes the scope of joining the person and village table
Solution 2 is the most preferred way of going about as it keeps all your options open (whether you would frequently need village data or not)
Another way of going about solution 2 is to create a cross-reference table for user_id and village_id
CREATE TABLE players (
tribe INTEGER NOT NULL,
user_id INTEGER PRIMARY KEY,
user_name VARCHAR(30) UNIQUE NOT NULL,
aliance_id INTEGER NOT NULL,
aliance_name VARCHAR(30) NOT NULL
);
CREATE TABLE villages (
id INTEGER NOT NULL,
x INTEGER NOT NULL,
y INTEGER NOT NULL,
village_id INTEGER NOT NULL,
village_name VARCHAR NOT NULL,
pop INTEGER NOT NULL,
region VARCHAR
);
CREATE TABLE xref_players_villages (
user_id INTEGER,
CONSTRAINT fk_con1 FOREIGN KEY(user_id) REFERENCES players(user_id),
village_id INTEGER,
CONSTRAINT fk_con2 FOREIGN KEY(village_id ) REFERENCES villages(village_id ),
);

Related

How to design a friend list in sql /sqlite

Im relatively new to sql and Im trying to create a relationship between two users.
A friend request has to be sent and accepted to be friends with each other. Now I have a third table called items. A user should now be able to see all items that are owned by his friends. Im kinda stuck writing this query...
item table:
CREATE TABLE IF NOT EXISTS item(
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
owner VARCHAR(50) NOT NULL,
borrowedBy VARCHAR(50),
imageUrl TEXT
);
friendship table:
CREATE TABLE IF NOT EXISTS friendship(
friendship_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL
);
user_friendship
CREATE TABLE IF NOT EXISTS user_friendship(
username VARCHAR(50) NOT NULL,
friendship_id INTEGER NOT NULL,
status INTEGER NOT NULL,
PRIMARY KEY (username, friendship_id)
);
You need to look at tutorials on how to build a SQL database, mainly on table relationship.
Here, for example, you don't need separate friendship and user_friendship table.
You could have one friendship table:
CREATE TABLE IF NOT EXISTS friendship(
friendship_id INTEGER NOT NULL,
username1 VARCHAR(50) NOT NULL,
username2 VARCHAR(50) NOT NULL,
status INTEGER NOT NULL,
PRIMARY KEY (friendship_id, username1 , username2 )
);
Note that you should use integer value for foreign keys, not varchar(50).

SQL library trigger

Im trying to make a library database. I would like to ensure that one book can be borrowed just to one person at a time. I have no expirience with triggers so I thought I might ask you.
create table "book" (
"book_id" INTEGER not null,
"condition" VARCHAR2(50),
"isbn" VARCHAR2(50) not null,
constraint PK_BOOK primary key ("book_id")
);
create table "borrowed" (
"book_id" INTEGER not null,
"borrowed_id" INTEGER not null,
"user_id" INTEGER not null,
"date_borrowing" DATE not null,
"date_returning" DATE not null,
"returned" SMALLINT not null,
constraint PK_BORROWED primary key ("book_id", "borrowed_id")
);
atribute "returned" has just yes or no value (1 or 0)
You don't need a trigger for this. Your borrowed table should be structured like this. Note the use of a virtual column, which requires Oracle 11g+:
create table borrowed (
borrowed_id INTEGER not null primary key, -- this should be set to a unique id for each row, using your preferred method
book_id INTEGER not null,
user_id INTEGER not null,
date_borrowing DATE not null,
date_returning DATE not null,
date_actualreturn DATE,
returned as (case date_actualreturn is null then 0 else 1 end)
);
Then, you want date_actualreturn to have at most one NULL value per book. You can do this with a unique index or constraint:
create table borrowed (
borrowed_id INTEGER not null primary key, -- this should be set to a unique id for each row, using your preferred method
book_id INTEGER not null,
user_id INTEGER not null,
date_borrowing DATE not null,
date_returning DATE not null,
date_actualreturn DATE,
returned as (case date_actualreturn is null then 0 else 1 end),
constraint unq_only_one_book_borrowed unique
(coalesce(date_actualreturn, date '1900-01-01'), book_id)
);
This creates a unique constraint on the column. If a book has not been returned, the date always looks the same -- so the same book cannot be borrowed twice. And, voila! No trigger.

Designing a recipe database

I'm making a simple recipe app that allows user to create and save their recipes. I'm using SQLite if that makes any difference.
My database knowledge is a bit rusty so I've been looking around for inspiration in order to design an appropriate database.
One example that users here seem to point to is the following schema by Barry Williams: http://www.databaseanswers.org/data_models/recipes/index.htm
This db is overly detailed so I decided to used only the following tables:
Recipe (recipe_id(PK), name, description)
Ingredient (ingredient_id(PK), name)
Recipe_Steps (recipe_id(PF), step_number(PK), instructions)
Recipe_Step_Ingredients(recipe_id(PF), step_number(PF), ingredient_id(PF), amount)
From my understanding, each ingredient is associated to a step in the recipe whereas I feel like each ingredient should be associated directly to a recipe and that the steps could be an attribute of the recipe table or in a Step table directly related to Recipe. Here would be my design:
Recipe (recipe_id(PK), name, description)
Ingredient (ingredient_id(PK), name)
Recipe_Steps (recipe_id(PF), step_number(PK), instructions)
Recipe_Ingredients (recipe_id(PF), ingredient_id(PF), amount)
Mr. Williams knows much more about databases than I do so I don't know if I'm misunderstanding his schema and screwing it up with my version or if this all comes down to personal preference in design.
Is my version of the schema correct, if not, would you please be able to guide me towards the appropriate answer?
Thanks in advance.
Here is the database schema I used to implement the AnyMeal recipe software:
PRAGMA user_version = 1;
BEGIN;
CREATE TABLE recipes(id INTEGER PRIMARY KEY, title VARCHAR(60) NOT NULL, servings INTEGER NOT NULL, servingsunit VARCHAR(40) NOT NULL);
CREATE TABLE categories(id INTEGER PRIMARY KEY, name VARCHAR(40) UNIQUE NOT NULL);
CREATE TABLE category(recipeid INTEGER NOT NULL, categoryid INTEGER NOT NULL, PRIMARY KEY(recipeid, categoryid), FOREIGN KEY(recipeid) REFERENCES recipes(id), FOREIGN KEY(categoryid) REFERENCES categories(id));
CREATE TABLE ingredients(id INTEGER PRIMARY KEY, name VARCHAR(60) UNIQUE NOT NULL);
CREATE TABLE ingredient(recipeid INTEGER NOT NULL, line INTEGER NOT NULL, amountint INTEGER NOT NULL, amountnum INTEGER NOT NULL, amountdenom INTEGER NOT NULL, amountfloat REAL NOT NULL, unit CHARACTER(2) NOT NULL, ingredientid INTEGER NOT NULL, PRIMARY KEY(recipeid, line), FOREIGN KEY(recipeid) REFERENCES recipes(id), FOREIGN KEY(ingredientid) REFERENCES ingredients(id));
CREATE TABLE instruction(recipeid INTEGER NOT NULL, line INTEGER NOT NULL, txt TEXT NOT NULL, PRIMARY KEY(recipeid, line), FOREIGN KEY(recipeid) REFERENCES recipes(id));
CREATE TABLE ingredientsection(recipeid INTEGER NOT NULL, line INTEGER NOT NULL, title VARCHAR(60) NOT NULL, PRIMARY KEY (recipeid, line), FOREIGN KEY(recipeid) REFERENCES recipes(id));
CREATE TABLE instructionsection(recipeid INTEGER NOT NULL, line INTEGER NOT NULL, title VARCHAR(60) NOT NULL, PRIMARY KEY (recipeid, line), FOREIGN KEY(recipeid) REFERENCES recipes(id));
COMMIT;

Create views for user profile management

I have the below schema for user profile management,
CREATE TABLE IF NOT EXISTS users
(
userid TEXT NOT NULL,
name TEXT NULL,
lmessage INTEGER NULL,
statusid INTEGER NULL,
/* statusid should refer to last status of the user in status table*/
locationid INTEGER NULL,
/* locationid should refer to last status of the user in locations table */
registered INTEGER NOT NULL,
tinypic INTEGER NULL
/* this refers to media id in media table */,
largepic INTEGER NULL
/* this also refers to media id in media table */,
groupid INTEGER NULL
/* this refers to id in groups table */ ,
PRIMARY KEY (userid)
);
CREATE TABLE IF NOT EXISTS locations
(
serial INTEGER,
locationid TEXT NOT NULL,
userid TEXT NOT NULL,
time INTEGER NULL,
PRIMARY KEY (serial)
);
CREATE TABLE IF NOT EXISTS status
(
serial INTEGER,
userid TEXT NULL,
message TEXT NOT NULL,
time INTEGER NULL,
PRIMARY KEY (serial)
);
CREATE TABLE IF NOT EXISTS messages
(
sno INTEGER,
messageid INTEGER NOT NULL,
sender TEXT NOT NULL,
receiver TEXT NOT NULL,
time INTEGER NULL,
message TEXT NULL,
image INTEGER NULL,
video INTEGER NULL,
audio INTEGER NULL,
PRIMARY KEY (sno)
);
CREATE TABLE IF NOT EXISTS media
(
mediaid TEXT NOT NULL UNIQUE,
url TEXT NULL,
downloaded INTEGER NULL,
thumbnail TEXT NULL,
PRIMARY KEY (mediaid)
);
CREATE TABLE IF NOT EXISTS groups
(
serial INTEGER,
name TEXT NOT NULL,
id INTEGER NOT NULL
PRIMARY KEY(serial)
);
CREATE UNIQUE INDEX IF NOT EXISTS id_unique ON users (userid ASC);
CREATE UNIQUE INDEX IF NOT EXISTS serial_unique ON status (serial ASC);
CREATE UNIQUE INDEX IF NOT EXISTS id_unique ON messages (sno ASC);
CREATE UNIQUE INDEX IF NOT EXISTS serial_unique ON patterns (serial DESC);
CREATE UNIQUE INDEX IF NOT EXISTS mediaid_unique ON media (mediaid ASC);
How can create views on user table to get list of users based on filter conditions. Please suggest me schema design is not good.
A view example that I would like to add on this schema :
Select all users who belong to group and all groups created after that group.
Select all users with last message, status, location and media urls included.
Thanks.
Please Note that I am SQL nube, please forgive me if you feel like this is immature question. All I need is, I want to learn from reviews of other people.
for 1 - you need to have timestamp (INTEGER id you go by UTC ticks) in group table - it is missing
for 2 - you can do it with join
bot case i don't think you need to have a view unless you have some specific reasons. You can go by select queries with join to get required data.
I recommend you to use http://www.sqliteexpert.com/download.html - try out creating the schema there and try out all your queries before you get in to implementing it in android

SQL Unknown number of attributes

I have a one-to-many relationship between two classes for this situation. I have a swimming competition and that competition can have x swimmers.
How can I create an SQL table for this, I know I will have to use the Primary Key of Swimmers as a foreign key in the Swimming competition but I have no idea how to represent the correct number of attributes since it's unknown.
This is called a m:n relationship and usually solved with a mapping table.
Something like this:
create table swimmer
(
id integer not null primary key,
lastname varchar(100) not null,
firstname varchar(100)
)
create table competition
(
id integer not null primary key,
name varchar(50) not null,
comp_date date not null
)
create table participant
(
swimmer_id integer not null,
competition_id integer not null,
rank_in_competetion integer,
primary key (swimmer_id, competition_id),
constraint fk_participant_swimmer (swimmer_id) references swimmer(id),
constraint fk_participant_competition (competition_id) references competition(id)
)