I am about to update records which contain data from another table. From the given example table data below, I want to update the NULL values of company_name and domain of log table:
User Table
id | email_address
1 test#gmail.com
2 test#yahoo.com
Log table
id | user_id | company_name | domain
1 1 NULL | NULL
2 1 NULL | NULL
3 2 Yahoo | yahoo.com
4 1 Google Inc | gmail.com
Company_domain table
id | company | domain
1 | Google Inc | google.com,gmail.com,gmail.com.us
2 | Yahoo | yahoo.com,yahoomail.com
The company_name should be based on the domain of user email address. From the example log table above, the company_name of id #3 is Yahoo since the user_id=2 which is test#yahoo.com. This should also reflect on log.domain
My sql query below does not match with the company.
UPDATE user_log AS log INNER JOIN user AS u ON u.id=log.user_id
SET log.domain = (
select (SUBSTR(u.email_address, INSTR(u.email_address, '#') + 1))
),
log.company_name = (
SELECT company FROM company_domain
WHERE find_in_set(
(
SELECT (SUBSTR(log.domain, INSTR(log.domain, '#') + 1))
),
domain
)
);
Does anybody know?
I got this sql query working on my local. I notice that the domain should come from the email address since the domain from log table is null.
UPDATE user_log ul
INNER JOIN user u ON u.id = ul.user_id
SET
ul.domain = (
SELECT (SUBSTR(u.email_address, INSTR(u.email_address, '#') + 1))
),
ul.company_name = (
SELECT company FROM company_domain
WHERE FIND_IN_SET(
(
SELECT (SUBSTR(u.email_address, INSTR(u.email_address, '#') + 1))
),
domain
) LIMIT 1
);
Related
I want to update records in table Users that are not present in table UserActions (see sqlfiddle demo or sql and data at gist.github)
My tables
Table Users
ID | UserName | isActive
1 | Ben Busy | 1
2 | Lui Lazy | 1 <-- never logged in
3 | Emmy Eager | 1
4 | Lana Later | 1 <-- never logged in
Table UserActions
ID | User_ID | Type | ActionDate
1 | 1 | Login | 2021-01-01 <-- Joe
2 | 3 | Login | 2021-01-02 <-- Eda
3 | 1 | Login | 2021-01-02 <-- Joe
4 | 1 | Login | 2021-01-03 <-- Joe
I want to set isActive = 0 for all Users that never logged in.
This query returns the userIDs that never logged in:
SELECT ID FROM Users u LEFT JOIN UserActions ua
ON u.ID = ua.User_ID
AND (ua.Type = "Login" OR ua.Type = NULL)
WHERE ua.ActionDate IS NULL
I was not able to use this question or this question so i thought that using a varible SET #userIDs := (....) should work as well
SET #userIDs := (
SELECT GROUP_CONCAT(ID SEPARATOR ', ') FROM Users u LEFT JOIN UserActions ua
ON u.ID = ua.User_ID
AND (ua.Type = "Login" OR ua.Type = NULL)
WHERE ua.ActionDate IS NULL);
The varible #userIDs contains all the relevant user-ids that never logged in (2,4).
But this statement
SELECT * FROM Users where ID in (#userIDs);
only returns the first result.
Questions
Why does where ID in #myVar not work?
Is there a way besides using a temporary table?
Code and Data
sql and data at gist
sqlfiddle demo
Perhaps you can just use NOT EXISTS:
UPDATE Users u
SET u.IsActive=0
WHERE NOT EXISTS
(SELECT user_id FROM UserActions ua
WHERE (ua.Type = "Login" OR ua.Type = NULL) AND u.ID=ua.user_id);
Demo fiddle
Update 3rd party edit
If you want to use a variable you can do it with find_in_set
SET #userIDs := (
SELECT GROUP_CONCAT(u.ID SEPARATOR ',') FROM Users u
LEFT JOIN UserActions ua
ON u.ID = ua.User_ID
AND (ua.Type = "Login" OR ua.Type = NULL)
WHERE ua.ActionDate IS NULL);
And then
SELECT * FROM Users WHERE FIND_IN_SET(Users.ID, #userIDs);
See this dbfiddle
Details:
MySQL tables
create table request (
id int not null primary key auto_increment,
date_requested timestamp,
user_id int
);
create table verify (
id int not null primary key auto_increment,
user_id int,
request_id int,
created timestamp
);
Values
Request Table
ID DATE REQUESTED USER ID
1 2020.. 1
2 2020.. 3
Verify Table
ID USER_ID REQUEST_ID CREATED
1 2 1 2020...
Problem:
I have this query below:
select * from request join verify on request.id = verify.request_id where verify.user_id !=2;
This returns me nothing. Why is it not showing the 2nd record from request table?
My goal is simply return all values from request tables based on this:
If record.id is in verify table then check if verify table has user_id of 2
If it does, then do not show that request record
If there are no record.id in verify table at all
Then show them
You need a left join of the tables and filter out the matching rows:
select r.*
from request r left join verify v
on r.id = v.request_id and v.user_id =2
where v.request_id is null
See the demo.
Or with NOT EXISTS:
select r.*
from request r
where not exists (
select 1 from verify v
where r.id = v.request_id and v.user_id =2
)
See the demo.
Results:
| id | date_requested | user_id |
| --- | ------------------- | ------- |
| 2 | 2020-04-08 00:00:00 | 3 |
I have a huge database that I have almost over 10k row in my user table and there are 2700 duplicate email addresses.
Basically the application did not limit the users from registering their accounts with the same email address over and over again. I have cleaned the multiple ones -more than 2 times- manually, there weren't many, but there are 2700 email addresses with duplicate value occur at least 2 times. So I want to update the duplicate email addresses and change the email address with a smaller id number to something like from "email#mail.com" to "1email#mail.com", basically adding "1" to the beginning of all duplicate email addresses. I can select and display the duplicate email addresses but could not find the way to update only one of the email addresses and leave the other on untouched.
My table structure is like id username email password.
If you do not have MySQL 8:
Here I am just prepending the id of the row to the email address:
UPDATE my_table JOIN (
SELECT email, MAX(id) AS max_id, COUNT(*) AS cnt FROM my_table
GROUP BY email
HAVING cnt > 1
) sq ON my_table.email = sq.email AND my_table.id <> sq.max_id
SET my_table.email = CONCAT( my_table.id, my_table.email)
;
See DB-Fiddle
The inner query:
SELECT email, MAX(id) AS max_id, COUNT(*) AS cnt FROM my_table
GROUP BY email
HAVING cnt > 1
looks for all emails that that are duplicated (i.e. there is more than one row with the same email address) and computes the row that has the maximum id value for each email address. For the sample data in my DB-Fiddle demo, it would return the following:
| email | max_id | cnt |
| ---------------- | ------ | --- |
| emaila#dummy.com | 3 | 3 |
| emailb#dummy.com | 5 | 2 |
The above inner query is aliased as table sq.
Now if I join my_table with the above query as follows:
SELECT my_table.* from my_table join (
SELECT email, MAX(id) AS max_id, COUNT(*) AS cnt FROM my_table
GROUP BY email
HAVING cnt > 1
) sq on my_table.email = sq.email and my_table.id <> sq.max_id
I get:
| id | email |
| --- | ---------------- |
| 1 | emaila#dummy.com |
| 2 | emaila#dummy.com |
| 4 | emailb#dummy.com |
because I am selecting from my_table all rows that have duplicate email addresses (condition my_table.email = sq.email except for the rows that have the highest value of id for each email address (condition my_table.id <> sq.max_id).
It is the ids from the above join whose email addresses are to be modified.
WITH cte AS ( SELECT id,
email,
ROW_NUMBER() OVER (PARTITION BY email ORDER BY id) rn
FROM sourcetable )
UPDATE sourcetable src, cte
SET src.email = CONCAT(rn - 1, src.email)
WHERE src.id = cte.id
AND cte.rn > 1;
fiddle
I want to update the duplicate email addresses and change the email address with a smaller id number
If so the ordering in window function must be reversed:
WITH cte AS ( SELECT id,
email,
ROW_NUMBER() OVER (PARTITION BY email ORDER BY id DESC) rn
FROM sourcetable )
UPDATE sourcetable src, cte
SET src.email = CONCAT(rn - 1, src.email)
WHERE src.id = cte.id
AND cte.rn > 1;
fiddle
I want to achieve the following. I have an application that uses chats and I want to display to the logged-in user his chats in which he is involved.
I have a table named:
- CHAT_USER where I store the ID of the conversation & user and
- CHAT where I have the Topic and other information.
My approach is to get the list of chat IDs by selecting in the first instance what I received from the parameters and then use that output to create a view with the active chats.
DELIMITER $$
CREATE DEFINER=`mysql`#`%` PROCEDURE `GetChats`(
IN userName VARCHAR(60),
IN ticket INT)
NOT DETERMINISTIC CONTAINS SQL SQL SECURITY DEFINER
BEGIN
SELECT
( select `CHAT_USER_ID`, `CHAT_ID`, `TICKET_ID`, `USER` from `CHAT_USER`
where `USER`= userName and `TICKET_ID` = ticket ) as ChatsForUser ,
(select `r`.`CHAT_ID` ,
`l`.`CHAT_TYPE` ,
`r`.`TICKET_ID`,
group_concat(`r`.`USER` separator ',') AS `USER`,
`l`.`IS_PUBLIC` ,
`r`.`IS_ACTIV` ,
`l`.`CHAT_TOPIC`
from (`CHAT_USER` `r` left join `CHAT` `l` on((`r`.CHAT_ID` = `l`.`CHAT_ID`)))
where ((`r`.`IS_ACTIV` = '1') and (`r`.`USER_TYPE` <> 'INITIATOR'))
group by `r`.`CHAT_ID` having `r`.`CHAT_ID` IN ChatsForUser.`CHAT_ID`) as OutputChats;
COMMIT;
END$$
DELIMITER ;
More info:
Table CHAT_USER
===========================================================
CHAT_USER_ID | CHAT_ID | TICKET_ID | USER | ...other fields
===========================================================
Table CHAT
============================================================================
CHAT_ID | TICKET_ID | CHAT_TYPE | CHAT_TOPIC | IS_ACTIV | IS_PUBLIC | other fields
============================================================================
I want an output that will have this:
===============================================================
CHAT_ID | TICKET_ID | USERS | IS_ACTIV | IS_PUBLIC | CHAT_TOPIC
===============================================================
CONDITIONS:
To display active CHAT_IDs on a TICKET, where the logged-in user is involved in conversation.
An example of output should return only the first 2 rows, because ME is involved in the discussions:
12 | 234 | me, user1, user 4 | 1 | 1 | Problem 1
14 | 234 | me, user56, user 9 | 1 | 1 | Problem 233
19 | 234 | user44, user 19 | 1 | 1 | Problem 12
22 | 234 | user33, user 22 | 1 | 1 | Problem 230
Is this possible? If not, can give some ideas how to achieve this? Thank you in advance!
You can try following Select statement before you start with your procedure and see if it returns the wanted data. The testing only is possible with actual data and the wanted result.
SELECT
c.CHAT_ID,c.TICKET_ID
,GROUP_CONCAT(DISTINCT Cu.USER ORDER BY Cu.USER SEPARATOR ', '),
MAX(cu.IS_ACTIV), MAX(cu.IS_PUBLIC),MAX(cu.CHAT_TOPIC)
FROM CHAT C inner join CHAT_USER cu
On c.CHAT_ID = cu.CHAT_ID and c.TICKET_ID =cu.TICKET_ID;
GROUP BY c.CHAT_ID,c.TICKET_ID
try this if it gives you the expected result
DELIMITER $$
CREATE DEFINER=`mysql`#`%` PROCEDURE `GetChats`(
IN userName VARCHAR(60),
IN ticket INT)
NOT DETERMINISTIC CONTAINS SQL SQL SECURITY DEFINER
BEGIN
SELECT
c.CHAT_ID,c.TICKET_ID
,GROUP_CONCAT(DISTINCT Cu.USER ORDER BY Cu.USER SEPARATOR ', '),
MAX(c.IS_ACTIV), MAX(c.IS_PUBLIC),MAX(c.CHAT_TOPIC)
FROM CHAT C
inner join (SELECT * FROM CHAT_USER WHERE `CHAT_ID`
in
(SELECT `CHAT_ID` FROM CHAT_USER WHERE `USER` Like userName )) cu
On c.CHAT_ID = cu.CHAT_ID and c.TICKET_ID =cu.TICKET_ID
WHERE c.TICKET_ID = ticket
GROUP BY c.CHAT_ID,c.TICKET_ID;
END$$
DELIMITER ;
This procedure will give you all the chats and users to a specific ticket id.
As i said in the comment. i don't completely understand your stored procedure.
I have table users AND orders. After every UPDATE row in orders. I want update DATA in users table namely concat(OLD.DATA + ID which was updated).
Table 'users'.
ID NAME DATA
1 John 1|2
2 Michael 3|4
3 Someone 5
Table 'orders'.
ID USER CONTENT
1 1 ---
2 1 ---
3 2 ---
4 2 ---
5 3 ---
For example:
SELECT `data` from `users` where `id` = 2; // Result: 3|4
UPDATE `orders` SET '...' WHERE `id` > 0;
**NEXT LOOP**
UPDATE `users` SET `data` = concat(OLD.data, ID.rowUpdated) WHERE `user` = 1;
UPDATE `users` SET `data` = concat(OLD.data, ID.rowUpdated) WHERE `user` = 1;
UPDATE `users` SET `data` = concat(OLD.data, ID.rowUpdated) WHERE `user` = 2;
UPDATE `users` SET `data` = concat(OLD.data, ID.rowUpdated) WHERE `user` = 2;
UPDATE `users` SET `data` = concat(OLD.data, ID.rowUpdated) WHERE `user` = 3;
Result:
SELECT data from users where id = 1; // Result: 1|2|1|2
SELECT data from users where id = 2; // Result: 3|4|3|4
SELECT data from users where id = 3; // Result: 5|5
How can I do it?
I think you are making the same mistake I made not too long ago, ie storing an array/object in a column.
I would recommend using the following tables in your scenario:
users
+-----------+-----------+
| id | user_name |
+-----------+-----------+
| 1 | John |
+-----------+-----------+
| 2 | Michael |
+-----------+-----------+
orders
+-----------+-----------+------------+
| id | user_id |date_ordered|
+-----------+-----------+------------+
| 1 | 1 | 2019-03-05 |
+-----------+-----------+------------+
| 2 | 2 | 2019-03-05 |
+-----------+-----------+------------+
Where user_id is the foreign key to users
sales
+-----------+-----------+------------+------------+------------+
| id | order_id | item_sku | qty | price |
+-----------+-----------+------------+------------+------------+
| 1 | 1 | 1001 | 1 | 2.50 |
+-----------+-----------+------------+------------+------------+
| 2 | 1 | 1002 | 2 | 3.00 |
+-----------+-----------+------------+------------+------------+
| 3 | 2 | 1001 | 2 | 2.00 |
+-----------+-----------+------------+------------+------------+
where order_id is the foreign key to orders
Now for the confusing part. You will need to use a series of JOINs to access the relevant data for each user.
SELECT
t3.id AS user_id,
t3.user_name,
t1.id AS order_id,
t1.date_ordered,
SUM((t2.price * t2.qty)) AS order_total
FROM orders t1
JOIN sales t2 ON (t2.order_id = t1.id)
LEFT JOIN users t3 ON (t1.user_id = t3.id)
WHERE user_id=1
GROUP BY order_id;
This will return:
+-----------+--------------+------------+------------+--------------+
| user_id | user_name | order_id |date_ordered| order_total |
+-----------+--------------+------------+------------+--------------+
| 1 | John | 1 | 2019-03-05 | 8.50 |
+-----------+--------------+------------+------------+--------------+
These type of JOIN statements should come up in basically any project using a relational database (that is, if you are designing your DB correctly). Typically I create a view for each of these complicated queries, which can then be accessed with a simple SELECT * FROM orders_view
For example:
CREATE
ALGORITHM = UNDEFINED
DEFINER = `root`#`localhost`
SQL SECURITY DEFINER
VIEW orders_view AS (
SELECT
t3.id AS user_id,
t3.user_name,
t1.id AS order_id,
t1.date_ordered,
SUM((t2.price * t2.qty)) AS order_total
FROM orders t1
JOIN sales t2 ON (t2.order_id = t1.id)
LEFT JOIN users t3 ON (t1.user_id = t3.id)
GROUP BY order_id
)
This can then be accessed by:
SELECT * FROM orders_view WHERE user_id=1;
Which would return the same results as the query above.
Depending on your needs, you will probably need to add a few more tables (addresses, products etc.) and several more rows to each of these tables. Very often you will find that you need to JOIN 5+ tables into a view, and sometimes you might need to JOIN the same table twice.
I hope this helps despite it not exactly answering your question!
It is probably a bad idea to update the USERS table after inserting into (or updating) the ORDERS table. Avoid storing data twice. In your case: you can always get all "order ids" for a user by querying the ORDERS table. Thus, you don't need to store them in the USERS table (again). Example (tested with MySQL 8.0, see dbfiddle):
Tables and data
create table users( id integer primary key, name varchar(30) ) ;
insert into users( id, name ) values
(1, 'John'),(2, 'Michael'),(3, 'Someone') ;
create table orders(
id integer primary key
, userid integer
, content varchar(3) references users (id)
);
insert into orders ( id, userid, content ) values
(101, 1, '---'),(102, 1, '---')
,(103, 2, '---'),(104, 2, '---'),(105, 3, '---') ;
Maybe a VIEW - similar to the one below - will do the trick. (Advantage: you don't need additional columns or tables.)
-- View
-- Inner SELECT: group order ids per user (table ORDERS).
-- Outer SELECT: fetch the user name (table USERS)
create or replace view userorders (
userid, username, userdata
)
as
select
U.id, U.name, O.orders_
from (
select
userid
, group_concat( id order by id separator '|' ) as orders_
from orders
group by userid
) O join users U on O.userid = U.id ;
Once the view is in place, you can just SELECT from it, and you will always get the current "userdata" eg
select * from userorders ;
-- result
userid username userdata
1 John 101|102
2 Michael 103|104
3 Someone 105
-- add some more orders
insert into orders ( id, userid, content ) values
(1000, 1, '***'),(4000, 1, '***'),(7000, 1, '***')
,(2000, 2, ':::'),(5000, 2, ':::'),(8000, 2, ':::')
,(3000, 3, '###'),(6000, 3, '###'),(9000, 3, '###') ;
select * from userorders ;
-- result
userid username userdata
1 John 101|102|1000|4000|7000
2 Michael 103|104|2000|5000|8000
3 Someone 105|3000|6000|9000