Triggers and multi-column Primary Keys - mysql

I have a books table and a music table. Both of these tables have a Product_ID as their primary key. I also have a products table that has a Product_ID column and a type column (0 for books, 1 for music) where the Product_Id and Type columns are the primary key for the table. In phpMyAdmin, these 2 columns are underlined and SHOW COLUMNS for the table shows Type and Product_Id marked as primary keys.
The problem I'm having is with my trigger. There are 2 triggers, with a couple of differences to handle the different tables. The music trigger for example executes this statement:
INSERT INTO products
SET
Type=1,
Product_Id=NEW.Product_ID,
Title=NEW.Album,
Price=0
The books trigger is the same, except type=0 and NEW.Album is replaced with NEW.Title. When I'm running an INSERT query now though, after some data has been inserted and deleted, I'm getting a duplicate primary key error. The problem is that the MySQL database is only reading the Product_ID column as a primary key, so when its trying to insert a duplicate there, it spits out this error even though it the value in the Type column is different. I'm stumped here.
The products table
Product_Id int(11) - PK
Title int(11)
Price double
Type int(11) - PK
NewBool tinyint(1)
The music Table
Product_ID int(11) - PK
Artist varchar(32)
Album varchar(32)
Genre varchar(32)
Year int(11)
Length double
Rating double
NumRatings int(11)
Studio varchar(64)
The books table
Product_ID int(11) - PK
ISBN bigint(20)
Title text
Author text
Year int(11)
Genre text
Pages int(11)
Publisher text
Edition int(11)
Rating double
NumRatings int(11)

It looks like you have forgot a trigger that deletes rows in Products. If you ever update the data in Books or Music you also need a trigger that makes the corresponding change in Products.
Here is your problem now
http://sqlfiddle.com/#!2/be9c93/1
Here is how I've fixed it
http://sqlfiddle.com/#!2/2ff27/2

Related

updating column when not present in other table [large data set]

I have two tables entries and users that has following columns
create table entries
(
id int(11) unsigned auto_increment primary key,
user_id int unsigned null,
status enum ('active', 'inactive', 'blocked')
)
create index user_id on entries (user_id);
------------------------
create table users
(
id int(11) unsigned auto_increment primary key,
email varchar(255) not null,
name varchar(255) not null,
phone varchar(255) not null
)
There are 5 million records in users table and around 20 million records in entries table and a lot of them have dangling user_id values, meaning user_id is pointing to a non-existant value in users table.
I'd like to update those values in entries as efficiently as possible without locking the entire table for an update for many minutes.
I've tried using batch updates by providing different status each time i.e.
UPDATE entries
SET user_id = null
WHERE user_id IS NOT NULL
AND status = 'active'
AND NOT EXISTS(SELECT id
FROM users
WHERE id = entries.user_id);
but had to kill query after a couple of minutes. any suggestions?
You might find that adding an index to the entries table speeds up the update:
CREATE INDEX idx ON entries (user_id, status);

USING STORED PROCEDURE to SUM, GROUP BY and insert to another table with

I have created 3 tables: item, shop and stock. Plus a stored procedure called inserting
which inserts to the shop table with a given item from the item table
CREATE TABLE item(
i_id int(11) auto_increment,
i_name varchar(255) not null,
primary key(i_id));
CREATE TABLE shop(
s_id int(11) auto_increment,
s_name varchar(255) not null,
s_item int(11) not null,
s_qty int(11) not null,
primary key(s_id),
foreign key(s_item) references item(i_id)
);
CREATE TABLE stock(
item int(11) not null,
total int(11) not null
);
CREATE PROCEDURE inserting (
IN shop_name varchar(225),
IN shop_item int(11),
IN shop_qty int(11)
)
BEGIN
INSERT INTO shop(s_name, s_item, s_qty)
VALUES
(shop_name, shop_item, shop_qty);
INSERT INTO STOCK(item, total)
SELECT s_item, SUM(s_qty) FROM shop GROUP BY s_item
ON DUPLICATE KEY UPDATE
item = VALUES(item),
total = VALUES(total);
The first insert works, but on the second insert when it populates the stock table it gives me extra columns, which i'm not expecting.
I have tried using REPLACE INTO and ON DUPLICATE KEY UPDATE to get single results, still the results comes as the following:
SELECT * FROM `stock`;
+------+-------+
| ITEM | TOTAL |
+------+-------+
| 1 | 5 |
| 1 | 9 |
+------+-------+
what I am trying to achieve is, group the ITEM column, and sum up the TOTAL to a single row.
what am I doing wrong here, or missing from the query?
thanks.
For the on duplicate key syntax to work as expected, you need a unique or primary key constraint on the target table, so the database can identify the "duplicate" rows. Same goes for the REPLACE syntax.
But your stock table does not have a primary key. Consider the following DDL instead:
CREATE TABLE stock(
item int(11) primary key,
total int(11) not null
);
Side note: there is no need to reassign column item in the on duplicate key clause, since it's what is used to identify the conflict in the first place. This is good enough:
INSERT INTO STOCK(item, total)
SELECT s_item, SUM(s_qty) FROM shop GROUP BY s_item
ON DUPLICATE KEY UPDATE total = VALUES(total);
If you run this one time, it should work as you expected. But subsequent runs may bring duplicate ITEM because of what #gmb said. The table must have a UNIQUE index or PRIMARY KEY. See more details here
https://dev.mysql.com/doc/refman/5.7/en/insert-on-duplicate.html

How to insert data into a table with a foreign key

I'm currently having issues with inserting values into a database table that uses a foreign key from another table to align the is together. The tables are pretty simple. One holds information about a project, and the other hold values for the project images. Here they are in detail.
The projects table
project_id int(50) PRIMARY KEY NOT NULL AUTO_INCREMENT,
project_name varchar(50) NOT NULL,
project_permitted timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT "The date that the project took place.",
project_in varchar(50) NOT NULL COMMENT 'The place where the project took place (ie the city and state).',
project_type varchar(50) NOT NULL COMMENT 'The project type (ie residentual, commercial, etc).',
project_description longtext,
project_published timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
Here is the second table called project_images
image_id int(50) PRIMARY KEY NOT NULL AUTO_INCREMENT,
project_id int(50),
image_url varchar(50) NOT NULL,
CONSTRAINT fk_projects FOREIGN KEY (project_id) REFERENCES projects(project_id)
What I am trying to do is insert values into the second table using the project_id from the projects table using a subquery. That query looks like this:
insert into project_images (project_id, project_url, project_description)
values (
(select project_id from projects where project_name = 'The Venue'),
"images/theVenue.png",
"The Venue: an appartment complex in Austin, Texas."
)
With this query I keep getting an error that says
something to the effect of "You are missing a comma or closing bracket
near project_id.
Can anyone help or point out the best way to handle this situation.
Modify your query to be like
insert into project_images (project_id, project_url, project_description)
select project_id ,
"images/theVenue.png",
"The Venue: an appartment complex in Austin, Texas."
from projects where project_name = 'The Venue';
After looking into this question a bit more, it seems that you cannot use a subquery the way I am using it to get the value of a column, However, the column can be inserted directly so long as the foreign key points to a primary key from another table that has already be inserted. The whole point to using this query was for a PHP project, so I guess I'll just do a select query in a project to get its ID then add that to the sql that queries the project_images table. This seems to be the only way to do that.

Best way with relation tables

I have a question about tables and relations tables ...
Actually, I have these 3 tables
CREATE TABLE USER (
ID int(11) NOT NULL AUTO_INCREMENT,
NAME varchar(14) DEFAULT NULL
);
CREATE TABLE COUNTRY (
ID int(11) NOT NULL AUTO_INCREMENT,
COUNTRY_NAME varchar(14) DEFAULT NULL
);
CREATE TABLE USER_COUNTRY_REL (
ID int(11) NOT NULL AUTO_INCREMENT,
ID_USER int(11) NOT NULL,
ID_COUNTRY int(11) NOT NULL,
);
Ok, so now, 1 user can have one or more country, so, several entries in the table USER_COUNTRY_REL for ONE user.
But, my table USER contains almost 130.000 entries ...
Even for 1 country by user, it's almost 10Mo for the USER_COUNTRY_REL table.
And I have several related tables in this style ...
My question is, is it the fastest, better way to do?
This would not be better to put directly in the USER table, COUNTRY field that contains the different ID (like this: "2, 6, ...")?
Thanks guys ;)
The way you have it is the most optimal as far as time constraints go. Sure, it takes up more space, but that's part of space-time tradeoff - If you want to be faster, you use more space; if you want to use less space, it will run slower (on average).
Also, think of the future. Right now, you're probably selecting the countries for each user, but just wait. Thanks to the magic of scope creep, your application will one day need to select all the users in a given country, at which point scanning each user's "COUNTRY" field to find matches will be incredibly slow, as opposed to just going backwards through the USER_COUNTRY_REL table like you could do now.
In general, for a 1-to-1 or 1-to-many correlation, you can link by foreign key. For a many-to-many correlation, you want to have a relation table in between the two. This scenario is a many-to-many relationship, as each user has multiple countries, and each country has multiple users.
Why not try like this: Create table country first
CREATE TABLE COUNTRY (
CID int(11) NOT NULL AUTO_INCREMENT,
COUNTRY_NAME varchar(14) DEFAULT NULL
);
Then the table user:
CREATE TABLE USER (
ID int(11) NOT NULL AUTO_INCREMENT,
NAME varchar(14) DEFAULT NULL,
CID Foreign Key References CID inCountry
);
just Create a Foreign Key relation between them.
If you try to put this as explicit relation , there will lot of redundancy data.
This is the better approach. You can also make that Foreign Key as index . So that the databse retrieval becomes fast during search operations.
hope this helps..
Note : Not sure about the exact syntax of the foreign key

Merging two tables with a relationship

I am building upon an existing database with a relationship construction I've seen nowhere before.
I have three tables:
legend1
legid INT(11), AUTO_INCREMENT, PRIMARY
description VARCHAR(255)
legend2
legid INT(11), AUTO_INCREMENT, PRIMARY
description VARCHAR(255)
items
id INT(11), AUTO_INCREMENT, PRIMARY
name VARCHAR(255)
legid INT(11)
legend VARCHAR(8)
Every record in items relates to data in either legend1 or legend2.
The field items.legend determines wich one it is. I want to get rid of this construction as legend1 and legend2 have an identical structure. The only thing different is the content.
I want to have this construction:
legend
legid INT(11), AUTO_INCREMENT, PRIMARY
description VARCHAR(255)
items
id INT(11), AUTO_INCREMENT, PRIMARY
name VARCHAR(255)
legid INT(11)
The problem is that the tables are full and no data may be lost. The id of both tables starts on 1 so almost every primary key will collide.
I have this query:
INSERT INTO legend1 (description) SELECT description FROM legend2;
This query doesn't work because it messes up referenced id's from legend2.
After you have executed your insert query:
INSERT INTO legend1 (description) SELECT description FROM legend2;
Perform the following query
UPDATE items SET legid = (SELECT legid FROM legend1 WHERE legend1.description = items.description) WHERE legend ='something to define that it is from the legend2 table'
Note that I haven't tried the query out but the solution is somewhat like this. If you pick out the syntax errors I've made I'm sure it will work.
What it does is the following:
After you insert your entire legend2 table into the legend1 table you update your items table to set the corresponding legendid