So I have a few tables in a database setup and I'm wondering what the best way to do something is.
Essentially I have a 1-to-many relationship where a single user can "own" multiple items, which is represented by an array in php, which will likely return the unique item number, type, and values of the item. How would I go about storing that in a MySql table?
Would it be best to have a lookup table of every single item along with it's ID and present owner? Would it make more sense to put a line item in the user table that had a csv list of every item owned by every player (that would be a nightmare to index?)
What makes sense here?
Example:
class character contains items[]
character 1 has the following entries in his items list:
1, 2, 5, 10,11,12
character 2 has the following entries in his items list:
3,4,6,7,8,9,13
What would be a decent way to store this data?
Thanks,
Never, ever store delimited string values in a database. Normalize your data by creating a many-to-many table instead. That way you'll be able to normally query your data.
That being said your schema might look like
CREATE TABLE characters
(
char_id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(32),
...
) ENGINE = InnoDB;
CREATE TABLE items
(
item_id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(6),
...
) ENGINE = InnoDB;
CREATE TABLE item_list
(
char_id INT NOT NULL,
item_id INT NOT NULL,
PRIMARY KEY (char_id, item_id),
FOREIGN KEY (char_id) REFERENCES characters (char_id),
FOREIGN KEY (item_id) REFERENCES items (item_id)
) ENGINE = InnoDB;
If you later need to produce a delimited list of items per character so that you can then explode this values while you iterating over the resultset you can use a query like this
SELECT char_id, GROUP_CONCAT(item_id) items
FROM item_list
-- WHERE ...
GROUP BY char_id
Here is SQLFiddle demo
Related
I have three objetcs per se, Clients, Products and Orders.
Clients is set up with its own values as are the products.
The problem arises when I need to set up a table for the orders since though it only has one client, therefore a one-way relationship is done easily, I cant think of how to make the list of products within the order (which is of a variable size).
Eg case:
Client table has following fields:ID,Name
Product table has following fields: ID,Name,Price
Now in order to create a table for orders I have this problem:
Order:
Id = 001
Client_ID = 002
(linked to client table)
Products = array? eg. ["milk","tomatoes","Thin_Crust Ham & Cheese Pizza no_Gluten"] (would use their ID this is just to visualize it)
When I first searched for this the most common answer was to create another table.
From what I have seen creating another table is not really possible since in those examples they are unique within the newly created table (eg. someone wanted to create a field to store multiple phone numbers for one person within the "person" table, so they can create a table of telf.numbers since they are unique and links them back to the "person" in question.)
The other option I have seen is just using a large varchar field with commas in between values.
If this is the only other way of doing so would there not be a problem if we reach the char limit per field?
This is a very common scenario in database design, you are looking to create a n:m (Many to Many) relationship between the order and the product. This can be achieved with a linking table.
you could use a comma-delimited string, JSON, XML or other serialization method to store this data in a single string column, but that complicates the querying of your data and you lose some of the power that using an RDBMS gives you.
Other RDBMS allow VARCHAR(MAX) which alleviates the field length issue when storing serialized data like this, in MySQL just set the field length to a very large number, or use the max value like VARCHAR(65535). See this topic for more help if you go down this route.
In the conceptual case of an Order, this is generally solved by adding a child table OrderItem. (or OrderLine) If you see this data in a report of a receipt, each of these items is a line on the receipt so you might see this referred to as a Line or Line Items approach. The minimum fields you need for this in your model are:
ID
Order_ID
Product_ID
Other common fields you might consider for a table like this include:
Qty: for scenarios where the user might select Extra Tomatoes, or you can simply allow multiple rows with the same Product_ID, perhaps you want both?
Cost_TaxEx: total cost of the Line Item excluding tax
Cost: total cost _including_tax.
This can be minimally represented in SQL like this:
CREATE TABLE Client (
ID INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
Name VARCHAR(100)
)
CREATE TABLE Product (
ID INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
Name VARCHAR(100),
Price DECIMAL(13,2) /* Just an assumption on length */
)
CREATE TABLE Order (
ID INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
Client_ID INT NOT NULL,
/* ... Insert other fields here ... */
FOREIGN KEY (Client_ID)
REFERENCES Client (ID)
)
CREATE TABLE OrderItem (
ID INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
Order_ID INT NOT NULL,
Product_ID INT NOT NULL,
/* ... Insert other fields here ... */
FOREIGN KEY (Order_ID)
REFERENCES Order (ID)
ON UPDATE RESTRICT ON DELETE CASCADE, /* the cascade on order:orderitem is up to you */
FOREIGN KEY (Product_ID)
REFERENCES Product (ID) /*DO NOT cascade this relationship! */
)
The above solution allows any number of Product entries in an Order but will also allow duplicate Product's, If you need to enforce only one of each product per Order, you can add a Unique Constraint to the OrderItem table:
CREATE TABLE OrderItem (
ID INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
Order_ID INT NOT NULL,
Product_ID INT NOT NULL,
/* ... Insert other fields here ... */
UNIQUE(Order_ID,Product_ID),
FOREIGN KEY (Order_ID)
REFERENCES Order (ID)
ON UPDATE RESTRICT ON DELETE CASCADE, /* the cascade on order:orderitem is up to you */
FOREIGN KEY (Product_ID)
REFERENCES Product (ID) /*DO NOT cascade this relationship! */
)
Let's say that I have the following tables:
Elements:
(int) ID
(int) Name_id
Names:
(int) ID
(varchar) Name
The Elements.Name_id has an internal relation with Names.id. The user enters an element name in a html form. Then a php script inserts the element name to the Elements table. And here comes my question:
Is it possible to specify the element name instead of the element name_id in the insert query? If not I will have to:
SELECT `ID` FROM `Names` WHERE `Names`.`Name` LIKE "$php_variable";
...
INSERT INTO `Elements` SET `Name` = "$php_variable";
Of course the second $php_variable is a result from the previous query.
You can just do:
insert into elements(name)
select n.name
from names n
where n.id = $php_variable;
Now, having said this, do not take this approach. Your data structure is wrong. You want to connect to names.id, not names.name, in most cases. (There are some exceptions where copying the name might be one solution for a slowly changing dimension, but I doubt that is your use-case.)
So, fix the data structure. The create table statements would look like:
create table names (
name_id int auto_increment primary key,
name varchar(255) unique
);
create table elements (
element_id int auto_increment primary key,
name_id int,
foreign key (name_id) references names(name_id)
);
Then use an explicit JOIN to get the name when you need it.
Note: I changed the ids of the table to be "tablename_Id". This is not required, but I find it helpful to have foreign keys and primary keys have the same names.
I need some advice for the choice of my table structure.
I am working on a project where I need to save values that are a combination of a variable amount of other values.
For example:
A = b,c,d
B = z,r
I was thinking on saving the combinations in a json object inside a column but I am afraid it can be long for big requests and not easy for filtering.
There was also the solution of having a multiple amount of columns (containing null when not necessary), but this will not be a good representation of the data, also filtering will be hard.
Finally I thought the best would be many to many relations, but the joins might be too heavy, are they ?
Do you see any other alternative (besides switching to nosql) ?
This shows the use of Junction tables to avoid saving data in comma separated lists, json, or other mechanisms that would be problematic in at least these areas:
Tables-scans (slowness, non-use of fast indexes)
Maintenance of data
Data integrity
Schema
create table cat
( -- categories
id int auto_increment primary key,
code varchar(20) not null,
description varchar(255) not null
);
create table subcat
( -- sub categories
id int auto_increment primary key,
code varchar(20) not null,
description varchar(255) not null
);
create table csJunction
( -- JUNCTION table for cat / sub categories
-- Note: you could ditch the id below, and go with composite PK on (catId,subCatId)
-- but this makes the PK (primary key) thinner when used elsewhere
id int auto_increment primary key,
catId int not null,
subCatId int not null,
CONSTRAINT fk_csj_cat FOREIGN KEY (catId) REFERENCES cat(id),
CONSTRAINT fk_csj_subcat FOREIGN KEY (subCatId) REFERENCES subcat(id),
unique key (catId,subCatId) -- prevents duplicates
);
insert cat(code,description) values('A','descr for A'),('B','descr for B'); -- id's 1,2 respectively
insert subcat(code,description) values('b','descr for b'),('c','descr for c'),('d','descr for d'); -- id's 1,2,3
insert subcat(code,description) values('r','descr for r'),('z','descr for z'); -- id's 4,5
-- Note that due to the thinness of PK's, chosen for performance, the below is by ID
insert csJunction(catId,subCatId) values(1,1),(1,2),(1,3); -- A gets a,b,c
insert csJunction(catId,subCatId) values(2,4),(2,5); -- B gets r,z
Good Errors
The following errors are good and expected, data is kept clean
insert csJunction(catId,subCatId) values(2,4); -- duplicates not allowed (Error: 1062)
insert csJunction(catId,subCatId) values(13,4); -- junk data violates FK constraint (Error: 1452)
Other comments
In response to your comments, data is cached only in so far as mysql has a Most Recently Used (MRU) strategy, no more or less than any data cached in memory versus physical lookup.
The fact that B may contain not only z,r at the moment, but it could also contain c as does A, does not mean there is a repeat. And as seen in the schema, no parent can duplicate its containment (or repeat) of a child, which would be a data problem anyway.
Note that one could easily go the route of PK's in cat and subcat using the code column. That would unfortunately cause wide indexes, and even wider composite indexes for the junction table. That would slow operations down considerably. Though the data maintenance could be visually more appealing, I lean toward performance over appearance any day.
I will add to this Answer when time permits to show such things as "What categories contain a certain subcategory", deletes, etc.
A my-sql database table is having millions of data records.This table consists of a primary key [say user id],serial number [can have duplicates] and some other columns which allows null values.
Eg: say the schema is
CREATE TABLE IF NOT EXISTS SAMPLE_TABLE (
USER_ID INTEGER NOT NULL,
SERIAL_NO NOT NULL,
DESCRIPTION VARCHAR(100),
PRIMARY KEY (USER_ID)
)ENGINE INNODB;
Now I want to search a data row,based on the serial number.
I tried first adding a unique index including both columns [user id and serial no.] as
CREATE UNIQUE INDEX INDEX_USERS ON U=SAMPLE_TABLE (USER_ID,SERIAL_NO);
and then search for the data query based on serial number as below;
SELECT * FROM SAMPLE_TABLE WHERE SERIAL_NO=?
But it didn't success and I'm getting OOM error in mysql server side when I execute above select query. Appreciate any help on this.
You should not have added user_id intobthecindex you created. You just need an index on serial_no for that query.
If you provides necessary codes,it would be better than given explainations..However first you should find the id references to seraial number,then search the column corresponding to id
I created a table in MySQL. I want to store a name, a surname and a vector of doubles, but the problem is: how do I create a vector column in MySQL? My vector contains 130 elements.
There are essentially two ways you can do that.
A simple one is to create a LONGBLOB or LONGTEXT field where you will store a serialized version of your vector.
But this is a quite ugly solution from a database modeling perspective because the DBMS is not capable of performing searches or to index the content of those vectors.
The correct way would be to use two tables in a 1-to-many relationship.
It means, you would have a table table_A with the following structure:
CREATE TABLE table_A ( -- records
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
name TEXT,
surname TEXT,
PRIMARY KEY (id)
);
And a table table_B containing the values in the vector and an association with their respective records in table_A:
CREATE TABLE table_B ( -- vector values
parent INT UNSIGNED NOT NULL,
id INT UNSIGNED NOT NULL, -- for indexing, in case the order of the vector elements matter
value TEXT,
PRIMARY KEY (parent, id),
FOREIGN KEY (parent) REFERENCES table_A (id) ON DELETE CASCADE ON UPDATE CASCADE
);
Working exemple: http://sqlfiddle.com/#!2/79521/2
With this format you are capable of allowing the DBMS to perform searches and manage the values of the vectors.
I suggest you to take a look at the JSON data type. This way you can store your vector in a more efficient way than text or varchar, and you can access your data directly form MySQL without having to parse the whole thing.
Take a look at this link : https://dev.mysql.com/doc/refman/5.7/en/json.html
You'll just have to store your vector in a JSON form like this [24, 12, 54, 39, ...]