How do I share resources in a relational-database (specific case)? - mysql

Say I have a table Dogs
mysql> select * from dogs;
+----------+-------------+----------+
| dog_id | tail_length | owner_id |
+----------+-------------+----------+
| dog-id-1 | 1 cm | owner-1 |
| dog-id-2 | 2 cm | owner-2 |
| dog-id-3 | 3 cm | owner-3 |
+----------+-------------+----------+
Where dog_id is a primary key and owner_id is unique.
Now I want sharing of dogs to be possible. so the table can be.
+----------+-------------+----------+
| dog_id | tail_length | owner_id |
+----------+-------------+----------+
| dog-id-1 | 1 cm | owner-1 |
| dog-id-2 | 2 cm | owner-2 |
| dog-id-3 | 3 cm | owner-3 |
| dog-id-3 | 3 cm | owner-1 |
| dog-id-3 | 3 cm | owner-1 |
+----------+-------------+----------+
But it is not possible as dog_id is a primary key and owner_id is unique in the table. A dog can possible be shared with 10000+ users.
Due to constraints of backward compatibility I cannot remove the primary and unique key constraints of the original table and I have to use mysql to do this. What would be the best strategy to achieve sharing?
Additional Constraint: I can only query through dog_id and not owner_id.

What you have is a many-to-many relationship.
A dog can have many owners
An individual owner can own many dogs
This is a common problem in relational database design. What you need for a many-to-many relationship is to define another table.
+----------+----------+
| dog_id | owner_id |
+----------+----------+
| dog-id-1 | owner-1 |
| dog-id-2 | owner-2 |
| dog-id-3 | owner-3 |
| dog-id-3 | owner-1 |
| dog-id-3 | owner-1 |
+----------+----------+
I can only query through dog_id and not owner_id
I don't understand why this is relevant. The data organization is about Third Normal Form, not about how you will query the data.
The only alternative you have is to store multiple owner ids in one column of one row, which is not a valid design for a relational database. See my answer to Is storing a delimited list in a database column really that bad?

Create another table Owners and supply a Foreign Key to the table Dogs.

Related

Unique constraint based on associated column value

I have the following (simplified) data structure:
Property Product
----------- n <----> 1 -------
product_id manufacturer
type name
value
Where the products table might look like this
| name |
|--------------|
| iPad 2 |
| iPhone 6 |
and the associated properties might be
| product_id | type | value |
|----------------------------------|
| 1 | RAM | 16GB |
| 1 | CPU | A11 |
| 1 | Screen Size | 10" |
| 2 | Cellular | yes |
| 2 | Screen Size | 5.5" |
Is it possible, in MySQL, to create a constraint, so that every product can have each property type at most once (e.g., a product can't have multiple Screen Sizes associated. As far as I could understand the MySQL docs, constraints only work on one relation, but not across relations.
I realized the solution immediately after posting this question. The constraint can be placed on the properties relation, so that each pair of product_id, type can only exist once.

MySQL Can Foreign Key Be Null

Question
Is it valid to have a Null foreign key? Are there any disadvantages?
Example
______ _________________
| id | | id | id_fk |
| 1 | | 1 | 2 |
| 2 | | 2 | 5 |
| 3 | | 3 | |
| 4 | | 4 | 1 |
| 5 | | 5 | |
‾‾‾‾‾‾ ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
Yes it is possible, and no it is not bad practice. You will want to make sure you take this into account when running queries on your database. If you attempt to run an inner join with your selects, then you will automatically exclude all rows with a null value. In cases where you want to join on that relationship even if the foreign key is null, you will want to use an outer join.

Mysql 5.6 not using index in subquery

I have a table of parents, and a table of children
My parents table is as follows:
+----------------+-------------------------------+
| parent_id | parent_name |
+----------------+-------------------------------+
| 10792 | Katy |
| 7562 | Alex |
| 13330 | Drew |
| 9153 | Brian |
+----------------+-------------------------------+
My children's table is:
+----------+-------------------------------+-----------+-----+
| child_id | child_name | parent_id | age |
+----------+-------------------------------+-----------+-----+
| 1 | Matthew | 10792 | 4 |
| 2 | Donald | 9153 | 5 |
| 3 | Steven | 10792 | 9 |
| 4 | John | 7562 | 6 |
+----------+-------------------------------+-----------+-----+
When I use a sub-select such as:
SELECT parent_name, (SELECT SUM(age) FROM children WHERE parent_id = parents.parent_id) AS combined_age FROM parents;
My issue is that when I execute this query (parents are 13,000 records, children are 21,000 records) an index of parent_id in children doesn't get used, as shown in the explain plan. I get:
+----+--------------------+--------------------------+--------+---------------+------+---------+-------+-------+-------------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+--------------------+--------------------------+--------+---------------+------+---------+-------+-------+-------------------------------------------------+
| 1 | PRIMARY | parents | ALL | NULL | NULL | NULL | NULL | 13548 | NULL
| 2 | DEPENDENT SUBQUERY | children | ALL | PARENTS,AGE | NULL | NULL | NULL | 21654 | Range checked for each record (index map: 0x22) |
+----+--------------------+--------------------------+--------+---------------+------+---------+-------+-------+-------------------------------------------------+
This query is taking over 3 minutes to run, and I can't seem to get the subquery to use an index to query where the children belong to the parent. I tried USE INDEX and FORCE INDEX, as well as USE KEY AND FORCE KEY. Any ideas?
So It turns out this happens when the two ID fields are not the same field type INT(11) VS. VARCHAR(45). In the application, one of the table's ID fields was created strangely. Updating the field type solved the SQL optimizer.
Thanks!
The index should be used. The optimal index would be children(parent_id, age).
You can try re-writing the query as a join:
select p.parent_name, sum(c.age)
from parents p left join
children c
on p.parent_id = c.parent_id
group by p.parent_name;

How to implement a superclass/subclass structure in MySQL?

These are the tables in my database, I need to create a couple superclass/subclass structures.
The first is where...
Superclass-Crew_Member
Subclasses-Director, Producer, Other_Directing, Other_Production, Art, Camera, Sound, Grip, Electrical, Post.
The second is...
Superclass-Producer
Subclasses-Salaries, Budget
+---------------------+
| Tables_in_film_crew |
+---------------------+
| art |
| budget |
| camera |
| crew_member |
| director |
| electrical |
| equipment |
| grip |
| location |
| manufacturer |
| other_directing |
| other_production |
| post_production |
| producer |
| salaries |
| sound |
+---------------------+
So how exactly would I go about creating those relationships?
Edit:
Maybe I should have clarified some other things too.
Here's what's contained in crew_member (Superclass):
+-------------+-------------+------+-----+-------------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+-------------+------+-----+-------------------+----------------+
| Member_ID | int(5) | NO | PRI | NULL | auto_increment |
| Member_Name | varchar(25) | YES | | [INSERT EXAMPLE] | |
| DOB | date | YES | | [INSERT EXAMPLE] | |
| Address1 | varchar(25) | YES | | [INSERT EXAMPLE] | |
| Address2 | varchar(25) | YES | | [INSERT EXAMPLE] | |
+-------------+-------------+------+-----+-------------------+----------------+
Meanwhile here's what's contained in Other_Directing (Example Subclass):
+---------------+--------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+--------+------+-----+---------+----------------+
| O_Director_ID | int(4) | NO | PRI | NULL | auto_increment |
| FAD_ID | int(5) | NO | MUL | NULL | |
| SAD_ID | int(5) | NO | MUL | NULL | |
| SUD_ID | int(5) | NO | MUL | NULL | |
+---------------+--------+------+-----+---------+----------------+
Now all the foreign keys are referring to Member_ID from Crew_Member. All the other tables (except Director and Producer) are created in similar ways.
You could start by following some general rules which have to be taken in consideration when creating a database. Put the information that different groups have in common in 1 table and the specific data in smaller satellite tables.
I would put the generic information about a crew member in the first table:
so we would have an id, name, address and whatever all the members have in common.
Then you create "sub-tables" which relate to the "crew-member" table through the value crew_member_id. In this tables you put only the specific information related to directors, producers etc..
So the fields here might be something like: id, crew_member_id, directed movies, etc..
Even with the superclass producer you should work in the same way. Relate the subtales with the superclass through its primary key to have relations between them.
I would suggest you to read some articles about database designing. It might save your life in the future, cause after a database is made then it becomes much harder to correct mistakes.
http://www.datanamic.com/support/lt-dez005-introduction-db-modeling.html
Yeah this is a really good question, that I was researching as well.
The ideas I came up with were:
1> Have a parent table as the super class with sattelite tables for the attributes for each subclass joined by a foreign key. You could then represent it as a view.
2> Have a parent table as the super class and another single table for all of the extra attributes. This would have to be matched by joined by two foreign keys.
3> One table which holds all of the classes.(Terrible idea)
Theres other ideas but I think the first is the best bet.
Here's more info that suggests the first way.
http://www.tomjewett.com/dbdesign/dbdesign.php?page=subclass.php

MYSQL - how to a second unique string key?

Here is my goods table.
+----------------------+---------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------------------+---------------+------+-----+---------+-------+
| ID | decimal(18,0) | NO | PRI | | |
| gkey | varchar(255) | YES | MUL | NULL | |
| GOODS | decimal(18,0) | YES | MUL | NULL | |
Column ID is auto-increment.
GOODS is the id of a goods category.
Here is the goods category table.
+-------+---------------------+
| ID | NAME |
+-------+---------------------+
| 1 | book |
| 2 | phone |
+-------+---------------------+
My question is in goods table I need the gkey is also an unique key with prefix-id(here id is started from 1.) Like BOOK-1,BOOK-2....PHONE-1,PHONE-2... when insert a new goods record into goods table.
Like that:
+--------------------+---------------+------+-----+---------+-------+
| ID | GKEY |GOODS | PRI | COUNTRY | Extra |
+--------------------+---------------+------+-----+---------+-------+
| 1 | BOOK-1 | 1 | 10 | | |
| 2 | PHONE-1 | 2 | 12 | | |
| 3 | BOOK-2 | 1 | 13 | | |
| 4 | BOOK-3 | 1 | 10 | | |
| 5 | PHONE-2 | 2 | 10 | | |
| 6 | PHONE-3 | 2 | 20 | | |
+--------------------+---------------+------+-----+---------+-------+
How to get that GKEY in PHP+MYSQL?
thanks.
You can use the UNIQUE constraint. For more information check here.
I don't think you want to be doing this at all. Instead, good.gkey should be a foreign key to goods_category.id. If the insertion order is important, you can add an insert_date date field to the goods table.
From there you can run all sorts of queries, with the added bonus of having referential integrity on your tables.
First you should do everything Database side. So no php. That would be my advice.
Then not having sequence in MySQL that what I would Suggest you :
SELECT COUNT(id)
FROM GOODS
WHERE GKEY LIKE('BOOK-%')
Then you insert
INSERT INTO goods (id, "BOOK-" || SELECT MAX(SUBSTR(LENGTH(GKEY -1), LENGTH(GKEY))FROM GOODS WHERE GKEY LIKE('BOOK-%') + 1, ...)
This way you will always have the next number available.
You can do it, but you need to be quite careful to make sure that two simultaneous inserts don't get given the same gkey. I'd design it with these two design points:
In the GoodsCategory table, have a counter which helps to generate the next gkey.
Use locks so that only one process can generate a new key at any one time.
Here's the details:
1. Add a couple of columns to the GoodsCategory table:
+----------------------+---------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------------------+---------------+------+-----+---------+-------+
| ID | TINYINT | NO | PRI | | |
| NAME | VARCHAR(80) | NO | | | |
| KeyCode | CHAR(5) | NO | | | |
| NextID | INT | NO | | 0 | |
+----------------------+---------------+------+-----+---------+-------+
The KeyCode has 'BOOK' or 'PHONE', and the NextID field stores an int which is used to generate the next key of that type i.e. if the table looks like this:
+----------------+------------+---------+--------+
| ID | NAME | KeyCode | NextID |
+----------------+------------+---------+--------+
| 1 | book | BOOK | 3 |
| 2 | phone | PHONE | 7 |
+----------------+------------+---------+--------+
Then the next time you add a book, it should be given gkey 'BOOK-3', and NextID is incremented to 4.
2: The locking will need to be done in a stored routine, because it involves multiple statements in a transaction and uses local variables too. The core of it should look something like this:
START TRANSACTION;
SELECT KeyCode, NextID INTO v_kc, v_int FROM GoodsCategory WHERE ID = 2 FOR UPDATE;
SET v_newgkey = v_kc + '-' + CAST(v_int AS CHAR);
INSERT INTO goods (gkey, goods, ...) VALUES (v_newgkey, 2, etc);
UPDATE GoodsCategory SET NextID = NextID + 1 WHERE ID = 2;
COMMIT;
The FOR UPDATE bit is crucial; have a look at the Usage Examples in the mysql manual where it discusses how to use locks to generate an ID without interference from another process.