I have this table:
ID | genre_id | is_best_in_genre | movie_name
--------------------------------
1 | 3 | 0 | Hateful Eight
2 | 3 | 0 | Django Unchained
2 | 3 | 1 | Inglorious B
2 | 3 | 0 | Once Upon A Time in Hollywood
is_best_in_genre can only be true (1) once for every genre_id. There can only be 1 best movie in each genre.
How would I make a constraint such as this?
Alternative idea:
The more proper, normalized way to handle this is probably to make a separate 'best_in_genre' table with a unique constraint on genre_id.
This is also easier to update, because you're not required to make sure that everything gets 0'd when selecting a new 'best'.
A better approach might be to "move" the column is_best_in_genre to a separate (joined) table. A simple table with two columns could do the job:
CREATE TABLE best (
idmovie int,
idgenre int,
PRIMARY KEY (idgenre))
This would need to be joined with the original table like:
SELECT m.*, CASE b.idmovie>0 THEN 1 ELSE 0 END best_in_genre
FROM movietable m
LEFT JOIN best b ON idmovie=id AND idgenre=genre_id
The PRIMARY KEY constraint in the table best will make sure that each genre can only appear once.
Related
I have 12 fixed tables (group, local, element, sub_element, service, ...), each table with different numbers of rows.
The columns 'id_' in all table is a primary key (int). The others columns are of datatype varchar(20). The maximum number of rows in these tables are 300.
Each table was created in this way:
CREATE TABLE group
(
id_G int NOT NULL,
name_group varchar(20) NOT NULL,
PRIMARY KEY (id_G)
);
|........GROUP......| |.......LOCAL.......| |.......SERVICE.......|
| id_G | name_group | | id_L | name_local | | id_S | name_service |
+------+------------+ +------+------------+ +------+--------------+
| 1 | group1 | | 1 | local1 | | 1 | service1 |
| 2 | group2 | | 2 | local2 | | 2 | service2 |
And I have one table that combine all these tables depending on user selects.
The 'id_' come from fixed tables selected by the user are recorded into this table.
This table was crate in this way:
CREATE TABLE group
(
id_E int NOT NULL,
event_name varchar(20) NOT NULL,
id_G int NOT NULL,
id_L int NOT NULL,
...
PRIMARY KEY (id_G)
);
The tables (event) look like this:
|....................EVENT.....................|
| id_E | event_name | id_G | id_L | ... |id_S |
+------+-------------+------+------+-----+-----+
| 1 | mater1 | 1 | 1 | ... | 3 |
| 2 | master2 | 2 | 2 | ... | 6 |
This table get greater each day, an now it has about thousunds of rows.
Column id_E is the primary key (int), event_name is varchar(20).
This table has, in addition of id_E and event_name columns, 12 other columns the came from the fixed tables.
Every time than I need to retrieve information on the event table, to turn more readable, I need to do about 12 joins.
My query look like this where i need to retrieve all columns from table event:
SELECT event_name, name_group, name_local ..., name_service
FROM event
INNER JOIN group on event.id_G = group.id_G
INNER JOIN local on event.id_L = local.id_L
...
INNER JOIN service on event.id_S = service.id_S
WHERE event.id_S = 7 (for example)
This slows down my system performance. Is there a way to reduce the number of joins? I've heard about using Natural Keys, but I think this is not a good idea to form my case thinking in future maintenance.
My queries are taking about 7 seconds and I need to reduce this time.
I changed the WHERE clause and this caused not affect. So, I am sure that the problem is that the query has so many joins.
Could someone give some help? thanks a lot...
MySQL has a great keyword of "STRAIGHT_JOIN" and might be what you are looking for. First, each of your lookup tables (id/description) I have to assume already have an index on the ID column since that is primary key.
Your event table is the one you are querying as the primary basis of the details and joining to the lookups per their respective IDs. As long as your WHERE clause applicable to the EVENT table is optimized, such as the ID you are looking for, it SHOULD be virtually instantaneous.
If it is not, then it might be that MySQL is trying to think for you and take one of the secondary lookup tables and make it a primary basis of the query for whatever reason, such as much lower record count. In this case, add the keyword and try it..
SELECT STRAIGHT_JOIN ... rest of your query
This tells MySQL to do the query in the order you gave it, thus the Event table first and it's where clause on the ID. It should find that one thing, then grab all the corresponding lookup descriptions from the other tables.
Create indexes, concretely use compound indexes, for instance, start creating a compound index for event and groups:
on table events create one for (event id, group id).
then, on the group table create another one for the next relation (group id, local id).
on local do the same with service, and so on...
Im playing around with MySQL at the moment, learning stuff about database design and wondered something i couldnt find an answer to in Google.
Imagine a table named 'products' with the primary key 'id' and two additional columns named 'name' and 'primary_image_id', where 'primary_image_id' is a foreign key linking to a second table.
The second table is named 'product_images' also with the primary key 'id' and two additional columns this time called 'path' (path to the image) and 'product_id'. 'product_id' is of course a foreign key linking back to the first table.
+----+-----------+------------------+
| id | name | primary_image_id |
+----+-----------+------------------+
| 1 | product_A | 3 |
+----+-----------+------------------+
| 2 | product_B | 6 |
+----+-----------+------------------+
+----+-----------+------------------+
| id | path | product_id |
+----+-----------+------------------+
| 1 | /image_01 | 2 |
+----+-----------+------------------+
| 2 | /image_02 | 1 |
+----+-----------+------------------+
| 3 | /image_03 | 1 |
+----+-----------+------------------+
| 4 | /image_04 | 1 |
+----+-----------+------------------+
| 5 | /image_05 | 2 |
+----+-----------+------------------+
| 6 | /image_06 | 2 |
+----+-----------+------------------+
The idea is to have a table with all product images while only one image per product is the preview image (primary image). Is this type of foreign key linking even possible? And if yes, is it good databse design or should I use an other method?
Thank you in advance!
This is a valid use case and the table design looks good if your intention is to just read data using foreign key like "Get all image paths for product id 1" or "Get primary image of product id 1" or "Get paths of all primary images".
People tend to avoid the cycle of foreign key reference in tables specially if there is a cascade dependency on delete/update events. You need to answer questions like "What should happen to image 2, 3 ,4 if product 1 is deleted" or "what should happen to product 1 if image 3 is deleted".
The answers would help you come with a design that fulfills your requirement
Just use indexes without FOREIGN KEYs.
A more typical approach would be to move the primary flag to the images table. Both of these approaches have the potential for illogical data —
Your way would allow product 1 to name image A as its primary while image A could identify product 2 as its product.
My way would allow products to have 0 or 2+ primary images if the flag wasn’t well-managed.
Depending on how worried you are about either inconsistency, you could try to manage it via triggers or constraints, although MySQL is a little lacking in these areas compared to other DBMSs.
One way to absolutely prevent a problem would be to have the primary flag in the images table, but use it as an int (rank), not a Boolean with a convention that minimum rank is the “primary” — create a unique index on the combination of (product ID, rank) — and access this data via a stored proc or view that could implement the rank convention for you, e.g. select * from images a where product_id = whatever and does not exist (select 1 from images b where a.product_id = b.product_id and a.rank > b.rank).
Seems like overkill, but you need to be the judge how important potential data integrity issues are for your application.
I have 2 tables in a my MySQL Database.
Let's call 1st main, 2nd final.
TABLE `main` has the structure | TABLE `final` has the structure
|
`id` --> PRIMARY KEY (Auto Increment) | `id` --> PRIMARY KEY (Auto Increment)
| `id_main` --> ?? (Need help here)
|
id | name | info | id | id_main | name | info(changed)
--------------------- | ---------------------------------------
1 | Peter | 5,9 | 1 | 2 | Butters | 0.3,34
2 | Butters | 3,3 | 2 | 4 | Stewie | 1.2,4.4
3 | Stan | 2,96 | 3 | 1 | Peter | 5.7,0.9
4 | Stewie | 1,84 | 4 | 3 | Stan | 4.8,0.74
After analysing data in main the results get put into final.
As you can see final has an extra column (id_main) which points back to main.id
In actuality these 2 tables are 100 million+ rows each, my problem arises while performing SQL queries.
How should final especially (id & id_main) be configured so that Querying from main to final is the fastest.
Can I do away with final.id (PRIMARY KEY, Auto Increment) & keep
final.id_main (As an UNIQUE Index?)
OR
Should I keep id AS PRIMARY KEY (AI) & final.id_main AS UNIQUE Index?
I would be making calls like:
int id_From_Main= 10000;
SELECT `id_main` FROM `final` WHERE `id`='"+id_From_Main+"'
If there's a 1:1 relation between those tables, I don't see any reason why they would need two separate auto-incremented primary keys.
I would remove the final.id column and have the final.id_main as a non-auto-incremented primary key and a foreign key to the main.id column.
In general, you can also have a table without a primary key at all. It depends on if you want to be able to select specific individual rows or not.
I don't understand your query SELECT id_main FROM final WHERE id = '"+id_From_Main+"' — you're trying to select the value of ID from main by ID from main. What's the purpose, why are you trying to get the value you already have?
Anyway, you're not providing enough information to give you a qualified answer. You have to optimize you data structures according to queries you'll be doing.
Make sure you have indexes on columns which you are using in the WHERE clausule. If you're selecting by final.id_main, have an index on that column. If you're selecting by final.id_main and final.name, have a composite index on both columns, etc.
Do you really need to have the name column in both tables? It's a bad database design, unless it's some performance optimization (to avoid a join).
So, you should:
collect all queries you're currently using, set proper indexes according to them
remove any unnecessary columns (e.g. final.id, final.name)
use the EXPLAIN on your queries to get execution information (you can also use the Explain analyzer to help you interpret the results)
you can try query profiling
In mysql, you have to define id as PK because it is auto_increment. Define id_main as UNIQUE.
I'm trying to achieve something in MySQL that I have not heard is possible before (maybe it is, researching didn't help me much).
What I'm trying to do is enforce uniqueness in MySQL across two columns. What I mean by this is not setting UNIQUE(column1,column2) but the following two conditions:
If a value exists in column1, it cannot be repeated in column1 (same as setting UNIQUE(column1)).
If a value exists in either column, it cannot exist in the other column.
Hence, for the data set {column1,column2}, if {1,2}, {3,4}, {5,6} are data already present, then neither of the two columns can have any of the above data items for new data,i.e. new data item {x,y} where x=NOT{column1} AND y=NOT{column2} AND x!=y
Is this possible? Please help me out here. Thank you.
This might be an overkill, but you can store column1 and column2 in a separate table.
Let's say your table is
create table items (
id int primary key,
column1 int,
column2 int
);
with data:
id | column1 | column2
---|---------|--------
1 | 1 | 2
1 | 3 | 4
1 | 5 | 6
You can change your schema to
create table items (
id int primary key
);
create table item_columns (
item_id int,
position int,
val int,
primary key (item_id, position),
unique key (val),
foreign key (item_id) references items(id)
);
with data:
item_id | position | val
--------|----------|----
1 | 1 | 1
1 | 2 | 2
2 | 1 | 3
2 | 2 | 4
3 | 1 | 5
3 | 2 | 6
You can simulate the old schema with
select i.id, c1.val as column1, c2.val as column2
from items i
left join item_columns c1
on c1.item_id = i.id
and c1.position = 1
left join item_columns c2
on c2.item_id = i.id
and c2.position = 2
You can use it in a view if you like.
Demo: http://rextester.com/PPBT42478
To guaranty the integrity for the position column, you can make it a foreign key to a positions table, which will only contain the values 1 and 2. You could also use ENUM('1', '2'), but ENUM always allow an empty string as a value.
I have a table like the following,
| id | name | color |
------+--------+---------
| 1 | pear | green |
| 2 | apple | red |
| 3 | banana | yellow |
| 4 | grape | purple |
I'd like to reorder alphabetically using the "name" column and reset the id (autoincrement) with this new order to end up with the following
| id | name | color |
------+--------+---------
| 1 | apple | red |
| 2 | banana | yellow |
| 3 | grape | purple |
| 4 | pear | green |
QUESTION: how can I do this with MYSQL?
The cleanest way to reset the auto increment is to create another table.
MySQL provides commands such as CREATE TABLE LIKE and RENAME TABLE that are useful.
CREATE TABLE table2 LIKE table1;
INSERT INTO table2
SELECT * FROM table1 ORDER BY name;
DROP TABLE table1;
RENAME TABLE table2 TO table1;
Can I ask why you would want to do this?
If anyone modifies any of the name values or inserts new rows it will mess up your ordering scheme. Trying to store some meaning in the ordering of the PK that is already available elsewhere in the table (the name column) seems redundant and consequently a bad idea.
A much better solution is not to worry about the value of the ID column and just sort on the name column when you use the data in your app.
PS: Sorry for the non-answer type response. Normally I'd assume you had a good reason and just give an answer that directly addresses what you are trying to do, but I noticed from your other questions that you are still in the early learning stages about database design, so I wanted to help point you in the right direction instead of helping further your progress towards an ill-advised approach.
You can SELECT INTO a new table from the old table, ordering your select into as desired. Have an auto-increment ID in the new table. If needed, drop the old table and rename the new table.
Why not adding "ORDER BY name ASC" at the end of your query? My guess would be that you need the ID for some reason.
If you have a table with an autoincrement primary key (named 'id' for example), and the key is not being depended on by other tables, the way to go about this is this.
Remove the column id entirely.
alter table order by column_x, column_y;
Add the primary key column 'id' again, with autoincrement.
I did this a few times, with success and quite fast, using phpmyadmin.
Reordering a table that has a primary key as an index, is impossible. That's why u need to remove it first.
If u need to keep the 'id' column, but need to re-sort, based on other columns, u need to omit the primary key & index status of the id column & re-sort. Then you need to add a new column as primary key / index.
SELECT
RANK() Over (ORDER BY Name) As NewID
, Name
, Color
FROM Fruits
could save to a temp table then truncate then truncate the fruit table and insert, but it's probably a crappy solutions.