Unique constraint based on associated column value - mysql

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.

Related

MySQL UPDATE | How to update filepath URI that saved in database

I have a table album that contains about 1000 rows. I want to update all rows.
Table album have a column named path. See full table structure below.
+---------------------------------------------------------------------------------------------+
| id | name | path |
+---------------------------------------------------------------------------------------------+
| 1 | Believe | Believe |
+---------+-----------+-----------------------------------------------------------------------+
| 2 | A Promise | A Promise |
+---------+----------+------------------------------------------------------------------------+
| 3 | Forever | Forever |
+---------------------------------------------------------------------------------------------+
I want to update path here. Want to add album in path for SEO friendly URLs. table should look like this for that goal.
+--------------------------------------------------------------------------------------------------+
| id | name | path |
+--------------------------------------------------------------------------------------------------+
| 1 | Believe | Believe |
+---------+-----------+----------------------------------------------------------------------------+
| 2 | A Promise | A Promise|
+---------+----------+-----------------------------------------------------------------------------+
| 3 | Forever | Forever |
+--------------------------------------------------------------------------------------------------+
I can't figure out how to use LIKE here, what to insert between % %. Please help me. Thanks in advance.

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

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.

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

same query, dramatic different performances on different data. MySQL

So, I have this huge database with 40 Millions entries.
the query is a simple (a_view is a view!)
select * from a_view where id > x LIMIT 10000
this is the behavior I get: If x is a little number (int) the query is super fast.
when x > 29 Millions the query starts to take minutes. if it is closer to 30 Millions it takes hours. and so on...
why is that? what can I do to avoid this?
I am using InnoDB as engine, tables have indexes.
the value of the limit is a critical one, it affects performances. if it is small the query is always fast. but if x is close to 30Millions then I need to be very careful to set it not too big (less than 300 hundreds), and still it is quite slow, but doesn't take forever
If you need more details, feel free to ask.
EDIT: here is the explain
+----+-------------+-------+--------+-----------------+---------+---------+---------------------+---------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+-----------------+---------+---------+---------------------+---------+-------------+
| 1 | SIMPLE | aH | index | PRIMARY | PRIMARY | 39 | NULL | 3028439 | Using index |
| 1 | SIMPLE | a | eq_ref | PRIMARY | PRIMARY | 4 | odb.aH.albumID | 1 | Using where |
| 1 | SIMPLE | aHT | ref | PRIMARY,albumID | albumID | 4 | odb.a.albumID | 4 | |
| 1 | SIMPLE | t | eq_ref | PRIMARY | PRIMARY | 4 | odb.aHT.id | 1 | Using where |
| 1 | SIMPLE | g | eq_ref | PRIMARY | PRIMARY | 4 | odb.t.genre | 1 | |
| 1 | SIMPLE | ar | eq_ref | PRIMARY | PRIMARY | 4 | odb.t.artist | 1 | |
+----+-------------+-------+--------+-----------------+---------+---------+---------------------+---------+-------------+
Here is a guess. Basically, your view is a select on some tables. The "id" could be a row number. The larger your "x" is, the more select rows need to be created (and discarded) before you can get whatever data you want. That is why your query slows down when your "x" increases.
If this is true, one solution could be to create a table that contains the rownum and a primary key sorted by whatever "order by" you are using. Once you have that table, you can join it with the rest of your data and select your data window by a rownum range.

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.