How can I update two different tables in one query? - mysql

I have two tables like this:
// users
+----+--------+----------------------------+------------------+
| id | name | email | notification_num |
+----+--------+----------------------------+------------------+
| 1 | John | John134#gmail.com | 4 |
| 2 | Peter | matrix_peter#ymail.com | 2 |
| 3 | Jack | jk_43m#gmail.com | 6 |
+----+--------+----------------------------+------------------+
// notifications
+----+---------+---------+------+
| id | post_id | user_id | seen |
+----+---------+---------+------+
| 1 | 4325 | 2 | NULL |
| 2 | 8765 | 3 | NULL |
| 3 | 5454 | 3 | 1 |
| 4 | 4346 | 2 | NULL |
| 5 | 3243 | 1 | 1 |
| 6 | 3246 | 2 | 1 |
| 7 | 3764 | 3 | 1 |
+----+---------+---------+------+
Ok, All I have is just a user_id. For example :user_id = 2, And then I want this output:
// users
+----+--------+----------------------------+------------------+
| id | name | email | notification_num |
+----+--------+----------------------------+------------------+
| 1 | John | John134#gmail.com | 4 |
| 2 | Peter | matrix_peter#ymail.com | 0 |
| 3 | Jack | jk_43m#gmail.com | 6 |
+----+--------+----------------------------+------------------+
// notifications
+----+---------+---------+------+
| id | post_id | user_id | seen |
+----+---------+---------+------+
| 1 | 4325 | 2 | 1 |
| 2 | 8765 | 3 | NULL |
| 3 | 5454 | 3 | 1 |
| 4 | 4346 | 2 | 1 |
| 5 | 3243 | 1 | 1 |
| 6 | 3246 | 2 | 1 |
| 7 | 3764 | 3 | 1 |
+----+---------+---------+------+
As you see, I want to update users.cotification_num and set it to 0 (for such an user), and I also want to update notifications.seen and set all rows to 1 (for such an user). How can I do that?
I can do that by two different queries like this:
UPDATE users SET notification_num = 0 WHERE id = :user_id
UPDATE notifications SET seen = 1 WHERE seen is NULL and user_id = :user_id
But I want to know how can I do that by one query?

You can use a multiple-table UPDATE:
UPDATE users u LEFT JOIN notifications n ON n.user_id = u.id
SET u.notification_num = 0,
n.seen = 1
WHERE u.id = 2

You can join the tables together to UPDATE both in a single query:
UPDATE users, notifications
SET users.notification_num = 0
, notification.seen = 1
WHERE users.id = notifications.user_id AND
users.id = 2 AND
notifications.seen is NULL
Read here for more information. Another option would be to simply do 2 separate updates within a transaction:
START TRANSACTION;
UPDATE users SET notification_num = 0 WHERE id = 2;
UPDATE notifications SET seen = 1 WHERE user_id = 2 AND notifications.seen is NULL;
COMMIT;

Note that my schema is very slightly different from yours - but it makes very little difference...
DROP TABLE IF EXISTS users;
CREATE TABLE users
(id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
,name VARCHAR(12) NOT NULL
,email VARCHAR(20) NOT NULL
,notification_num INT NOT NULL
);
INSERT INTO users VALUES
(1,'John','John134#gmail.com',4),
(2,'Peter','matrix_peter#ymail.com',2),
(3,'Jack','jk_43m#gmail.com',6);
DROP TABLE IF EXISTS notifications;
CREATE TABLE notifications
(post_id INT NOT NULL
,user_id INT NOT NULL
,seen TINYINT DEFAULT 0
,PRIMARY KEY(post_id,user_id)
);
INSERT INTO notifications VALUES
(4325,2,0),
(8765,3,0),
(5454,3,1),
(4346,2,0),
(3243,1,1),
(3246,2,1),
(3764,3,1);
SELECT * FROM notifications;
+---------+---------+------+
| post_id | user_id | seen |
+---------+---------+------+
| 3243 | 1 | 1 |
| 3246 | 2 | 1 |
| 3764 | 3 | 1 |
| 4325 | 2 | 0 |
| 4346 | 2 | 0 |
| 5454 | 3 | 1 |
| 8765 | 3 | 0 |
+---------+---------+------+
SELECT * FROM users;
+----+-------+----------------------+------------------+
| id | name | email | notification_num |
+----+-------+----------------------+------------------+
| 1 | John | John134#gmail.com | 4 |
| 2 | Peter | matrix_peter#ymail.c | 2 |
| 3 | Jack | jk_43m#gmail.com | 6 |
+----+-------+----------------------+------------------+
UPDATE users u
JOIN notifications n
ON n.user_id = u.id
SET u.notification_num = 0, n.seen = 1
WHERE u.id = 2;
SELECT * FROM users;
+----+-------+----------------------+------------------+
| id | name | email | notification_num |
+----+-------+----------------------+------------------+
| 1 | John | John134#gmail.com | 4 |
| 2 | Peter | matrix_peter#ymail.c | 0 |
| 3 | Jack | jk_43m#gmail.com | 6 |
+----+-------+----------------------+------------------+
SELECT * FROM notifications;
+---------+---------+------+
| post_id | user_id | seen |
+---------+---------+------+
| 3243 | 1 | 1 |
| 3246 | 2 | 1 |
| 3764 | 3 | 1 |
| 4325 | 2 | 1 |
| 4346 | 2 | 1 |
| 5454 | 3 | 1 |
| 8765 | 3 | 0 |
+---------+---------+------+

Related

MySQL select users who was in range of dates

Hello :) I need to create sql which will calculate advances for user but only for that one who was in work. Tables looks like:
users:
| id | firstName | lastName |
| -- | --------- | -------- |
| 1 | John 1 | Test 1 |
| 2 | John 2 | Test 2 |
| 3 | John 3 | Test 3 |
| 4 | John 4 | Test 4 |
users_advances:
| id | user_id | amount | d_add | status_id |
| -- | ------- | ------ | ---------- | --------- |
| 1 | 1 | 100 | 2022-07-09 | 1 |
| 2 | 1 | 50 | 2022-07-10 | 2 |
| 3 | 2 | 100 | 2022-07-03 | 1 |
| 4 | 2 | 50 | 2022-07-05 | 2 |
| 5 | 2 | 100 | 2022-03-09 | 1 |
| 6 | 1 | 50 | 2022-07-02 | 2 |
users_arrivals
| id | user_id | start_date | end_date |
| -- | ------- | ---------- | ---------- |
| 1 | 1 | 2022-09-01 | 2022-09-30 |
| 2 | 2 | 2022-09-22 | 2022-09-25 |
| 3 | 3 | 2022-09-19 | 2022-09-25 |
I created SQL
SELECT u.id AS user_id, CONCAT(u.firstName, SPACE(1), u.lastName) AS fullName, IFNULL(SUM(uz.amount), 0) AS suma
FROM users u
LEFT JOIN users_advances uz ON uz.user_id = u.id AND (uz.d_add BETWEEN '2022-09-19' AND '2022-09-25') AND ((uz.status_id = 1) OR (uz.status_id = 2))
LEFT JOIN users_arrivals po ON po.user_id = u.id
WHERE po.start_date <= '2022-09-19' AND po.end_date >= '2022-09-24'
GROUP BY u.id
but it doesnt return me user 2 who had start_date at 2022-09-22.

Select Same Column From 2 Tables into 1 Column in View

I have two column with the same name in different tables.
I want to join them into one column in a view.
Here my first table stocks:
+----------+------------+------------+---------+----------------+--------+
| stock_id | stock_cost | stock_left | item_id | purchasedtl_id | trx_id |
+----------+------------+------------+---------+----------------+--------+
| 1 | 1000 | 0 | 1 | 1 | 1 |
| 2 | 1000 | 5 | 1 | 2 | 2 |
| 3 | 1000 | 1 | 1 | 3 | 4 |
+----------+------------+------------+---------+----------------+--------+
Second table stocks_out
+-------------+----------------+--------------+---------+----------+------------+--------+
| stockout_id | stockout_price | stockout_qty | item_id | stock_id | saledtl_id | trx_id |
+-------------+----------------+--------------+---------+----------+------------+--------+
| 1 | 2000 | 1 | 1 | 1 | 1 | 3 |
+-------------+----------------+--------------+---------+----------+------------+--------+
And I want to join them to be like this trx_id, trx_no, trx_closetime, trx_type stock_id, stock_cost, stockout_id, stock_out_cost, stockout_price, item_id
item_id is the field I want to join in one column.
The current Query is :
select `transactions`.`trx_id` AS `trx_id`,`transactions`.`trx_no` AS `trx_no`,`transactions`.`trx_closetime` AS `trx_closetime`,`transactions`.`trx_type` AS `trx_type`,`stocks`.`stock_id` AS `stock_id`,`stocks`.`stock_cost` AS `stock_cost`,`stock_out`.`stockout_id` AS `stockout_id`,`stock_out`.`stockout_price` AS `stockout_price` from ((`transactions` left join `stocks` on(`stocks`.`trx_id` = `transactions`.`trx_id`)) left join `stock_out` on(`stock_out`.`trx_id` = `transactions`.`trx_id`)) order by `transactions`.`trx_closetime`;
And the current result:
+--------+---------------------+---------------------+----------+----------+------------+-------------+----------------+
| trx_id | trx_no | trx_closetime | trx_type | stock_id | stock_cost | stockout_id | stockout_price |
+--------+---------------------+---------------------+----------+----------+------------+-------------+----------------+
| 1 | 02002-02-170415-001 | 2017-04-15 19:40:03 | 2 | 1 | 1000 | NULL | NULL |
| 2 | 02002-02-170415-002 | 2017-04-15 19:40:13 | 2 | 2 | 1000 | NULL | NULL |
| 3 | 02002-01-170415-001 | 2017-04-15 19:40:57 | 1 | NULL | NULL | 1 | 2000 |
| 4 | 02002-02-170415-003 | 2017-04-15 19:41:14 | 2 | 3 | 1000 | NULL | NULL |
+--------+---------------------+---------------------+----------+----------+------------+-------------+----------------+
Found it guys.
I just need to add the following query as the column
COALESCE(`stocks`.`item_id`, `stocks_out`.`item_id`) AS `item_id`
So the query will be like
select `transactions`.`trx_id` AS `trx_id`,`transactions`.`trx_no` AS `trx_no`,`transactions`.`trx_closetime` AS `trx_closetime`,`transactions`.`trx_type` AS `trx_type`,`stocks`.`stock_id` AS `stock_id`,`stocks`.`stock_cost` AS `stock_cost`,`stock_out`.`stockout_id` AS `stockout_id`,`stock_out`.`stockout_price` AS `stockout_price`, COALESCE(`stocks`.`item_id`, `stocks_out`.`item_id`) AS `item_id from ((`transactions` left join `stocks` on(`stocks`.`trx_id` = `transactions`.`trx_id`)) left join `stock_out` on(`stock_out`.`trx_id` = `transactions`.`trx_id`)) order by `transactions`.`trx_closetime`;
And the result:
+--------+---------------------+---------------------+----------+----------+------------+-------------+----------------+---------+
| trx_id | trx_no | trx_closetime | trx_type | stock_id | stock_cost | stockout_id | stockout_price | item_id |
+--------+---------------------+---------------------+----------+----------+------------+-------------+----------------+---------+
| 1 | 02002-02-170415-001 | 2017-04-15 19:40:03 | 2 | 1 | 1000 | NULL | NULL | 1 |
| 2 | 02002-02-170415-002 | 2017-04-15 19:40:13 | 2 | 2 | 1000 | NULL | NULL | 1 |
| 3 | 02002-01-170415-001 | 2017-04-15 19:40:57 | 1 | NULL | NULL | 1 | 2000 | 1 |
| 4 | 02002-02-170415-003 | 2017-04-15 19:41:14 | 2 | 3 | 1000 | NULL | NULL | 1 |
+--------+---------------------+---------------------+----------+----------+------------+-------------+----------------+---------+

Mysql Best way to query notifications like on stackoverflow

I have 4 tables:
comments
+----+-----------+--------------+-------+
| id | content | user_id | article_id |
+----+-----------+--------------+-------+
| 1 | Comment 1 | 2 | 5 |
| 2 | Comment 2 | 5 | 3 |
| 3 | Comment 3 | 1 | 6 |
| 4 | Comment 4 | 6 | 8 |
| 5 | Comment 5 | 1 | 6 |
| ...| ... | ... | ... |
+----------------+---------+------------+
votes
+----+----------+--------------+---+
| id | type | user_id | article_id |
+----+----------+--------------+---+
| 1 | 1 | 2 | 5 |
| 2 | 1 | 3 | 3 |
| 3 | 0 | 1 | 6 |
| 4 | 1 | 7 | 4 |
| 5 | 0 | 9 | 4 |
| 6 | 0 | 1 | 6 |
| ...| ... | ... | ... |
+------------+----------+----------+
notifications (object_id is the id of the vote|comment)
+----+----------+--------------+-------------+-------------+--------------+
| id | object_url| object_id |activitytype_id| sender_id | recipient_id |
+----+----------+------------+---------------+-------------+--------------+
| 1 | /../../.. | 1 | 2 | 2 | 6 |
| 2 | /../../.. | 2 | 2 | 3 | 2 |
| 3 | /../../.. | 1 | 1 | 2 | 7 |
| 3 | /../../.. | 2 | 1 | 5 | 2 |
| 3 | /../../.. | 3 | 1 | 1 | 3 |
| 3 | /../../.. | 3 | 3 | 1 | 2 |
| 3 | /../../.. | 4 | 2 | 7 | 8 |
| 3 | /../../.. | 5 | 3 | 9 | 1 |
| 3 | /../../.. | 6 | 3 | 1 | 5 |
| ...| ... | ... | ... | | |
+----+-----------+-----------+---------------+-------------+--------------+
activitytypes
+----+------------+
| id | label |
+----+------------+
| 1 | comment |
| 2 | vote up |
| 3 | vote down |
| ...| ... |
+-----------------+
I would like to get notifications like on stackoverflow.
I want to query the last notification (with comment content if the activity type is a comment or null if not) for every activitytype and object_url combinaison for a specific user.
For example I have 3 artiles A,B and C which all have 3 comments, 4 voteup and 2 votedown. How to get the last comment, voteup and votedown for every article ?
I have tried this query:
SELECT n.id, n.object_url , n.object_id, n.activitytype_id, IF(n.activitytypeId = 1,
(SELECT content FROM comments WHERE id=n.object_id), null) AS activitycontent
FROM notifications n WHERE n.recipient_id =1
GROUP BY n.activitytype_id,n.object_url
ORDER BY n.id DESC
But it doesn't work. Can anyone help ?
EDIT:
This following query in farhadamjady's answer gives me the first comment:
SELECT
n.id,
n.object_url,
n.object_id,
n.activitytype_id,
cm.content AS activitycontent
FROM
notifications n
LEFT OUTER JOIN `COMMENT` AS cm ON cm.id = n.object_id and n.activitytypeId = 1
WHERE
n.recipient_id = 1
GROUP BY
n.activitytype_id,
n.object_url
HAVING MAX(cm.id)
ORDER BY
n.id DESC
How can I change it to get the last ?
you should use left outer join like this :
SELECT
n.id,
n.object_url,
n.object_id,
n.activitytype_id,
cm.content AS activitycontent
FROM
notifications n
LEFT OUTER JOIN `COMMENT` AS cm ON cm.id = n.object_id and n.activitytypeId = 1
WHERE
n.recipient_id = 1
GROUP BY
n.activitytype_id,
n.object_url
HAVING MAX(cm.id)
ORDER BY
n.id DESC

How can I select posts that have more than specific votes?

My tables structure:
// posts
+----+----------------------------------------+-----------+
| Id | body | user_id |
+----+----------------------------------------+-----------+
| 1 | content1 | 2 |
| 2 | content2 | 9 |
| 3 | content3 | 6 |
| 4 | content4 | 4 |
| 5 | content5 | 2 |
| 6 | content6 | 8 |
| 7 | content7 | 4 |
| 8 | content8 | 2 |
+----+----------------------------------------+-----------+
// votes
+----+---------+-------+
| id | post_id | value |
+----+---------+-------+
| 1 | 2 | 1 |
| 2 | 3 | -1 |
| 3 | 2 | 1 |
| 4 | 8 | -1 |
| 5 | 1 | 1 |
| 6 | 8 | 1 |
| 7 | 2 | -1 |
| 8 | 8 | -1 |
| 9 | 2 | 1 |
+----+---------+-------+
I need to select posts that have more than 1 vote total score. So this is expected output:
+----+----------------------------------------+-----------+-------------+
| Id | body | user_id | total_votes |
+----+----------------------------------------+-----------+-------------+
| 2 | content2 | 9 | 2 |
+----+----------------------------------------+-----------+-------------+
How can I do that?
create table qanda
( id int not null,
body varchar(100) not null,
user_id int not null
);
insert qanda values
(1,'a',2),
(2,'a',9),
(3,'a',6),
(4,'a',4),
(5,'a',2),
(6,'a',8),
(7,'a',2),
(8,'a',2);
create table votes
( id int not null,
post_id int not null,
value int not null
);
insert votes values
(1,2,1),
(2,3,-1),
(3,2,1),
(4,8,-1),
(5,1,1),
(6,8,1),
(7,2,-1),
(8,8,-1),
(9,2,1);
Query
select q.id,q.body,q.user_id,sum(v.value) as votes
from qanda q
join votes v
on v.post_id=q.id
group by q.id,q.body,q.user_id
having votes>1;
+----+------+---------+-------+
| id | body | user_id | votes |
+----+------+---------+-------+
| 2 | a | 9 | 2 |
+----+------+---------+-------+

redisplaying data from a table in mySQL

I am trying to redisplay the data from a table
currently the data in the table goes as
__________________________________________
| UserMeta |
|__________________________________________|
| id | userID | orgID | keyType | keyData |
|____|________|_______|__________|_________|
| 1 | 1 | 1 | UserName | Scarlet |
| 2 | 1 | 1 | Age | 23 |
| 3 | 2 | 2 | UserName | Diana |
| 4 | 2 | 2 | Age | 27 |
| 5 | 3 | 1 | UserName | Kevin |
| 6 | 3 | 1 | Age | 18 |
|____|________|_______|__________|_________|
I'd like to be able to format it to the following
_________________________________
| vUserMeta |
|_________________________________|
| UserID | orgID | UserName | Age |
|________|_______|__________|_____|
| 1 | 1 | Scarlet | 23 |
| 2 | 2 | Diana | 27 |
| 3 | 1 | Kevin | 18 |
|________|_______|__________|_____|
One approach would be to join UserMeta on itself as follows:
SELECT u.userID AS UserID,
u.orgID AS orgID,
u.keyData AS UserName,
v.keyData AS Age
FROM UserMeta u
JOIN UserMeta v
ON u.userID = v.userID
AND u.keyType = 'UserName'
AND v.keyType = 'Age'