foreign Key and innodb engine insert - mysql

I couldn't insert salary values into this table
error massage Field 'Tid' doesn't have a default value
I have tried so many times by adding constraint or default fk but it doesn't work out
My code is this
CREATE TABLE Actor (
aid INTEGER NOT NULL AUTO_INCREMENT,
NAME VARCHAR(20),
PRIMARY KEY (aid)
) ENGINE=INNODB;
CREATE TABLE Movie (
mid INTEGER NOT NULL AUTO_INCREMENT,
NAME VARCHAR(30),
YEAR INTEGER,
PRIMARY KEY (mid)
) ENGINE=INNODB;
CREATE TABLE acts_in (
aid INTEGER NOT NULL AUTO_INCREMENT,
mid INTEGER NOT NULL,
PRIMARY KEY (aid, mid),
FOREIGN KEY (aid) REFERENCES Actor (aid),
FOREIGN KEY (mid) REFERENCES Movie (mid)
) ENGINE=INNODB;
SHOW TABLES;
INSERT INTO Actor(NAME)
VALUES ('Brad Pitt'), ('Edward Norton'), ('Julia Roberts');
INSERT INTO Movie (NAME, YEAR)
VALUES ('Fight Club', 1999), ('Ocean\s Eleven', 2001), ('Fury', 2014), ('The Incredible Hulk', 2008), ('Closer', 2004);
INSERT INTO acts_in (aid, mid)
VALUES (1, 1), (1, 2), (1, 3),(2, 1), (2, 4), (3,2), (3, 5);
ALTER TABLE Acts_in
ADD COLUMN Salary INTEGER NOT NULL;
INSERT INTO acts_in (Salary)
VALUES (2000), (4000), (5000), (50), (7000), (750000), (888888);

Your acts_in table is a child table. The parent tables of acts_in table are:
actor
movie
So you cannot add or update a child table row if the corresponding foreign key values don't exist in the parent tables.
In this case you cannot execute this query:
INSERT INTO acts_in (Salary)
VALUES (2000), (4000), (5000), (50), (7000), (750000), (888888);
Look you are trying to add or update a child table but you didn't set any value of aid and mid.
Try instead the following queries to update the salaries:
UPDATE acts_in SET Salary = 2000 WHERE aid = 1 AND mid = 1;
UPDATE acts_in SET Salary = 4000 WHERE aid = 1 AND mid = 2;
UPDATE acts_in SET Salary = 5000 WHERE aid = 1 AND mid = 3;
UPDATE acts_in SET Salary = 50 WHERE aid = 2 AND mid = 1;
UPDATE acts_in SET Salary = 7000 WHERE aid = 2 AND mid = 4;
UPDATE acts_in SET Salary = 750000 WHERE aid = 3 AND mid = 2;
UPDATE acts_in SET Salary = 888888 WHERE aid = 3 AND mid = 5;
OR
this single query:
UPDATE
acts_in
SET Salary = CASE WHEN aid = 1 AND mid = 1 THEN 2000
WHEN aid = 1 AND mid = 2 THEN 4000
WHEN aid = 1 AND mid = 3 THEN 5000
WHEN aid = 2 AND mid = 1 THEN 50
WHEN aid = 2 AND mid = 4 THEN 7000
WHEN aid = 3 AND mid = 2 THEN 750000
WHEN aid = 3 AND mid = 5 THEN 888888
END
WHERE (aid,mid) IN ((1,1),(1,2),(1,3),(2,1),(2,4),(3,2),(3,5));
Note: In this case you are trying to update a child table where the corresponding foreign keys (in this case aid and mid) are containing valid values.
Check this SQL FIDDLE.

Related

SQL max number of records

So I have the following table:
CREATE TABLE Hospital_MedicalRecord(
recNo CHAR(5),
patient CHAR(9),
doctor CHAR(9),
enteredOn DATE NOT NULL,
diagnosis VARCHAR(50) NOT NULL,
treatment VARCHAR(50),
PRIMARY KEY (recNo, patient),
FOREIGN KEY (patient) REFERENCES Hospital_Patient(NINumber),
FOREIGN KEY (doctor) REFERENCES Hospital_Doctor(NINumber)
);
I want to make it so there are never more that 65,535 medical records for a single patient. Am I supposed to make a new statement or should I implement it in the table above. I can post the patient table if needed.
You would typically use a before insert trigger for this, that raises an error if the number of records for a patient reached the limit and a new insert is attempted:
delimiter //
create trigger Trg_Hospital_MedicalRecord
before insert on Hospital_MedicalRecord
for each row
begin
if (
select count(*) from Hospital_MedicalRecord where patient = new.patient
) = 65535 then
set msg = concat('Patient ', new.patient, ' cannot have more than 65535 records');
signal state '45000' set message_text = msg;
end if;
end
//
delimiter ;
I would assume that you should not allow a patient to be updated on an existing record. But if this may happen, then you also need a before update trigger (with the very same code).
Consider the following...
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table(id SERIAL PRIMARY KEY, user_id INT NOT NULL);
INSERT INTO my_table (user_id)
SELECT 1
FROM (SELECT 1) x
LEFT
JOIN (SELECT user_id FROM my_table GROUP BY user_id HAVING COUNT(*) >=3) y
ON y.user_id = 1
WHERE y.user_id IS NULL
LIMIT 1;
This limits INSERTS to 3 per user_id.

SQL Syntax for checking duplicates prior to update (not DUPLICATE KEY UPDATE)

I have a syntactical question with attempting an UPDATE if 2 non key fields are matched. -- INSERT if not matched.
Let me start by saying I have a working query that involves a SELECT with an ON DUPLICATE KEY UPDATE. Now I am just curious, can it be done differently?
Out of sheer curiosity, I am trying this in a manner that will not require the primary key. This really is just an experiment to see if it can be done.
What I want is like ON DUPLICATE KEY UPDATE -- However:
let's pretend I don't know the key , and
let's pretend I can't get the key with a SELECT
Here is the data structure I have:
+--------------------------------------------------------------------------+
| id | contractor_id | email_type_id | email_address |
+--------------------------------------------------------------------------+
Table creation:
CREATE TABLE `email_list` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`contractor_id` int(11) NOT NULL,
`email_type_id` int(11) NOT NULL,
`email_address` varchar(45) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `id_UNIQUE` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
Now what I am trying to do without a select and without ON DUPLICATE KEY UPDATE is -- If contractor_id and email_type_id are matched -- UPDATE the email_address -- else INSERT.
If have tried this -- (I know I am breaking my own rule of no SELECT):
IF EXISTS (SELECT * FROM email_list WHERE contractor_id = 1166)
UPDATE email_list SET (email_address='herky_jerky#snailmail.com')
WHERE contractor_id = 1166 AND email_type_id = 4
ELSE
INSERT INTO email_list VALUES (
contractor_id = 1166,
email_type_id = 4,
email_address = 'herky_jerky#snailmail.com');
I understand why this doesn't work .. I just don't know what the fix for it is -- It feels a little clunky too using an IF - ELSE statement. Also I don't want to use a SELECT -- So then I thought about using just an IF like:
UPDATE email_list SET email_address = 'herky#jerky.com'
WHERE contractor_id = 1166 AND email_type_id = 4
IF ##ROWCOUNT=0
INSERT INTO email_list VALUES (
contractor_id = 1166,
email_type_id = 4,
email_address = 'herky#jerky.com');
But I don't understand why that one doesn't work. This is just an exercise to see how creative one can be with this type of query. I think both of my ideas are doable -- Can anyone find a fix for either query to make it work?
I'd also love to see other, more creative, ways of attempting what I am asking as well!
I'd implement this using an UPDATE followed by a test of ROW_COUNT() and if no rows were updated, then INSERT.
drop table if exists t;
create table t (id int, x int, y int, str varchar(255));
insert into t (id, x, y, str) values (1, 2, 3, 'foo');
select * from t;
update t set str = 'bar'
where x = 2 and y = 3;
insert into t (id, x, y, str)
select 1, 2, 3, 'inserted'
from dual
where row_count() = 0;
select * from t;
update t set str = 'baz'
where x = 20 and y = 30;
insert into t (id, x, y, str)
select 10, 20, 30, 'baz'
from dual
where row_count() = 0;
select * from t;
drop table t;
You can see it in action here: https://rextester.com/FRFTE79537
The idea here is you do the UPDATE first, followed by an INSERT ... SELECT where the SELECT only returns a row if ROW_COUNT() = 0 is true, and that's only true if the UPDATE didn't match any rows.

insert, if exist update using trigger

I want to implement a Viewed system for my website. Here is the structure of my tables:
// table1
id | user_or_ip | post_id | date_time // inserting new row for each viewed
// table2
id | post_id | total_viewed // getting the number of total viewed for each post
Now I need to a trigger for insert/update table2 after insert in table1.
I
think I have to use on duplicate key.
You can do this fairly easily.
With the following 2 example table:-
CREATE TABLE table1
(
id INT NOT NULL AUTO_INCREMENT,
user_or_ip VARCHAR(255),
post_id INT,
date_time DATETIME,
PRIMARY KEY (id)
);
CREATE TABLE table2
(
id INT NOT NULL AUTO_INCREMENT,
post_id INT,
total_viewed INT,
PRIMARY KEY (id),
UNIQUE KEY post_id (post_id)
);
you can use the following trigger on table 1 to calculate the count and insert it to table 2:-
CREATE TRIGGER `trig_1` AFTER INSERT ON `table1`
FOR EACH ROW BEGIN
INSERT INTO table2(post_id, total_viewed)
SELECT post_id, COUNT(*)
FROM table1
WHERE post_id = NEW.post_id
GROUP BY post_id
ON DUPLICATE KEY UPDATE total_viewed = VALUES(total_viewed);
END
Note that if you are certain that there will never be an error you could just insert a count of 1 and set it to total_count + 1 in the ON DUPLICATE KEY clause. But if anything fails that prevents the trigger the counts will be forever wrong for that post_id:-
CREATE TRIGGER `trig_1` AFTER INSERT ON `table1`
FOR EACH ROW BEGIN
INSERT INTO table2(post_id, total_viewed)
VALUES(NEW.post_id, 1)
ON DUPLICATE KEY UPDATE total_viewed = total_viewed + 1;
END
Note also that the sub query to get the count will be more efficient with an index on post_id in table1

Update multiple rows with unique constraint

I have two tables in my database, foo and bar. Multiple foos have one bar, and contain a second column that controls the ordering of foos with the same bar. The values used for ordering should be unique, so the table has a unique constraint:
CREATE TABLE bar (
id int NOT NULL AUTO_INCREMENT,
PRIMARY KEY (id)
);
CREATE TABLE foo (
id INT NOT NULL AUTO_INCREMENT,
bar INT,
`order` INT,
PRIMARY KEY (id),
FOREIGN KEY (bar) REFERENCES bar (id),
UNIQUE KEY (bar, order)
);
What is the most efficient way to update the ordering of the foos for one bar? For example, if I have this data:
id | bar | order
1 | 1 | 1
2 | 1 | 2
3 | 1 | 3
4 | 1 | 4
And want to reorder them (1, 2, 4, 3), I now have the following queries:
UPDATE foo SET `order` = NULL WHERE id IN (3, 4);
UPDATE foo SET `order` = 3 WHERE id = 4;
UPDATE foo SET `order` = 4 WHERE id = 3;
The first query is necessary to prevent an integrity error for the other updates. Can this be improved?
The only I can think is you should have your update values in a separated table/query to make it more generic and can work with multiple ID
newQuery
ID newOrder
3 4
4 3
You update your order to null before update because the integrity restriction.
UPDATE foo SET `order` = NULL WHERE id IN (SELECT ID FROM newQuery);
Then update with a JOIN
UPDATE foo AS f
INNER JOIN newQuery AS n ON f.id = n.id
SET f.order = n.newOrder

Getting a Sum and Count from Different Tables

I'm guessing this is too local but I can't figure out a way to make it more general (which might be why I'm not able to find my answer on Google).
We have an application that tracks contacts for our business. These contacts (Contact table) are either contacted through the phone (Contact_Phone table) or through email (Contact_Email). If the user is contacted through the phone an agent keeps track of the total number of seconds (Contact_Phone.totalSeconds). Through a piece of business logic that I have no control over email contacts are treated as one second. A user might be contact through just email, just phone, or both.
I'm trying to generate a report on how long we've spent contacting each user but I can't get the results I expect.
Tables:
CREATE TABLE IF NOT EXISTS `Contact` (
`id` INT NOT NULL AUTO_INCREMENT ,
`name` VARCHAR(45) NULL ,
PRIMARY KEY (`id`) )
ENGINE = InnoDB;
CREATE TABLE IF NOT EXISTS `Contact_Email` (
`id` INT NOT NULL AUTO_INCREMENT ,
`ContactId` INT NULL ,
PRIMARY KEY (`id`) ,
INDEX `contact_email_contact_idx` (`ContactId` ASC) ,
CONSTRAINT `contact_email_contact`
FOREIGN KEY (`ContactId` )
REFERENCES `Contact` (`id` )
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
CREATE TABLE IF NOT EXISTS `Contact_Phone` (
`id` INT NOT NULL AUTO_INCREMENT ,
`totalSeconds` INT NULL ,
`ContactId` INT NULL ,
PRIMARY KEY (`id`) ,
INDEX `Contact_Phone_contact_idx` (`ContactId` ASC) ,
CONSTRAINT `Contact_Phone_contact`
FOREIGN KEY (`ContactId` )
REFERENCES `Contact` (`id` )
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
Test Data:
insert into Contact (id, name) values (1, 'Scott');
insert into Contact (id, name) values (2, 'Joe');
insert into Contact_Phone (totalSeconds, ContactId) values (10, 2);
insert into Contact_Phone (totalSeconds, ContactId) values (100, 2);
insert into Contact_Email (ContactId) values (1);
insert into Contact_Email (ContactId) values (1);
insert into Contact_Email (ContactId) values (2);
Query:
select
name,
(select sum(totalSeconds) from Contact_Phone where Contact_Phone.ContactId = Contact.id)
+
(select count(*) from Contact_Email where Contact_Email.ContactId = Contact.id)
from Contact;
Expected Results:
Joe 111
Scott 2
Actual Results:
Joe 111
Scott null
Thanks
How about using summaries and LEFT JOIN operations, like so?
SELECT Contact.name,
COALESCE(p.seconds,0) + COALESCE(e.seconds,0) seconds
FROM Contact.Name
LEFT JOIN (
SELECT ContactID AS id,
SUM(totalSeconds) AS seconds
FROM ContactPhone
GROUP BY ContactID
) p ON Contact.id = p.id
LEFT JOIN (
SELECT ContactID AS id,
COUNT(*) AS seconds
FROM ContactEmail
GROUP BY ContactID
) e ON Contact.id = e.id
The LEFT JOIN operations will preserve your result rows where one or the other of your "seconds" computations is NULL. And, the COALESCE operations will prevent your query from attempting arithmetic on NULL values, which yields NULL.