add foreign key with on update cascade - mysql

Let's say, I have 2 tables
user
+----+--------+
| id | status |
+----+--------+
| 1 | A |
| 2 | A |
+----+--------+
article
+----+-----+--------+
| id | uid | status |
+----+-----+--------+
| 1 | 1 | A |
| 2 | 2 | A |
| 3 | 2 | A |
| 4 | 2 | A |
| 5 | 1 | A |
| 6 | 2 | A |
| 7 | 2 | A |
| 8 | 1 | A |
| 9 | 2 | A |
| 10 | 2 | A |
+----+-----+--------+
How can I add a foreign key that if I run this query:
UPDATE user SET status='B' WHERE id=1 OR id=2;
the result will be:
user
+----+--------+
| id | status |
+----+--------+
| 1 | B |
| 2 | B |
+----+--------+
article
+----+-----+--------+
| id | uid | status |
+----+-----+--------+
| 1 | 1 | B |
| 2 | 2 | B |
| 3 | 2 | B |
| 4 | 2 | B |
| 5 | 1 | B |
| 6 | 2 | B |
| 7 | 2 | B |
| 8 | 1 | B |
| 9 | 2 | B |
| 10 | 2 | B |
+----+-----+--------+
Or in other words, if I update column user.status, MySQL will automatically update column article.status with the respective value.
How can I create this foreign key?

That job is not foreign key could finish. Use an update trigger, but for better Database compatibility, do this action on you code is preferred.
Trigger code:
CREATE TRIGGER SetArticleStatus AFTER UPDATE ON user
FOR EACH ROW
BEGIN
UPDATE `article`
SET status = NEW.status
WHERE uid = NEW.id
END

Since your FOREIGN KEY is only uid and doesn't know about status the thing you want can't be accomplished using only foreign-keys. This looks like a good use-case for a on-update-trigger.

Related

How do I query a three-level structure in two joined tables?

There are two tables,
Table A has a three-level structure that looks like
| id | name | level | up_level_id |
| :------- | :-------: | :------: | ----------:|
| 1 | lv1_name1 | 1 | null |
| 2 | lv1_name2 | 1 | null |
| 3 | lv2_name1 | 2 | 1 |
| 4 | lv2_name2 | 2 | 2 |
| 5 | lv3_name1 | 3 | 3 |
| 6 | lv3_name2 | 3 | 3 |
| 7 | lv3_name3 | 3 | 4 |
| 8 | lv3_name4 | 3 | 4 |
Table B looks like
| amount | org_id |
| -------- | -------- |
| 12,000 | 5 |
| 15,000 | 6 |
| 20,000 | 7 |
| 18,000 | 8 |
Table A and Table B can be joined on A.id=B.org_id, but they are all at the level-3 of Table A(Only level-3 has their amount)
I want to query the top-level name,level-1 name, and the summary amount that looks like
| sum_amount | top_lvl_name |
| -------- | -------- |
| 27,000 | lv1_name1 |
| 38,000 | lv1_name2 |
For Testing, I have already accomplished the query of the level-1 name from the level-3 id in TableA
The SQL is as follows
SELECT name
FROM TableA
WHERE id IN (
SELECT up_level_id
FROM Table A
WHERE id IN (
SELECT up_level_id
FROM Table A
WHERE id=5) --query the id:5's top-level name
);
But when I join these two tables as follows, it goes wrong
SELECT sum(amount) AS sum_amount, name AS top_lvl_name
FROM TableA, TableB
WHERE id = org_id
AND id IN (
SELECT up_level_id
FROM TableA
WHERE id IN (
SELECT up_level_id
FROM TableA
WHERE TableA.id IN(
SELECT org_id
FROM TABLEB
)
)
);
I get nothing as above
What can I do to make this query to be correct?
Thanks for everyone's answer and comment.
Finally, I find it very difficult to query the result as I wish. So, I've come up with a shortcut——create a new table that a three-level structure recorded horizontally, which looks like
| lv1_id | lv2_name | lv2_id | lv2_name | lv3_id | lv3_name |
| :------- | :-------: | :------: | :----------:| :------: | :----------:|
| 1 | lv1_name1 | 3 | lv2_name1 | 5 | lv3_name1 |
| 1 | lv1_name1 | 3 | lv2_name1 | 6 | lv3_name2 |
| 2 | lv1_name2 | 4 | lv2_name1 | 7 | lv3_name3 |
| 2 | lv1_name2 | 4 | lv2_name1 | 8 | lv3_name4 |
As above,I can easily connect two tables

how to update child table in mysql

I have have here an example table.incident table
+-------------+----------------------+
| incident_id | incident_description |
+-------------+----------------------+
| 1 | Accident |
| 2 | Homicide |
| 3 | Theft |
+-------------+----------------------+
incident_detail table:
+--------------------+-------------+-------------+
| incident_detail_id | person_name | incident_id |
+--------------------+-------------+-------------+
| 1 | errol | 1 |
| 2 | neo | 1 |
| 3 | aj | 1 |
| 4 | mark | 2 |
| 5 | calma | 2 |
| 6 | allan | 2 |
| 7 | dave | 3 |
| 8 | paul | 3 |
+--------------------+-------------+-------------+
I am providing a grid view like view that would allow the user to remove and add items in the incident_detail table. My question is, how can i update the incident_detail table? i am ok with adding new items, but removal. I don't know. Should i empty the entire table and insert the new items that the user added. But the problem here is that the existing items that weren't removed will be deleted and inserted again.
If you don't care about the incident_detail_id column incrementing more than it needs to, you can simply delete and re-insert the records, regardless of whether you changed the details for an incident.
It makes for easier SQL code but it does mean on each edit your IDs will go up. Assuming you're auto-incrementing.
After adding detail to Homicide you'll end up with:
+--------------------+-------------+-------------+
| incident_detail_id | person_name | incident_id |
+--------------------+-------------+-------------+
| 1 | errol | 1 |
| 2 | neo | 1 |
| 3 | aj | 1 |
| 9 | mark | 2 |
| 10 | calma | 2 |
| 11 | allan | 2 |
| 12 | new | 2 |
| 7 | dave | 3 |
| 8 | paul | 3 |
+--------------------+-------------+-------------+
If you're happy with that:
DELETE FROM incident_detail WHERE incident_id = 2;
INSERT INTO incident_detail (person_name, incident_id)
VALUES
('mark', 2)
('calma', 2)
('allan', 2)
('new', 2);

select data from one table based on the data status on other table

My first data table is couponsnmaster
+-----------+----------+------------+
|couponsnid | couponid | couponsn |
+-----------+----------+------------+
| 1 | 1 | 1000 |
| 2 | 1 | 1001 |
| 3 | 1 | 1002 |
| 4 | 1 | 1003 |
| 5 | 1 | 1004 |
| 6 | 1 | 1005 |
+-----------+----------+------------+
My second data table is distribute
+-----------+--------------+--------------+--------------+
| distid | couponid | couponsnid | status |
+-----------+--------------+--------------+--------------+
| 1 | 1 | 1 | distribute |
| 2 | 1 | 2 | distribute |
| 3 | 1 | 3 | distribute |
| 4 | 1 | 1 | returned |
+-----------+--------------+--------------+--------------+
I want to fetch all "couponsn" from "couponsnmaster" with respect to "couponid" except status is " distribute" or "sold" or "bonus" in table "distribute"....
Try this query:
SELECT c.couponsn FROM
couponsmaster c INNER JOIN distribute d
ON c.couponsid = d.couponsid
WHERE d.status NOT IN('distribute','sold','bonus')

How to delete from a table interlinking 4 tables

Hi following are my 4 tables.
client_parent_question :-
+----+------------+------------+---------+-----+------+------+
| id | is_deleted | sort_order | version | cid | pid | qid |
+----+------------+------------+---------+-----+------+------+
| 1 | | 1 | 0 | 1 | 1 | 1 |
| 2 | | 2 | 0 | 1 | 1 | 2 |
| 3 | | 3 | 0 | 1 | 1 | 3 |
| 4 | | 4 | 0 | 1 | 1 | 4 |
| 5 | | 1 | 0 | 1 | 2 | 7 |
+----+------------+------------+---------+-----+------+------+
mysql> select * from client_parent;
+----+------------+------------+---------+-----+------+
| id | is_deleted | sort_order | version | cid | pid |
+----+------------+------------+---------+-----+------+
| 1 | | 1 | 0 | 1 | 1 |
| 2 | | 2 | 0 | 1 | 2 |
+----+------------+------------+---------+-----+------+
2 rows in set (0.00 sec)
mysql> select * from client_question;
+----+------------+---------+-----+------+------+
| id | is_deleted | version | cid | pqid | qtid |
+----+------------+---------+-----+------+------+
| 1 | | 0 | 1 | 1 | 1 |
| 2 | | 0 | 1 | 2 | 4 |
| 3 | | 0 | 1 | 2 | 4 |
| 4 | | 0 | 1 | 1 | 1 |
| 5 | | 0 | 1 | 2 | 4 |
| 6 | | 0 | 1 | 3 | 4 |
| 7 | | 0 | 1 | 3 | 4 |
| 8 | | 0 | 1 | 1 | 1 |
| 9 | | 0 | 1 | 2 | 4 |
| 10 | | 0 | 1 | 3 | 4 |
| 11 | | 0 | 1 | 4 | 4 |
| 12 | | 0 | 1 | 4 | 4 |
+----+------------+---------+-----+------+------+
mysql> select * from client_question_option;
+----+------------+---------+------+------+
| id | is_deleted | version | cqid | oid |
+----+------------+---------+------+------+
| 1 | | 0 | 2 | 1 |
| 2 | | 0 | 3 | 4 |
| 3 | | 0 | 6 | 2 |
| 4 | | 0 | 7 | 3 |
| 5 | | 0 | 11 | 1 |
| 6 | | 0 | 12 | 4 |
| 7 | | 0 | 14 | 1 |
| 8 | | 0 | 15 | 4 |
+----+------------+---------+------+------+
I know only cid and pid of client_parent table
My aim is to delete all from client_question,client_parent_question and client_question_option
In the client_question_option cqid id is the id of client_question table
Following is the sqlfiddle
I did
DELETE FROM cqo,qo,cpq client_question_option cqo ,client_question cq,client_parent_question ,client_parent cp
WHERE cqo.cqid=cq.id AND cq.pqid=pq.id AND cqo.oid=qo.id AND cq.cid=1 AND cp.pid=1
But this did not work.
You just need to move the FROM keyword after the table aliases. See multiple-table syntax
DELETE cqo, qo, cpq
FROM client_question_option cqo,
client_question cq,
client_parent_question,
client_parent cp
WHERE cqo.cqid=cq.id AND cq.pqid=pq.id AND cqo.oid=qo.id
AND cq.cid=1 AND cp.pid=1;
Your DELETE query's syntax is wrong, which must be somthing like this:-
DELETE cqo,cp,pq
FROM client_question_option cqo, client_question cq, client_parent_question pq, client_parent cp
WHERE cqo.cqid=cq.id AND cq.pqid=pq.id AND cqo.oid=cp.id AND cq.cid=1 AND cp.pid=1;
Hope this will help you.
using inner join
DELETE cqo,cq,cp,pq
FROM client_question_option cqo
INNER JOIN client_question cq
INNER JOIN client_parent_question as pq
INNER JOIN client_parent cp
WHERE cqo.cqid=cq.id AND cq.pqid=pq.id AND cqo.oid=cqo.id AND cq.cid=1 AND cp.pid=1
You have an option in MYSQL Called as ONDELETE CASCADE where in when you set this on a key
example : foreign key(cid) references client_parent(cid) ON DELETE CASCADE when you delete the key based on your condition all referencing ids will be deleted , so one simple query to delete parent row is enough for it to delete all child rows present in other table data
Advantages of using ON DELETE CASCADE
you are not require to right innerjoins and delete data manually
you dont get parent key errors
you can save up your time
Thats it NJOY!!!

Updating multiple rows with first instance of record in same table

Assume following table:
+----+-----------+
| id | session |
+----+-----------+
| 1 | abcd1234 |
| 2 | abcd1234 |
| 3 | abcd1234 |
| 4 | qwert5678 |
| 5 | qwert5678 |
| 6 | abcd1234 |
| 7 | abcd1234 |
| 8 | qwert5678 |
| 9 | abcd1234 |
| 10 | qwert5678 |
| 11 | qwert5678 |
| 12 | qwert5678 |
+----+-----------+
Suppose we want to get the first id of a given session, then set every instance of that session to the id for all sessions, such that the table becomes:
+----+-----------+
| id | session |
+----+-----------+
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
| 4 | 4 |
| 5 | 4 |
| 6 | 1 |
| 7 | 1 |
| 8 | 4 |
| 9 | 1 |
| 10 | 4 |
| 11 | 4 |
| 12 | 4 |
+----+-----------+
We have a table with approximately 45M records, and are essentially changing every instance of column b to the value of min(column a) when grouped by column b.
Is there a way to do this in a single query? We have attempted several.
update example e
set session =
(select id from
(select id,min(session)
from example as first_id
group by session
) as this_id
);
...which errors out: "Subquery returns more than 1 row".
update example e
join
(select id
from
(select id,min(session)
from example as first_id
group by session
) as this_id
) as etable
set session = first_id;
...which errors out: "Unknown column 'first_id' in 'field list'". Also used 'this_id' to the same effect.
And other queries. Is this possible in a single query? Are we thinking about this incorrectly?
Query:
SQLFIDDLEExample
UPDATE example
SET session =(SELECT MIN(e2.ID)
FROM (SELECT *
FROM example) e2
WHERE e2.session = example.session)
Result:
| ID | SESSION |
----------------
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
| 4 | 4 |
| 5 | 4 |
| 6 | 1 |
| 7 | 1 |
| 8 | 4 |
| 9 | 1 |
| 10 | 4 |
| 11 | 4 |
| 12 | 4 |