MySQL - updating table based on chronological order - mysql

I have two tables as below:
logs
id | user | log_id
---------------------
1 | user1 | abc
2 | user2 | def
3 | user1 | xyz
...
users
id | user | code
---------------
1 | user1 | 1234
2 | user2 | 9876
3 | user1 | 5678
...
I want to add log_id to users and update it with log_id's from Table1, to make Table2 as below:
id | user | code | log_id
---------------------------
1 | user1 | 1234 | abc
2 | user2 | 9876 | def
3 | user1 | 5678 | xyz
...
The only way to match rows in logs and users is using the user field, and the chronological order they appear in the tables. id, as you may have guessed, is the primary key in both tables.
Much appreciated if someone could help me with the query for this. Thanks.

If the id fields are always matched then the reply by Ronak Shah would be my choice.
If the ids do not match then possibly something like this:-
Firstly:-
ALTER TABLE table1 ADD COLUMN code VARCHAR(25);
Then an update like this:-
UPDATE table2
INNER JOIN
(
SELECT id, user, code, #rank2:=IF(#prev_user2 = user, #rank2+1, 1) AS rank, #prev_user2 := user
FROM table2
CROSS JOIN (SELECT #rank2:=0, #prev_user2:='') sub2
ORDER BY user, id
) tab_2
ON table2.id = tab_2.id
INNER JOIN
(
SELECT id, user, log_id, #rank1:=IF(#prev_user1 = user, #rank1+1, 1) AS rank, #prev_user1 = user
FROM table1
CROSS JOIN (SELECT #rank1:=0, #prev_user1:='') sub1
ORDER BY user, id
) tab_1
ON tab_1.user = tab_2.user
AND tab_1.rank = tab_2.rank
SET table2.log_id = tab_1.log_id;
What this is doing is a pair of sub queries which adds a rank to each tables records (I have added the rank within the user, which should make it cope a bit better if one user on one table has an extra record). The results of these sub queries are joined together, and then joined to table2 to do the actual update (the sub query for table2 to get the rank can be joined to table2 based on id).
This seems to work when done in SQL fiddle:-
http://www.sqlfiddle.com/#!2/ad8a6b/1

Try this:
UPDATE dbo.Table2 A
SET A.log_id = B.log_id
INNER JOIN dbo.Table1 B
ON A.user = B.user
But first you have to add log_id column to table2 with alter query.

try this:
alter table table1 add column code varchar(100);
update table1,table2 set table1.code = table2.code where table1.id=table2.id and table1.user=table2.user;

I figured out the solution. I added 2 columns rank and prev_user in both tables, and incremented the value for rank from 1 for the first record for user_x to n for the nth record for user_x, as below:
ALTER TABLE users ADD COLUMN rank tinyInt(1);
ALTER TABLE users ADD COLUMN prevuser varchar(50);
SET #prevuser = '';
SET #rank = 0;
UPDATE users
SET rank = (#rank:=IF(#prevuser != user,1,#rank+1)),
prevuser = (#prevuser := user)
ORDER BY user,id;
ALTER TABLE users DROP COLUMN prevuser;
and,
ALTER TABLE logs ADD COLUMN rank tinyInt(1);
ALTER TABLE logs ADD COLUMN prevuser varchar(50);
SET #prevuser = '';
SET #rank = 0;
UPDATE logs
SET rank = (#rank:=IF(#prevuser != user,1,#rank+1)),
prevuser = (#prevuser := user)
ORDER BY user,id;
ALTER TABLE logs DROP COLUMN prevuser;
Now records can be matched between the tables using user & rank. I added the field log_id to users and updated it as below:
UPDATE users, logs SET users.log_id=logs.log_id WHERE users.user=logs.user AND users.rank = logs.rank;
And voila!

Related

MySQL JOIN two tables by one value and take other results (without that value too)

I have some problem with a mySQL query.
The table A is this:
A.id
A.value1
A.user
Table B is:
B.id
B.user
I need to find value_that_i_need from query, by searching for B.user.
But I don't need only values with A.user, i need all values from Table A with the same A.id (inside Table A) that matches B.user.
So I need all distinct id (where there is B.user=A.user) and search for them inside table A by A.id.
I want to avoid 2 different queries! Already tried differents JOIN, nothing works for me.
EDIT
Ok, i will ty to explain the problem in a easiest way.
I have this table:
+---------+------------+
| id_user | another_id |
+---------+------------+
id_user -> unique id for each user
another_id -> an id related to something like a group
another_id can be the same to more users, but i need to take only users who are inside my same groups.
So i will have to check my groups (by searching my id_user) and then i have to see all users with my same another_id.
Problem is that if i query something like this:
SELECT * FROM table0 AS t0, something_like_groups AS slg
JOIN user_inside_group as uig ON slg.id_group=uig.group_id AND slg.id_user='my_user_id'
WHERE slg.id='id_group' AND t0.user_id=uig.user_id
Actually i have to join 3 tables, but the problem is that i need to find the "group" inside i am and get ALL informations about all users inside my same group. (without an additional query)
Perhaps you just want to find the min id based on b user and then get all the rows from a which match. for example
drop table if exists t,t1;
create table t( id int,user varchar(10));
create table t1( id int,user varchar(10));
insert into t values
(1,'aaa'),(1,'bbb'),(2,'ccc');
insert into t1 values
(1,'bbb'),(2,'ccc')
;
select t.id,t.user
from t
join
(
select t1.user,min(t.id) minid
from t1
join t on t.user = t1.user
group by t1.user
) s
on t.id = s.minid;
+------+------+
| id | user |
+------+------+
| 1 | aaa |
| 1 | bbb |
| 2 | ccc |
+------+------+
3 rows in set (0.00 sec)

Update MySQL Table from Subquery/Joined Same Table

I've seen many questions along this issue, but can't get this to work.
I want to UPDATE multiple columns in a table (but will start with one) based upon a calculated value from the same table.
It is a list of transactions per customer, per month.
TransID | Cust | Month | Value | PastValue | FutureValue
1 | 1 | 2018-01-01 | 45 |
2 | 1 | 2018-02-01 | 0 |
3 | 1 | 2018-03-01 | 35 |
4 | 1 | 2018-04-01 | 80 |
.
UPDATE tbl_transaction a
SET PrevMnthValue =
(SELECT COUNT(TransactionID) FROM tbl_transaction b WHERE b.Cust=a.Cust AND b.Month<a.Month)
But we get the dreaded 'Can't update a table using a where with a subquery of the same table).
I've tried to nest the subquery as this has been touted as a workaround:
UPDATE tbl_transactions a
SET
PastValue =
(
SELECT CNT FROM
(
SELECT
COUNT(TransactionID) AS CNT
FROM tbl_transactions b
WHERE
b.CustomerRef=a.CustomerRef AND b.Month<a.Month
) x
),
FutureValue =
(
SELECT CNT FROM
(
SELECT
COUNT(TransactionID) AS CNT
FROM tbl_transactions b
WHERE
b.CustomerRef=a.CustomerRef AND b.Month>a.Month
) x
)
But I get an UNKNOWN a.CustomerRef in WHERE clause. Where am I going wrong?
You can't update and read from one table at the same time.
MySQL documentation tell about it
You cannot update a table and select from the same table in a subquery.
At first you must select necessary data and save them to somewhere, for example to temporary table
CREATE TEMPORARY TABLE IF NOT EXISTS `temp` AS (
SELECT
COUNT(`TransactionID`) AS CNT,
`CustomerRef`,
`Month`
FROM `tbl_transactions`
GROUP BY `Custom,erRef`, `Month`
);
After it, you can use JOIN statement for update table
UPDATE `tbl_transactions` RIGTH
JOIN `temp` ON `temp`.`CustomerRef` = `tbl_transactions`.`CustomerRef`
AND `temp`.`Month` < `tbl_transactions`.`Month`
SET `tbl_transactions`.`PastValue` = `temp`.`cnt`
UPDATED: if you want to update several columns by different condition you can combine temporary table, UPDATE + RIGHT JOIN and CASE statement. For example:
UPDATE `tbl_transactions`
RIGTH JOIN `temp` ON `temp`.`CustomerRef` = `tbl_transactions`.`CustomerRef`
SET `tbl_transactions`.`PastValue` = CASE
WHEN `temp`.`Month` < `tbl_transactions`.`Month` THEN `temp`.`cnt`
ELSE `tbl_transactions`.`PastValue`
END,
`tbl_transactions`.`FutureValue` = CASE
WHEN `temp`.`Month` > `tbl_transactions`.`Month` THEN `temp`.`cnt`
ELSE `tbl_transactions`.`FutureValue`
END
You can try below
UPDATE tbl_transactions a
Join
( SELECT CustomerRef,COUNT(TransactionID) AS CNT FROM tbl_transactions b
group by CustomerRef)x
SET PastValue = CNT
WHERE x.CustomerRef=a.CustomerRef AND x.Month<a.Month

How to write select query along with IF condition in place of column name in mysql query

SELECT DISTINCT o.receipt,
if(SELECT status FROM list WHERE receipt = o.receipt GROUP BY receipt) as status
FROM orderlist o
What is the correct way to write the above query If condition and get result like below example.
Same receipt(orderId) has more than one tuple(row) and all this tuple might have different status value.And I want to set value as per receipt(orderId) first tuple status using IF statement.
IF(status = 'shipped', "Yes", "NO");
If you data model looks like this (ie you have a way of distinguishing the order of events) then a limit in your subquery might do.
drop table if exists t,t1;
create table t(id int);
create table t1(id int, tid int, status varchar(10));
insert into t values (1),(2);
insert into t1 values (1,1,'a'),(2,1,'Shipped'),(3,1,'Returned'), (4,2,'shipped');
select t.id,
if(
(select status from t1 where t1.tid = t.id order by id limit 1)
= 'Shipped','yes','no') shipped
from t;
Result
+------+---------+
| id | shipped |
+------+---------+
| 1 | no |
| 2 | yes |
+------+---------+
2 rows in set (0.00 sec)
But isn't shipment usually the last status?
Try this.
SELECT DISTINCT o.receipt, CASE status when 'Shipped' then 'Yes' ELSE 'No' END as status
FROM orderlist o join receipt r on o.receipt = r.receipt

Sql update statement with variable

I have two table named "user" and "logs".
user table has column named "userID" which is also pk.
logs table has two columns named "log_detail" and "userID".
What I want to query is "get all user.userID values one by one from user table and check them if log_detail value contains this value, If it countains then update logs.userID with this userID value".
I tried some queries but I really don't know how to do it.
By the way I am using Mysql.
UPDATE logs
SET logs.userID = user.userID
SELECT userID
FROM logs
WHERE logs.userID LIKE concat("%",user.userID,"%");
SQL Fiddle
Schema details
create table user
(userid varchar(30));
create table logs
(log_detail varchar(100),
userid varchar(30));
insert into user values('user1');
insert into user values('user2');
insert into user values('user3');
insert into logs values('update by user1','user3');
insert into logs values('inserted by user2','user2');
insert into logs values('inserted by user3',null);
Table data before update
| log_detail | userid |
|-------------------|--------|
| update by user1 | user3 |
| inserted by user2 | user2 |
| inserted by user3 | (null) |
Update Query
update logs join user
set logs.userid=user.userid
where logs.log_detail LIKE concat("%",user.userID,"%");
Table data after update
| log_detail | userid |
|-------------------|--------|
| update by user1 | user1 |
| inserted by user2 | user2 |
| inserted by user3 | user3 |
update logs
inner join 'user' on
logs.userID = user.userID
set logs.userID = user.userID
WHERE logs.log_detail LIKE concat("%",user.userID,"%");
Also take into account that user is a reserved keyword in mysql
I am not very much familiar with MYSQL but in oracle we can satisfy your requirements like this
CREATE TABLE USERS
( USERID VARCHAR2(100 CHAR) PRIMARY KEY
);
INSERT INTO USERS
(SELECT 'AVROY'||LEVEL FROM DUAL CONNECT BY LEVEL < 10
);
COMMIT;
CREATE TABLE logs_UID
(
log_dtl VARCHAR2(200 CHAR),
UserID VARCHAR2(100 CHAR)
);
INSERT INTO logs_UID
SELECT 'test avroy'||level,NULL FROM dual CONNECT BY level < 10 ;
COMMIT;
MERGE INTO LOGS_UID lu USING USERS u
ON (UPPER(u.userid) = UPPER(SUBSTR(LU.LOG_DTL,INSTR(UPPER(lu.LOG_DTL),UPPER(u.userid),1),LENGTH(lu.log_dtl)-INSTR(UPPER(lu.LOG_DTL),UPPER(u.userid),1)+1)))
WHEN MATCHED THEN
UPDATE SET lu.USERID = u.USERID;
COMMIT;
Try with the following query.I think you will get what you need.
update logs l
join users u
on l.log_detail = u.userid
set l.userid = u.userid
I think we no need to write where condition again,because we are using inner join in the query.
Thank you.

Faster sql query then join

I have a big table with more than 10,000 rows and it will grow to 1,000,000 in the near future, and I need to run a query which gives back a Time value for each keyword for each user. I have one right now which is quite slow because I use left joins and it needs one subquery / keyword:
SELECT rawdata.user, t1.Facebook_Time, t2.Outlook_Time, t3.Excel_time
FROM
rawdata left join
(SELECT user, sec_to_time(SuM(time_to_sec(EndTime-StartTime))) as 'Facebook_Time'
FROM rawdata
WHERE MainWindowTitle LIKE '%Facebook%'
GROUP by user)t1 on rawdata.user = t1.user left join
(SELECT user, sec_to_time(SuM(time_to_sec(EndTime-StartTime))) as 'Outlook_Time'
FROM rawdata
WHERE MainWindowTitle LIKE '%Outlook%'
GROUP by user)t2 on rawdata.user = t2.user left join
(SELECT user, sec_to_time(SuM(time_to_sec(EndTime-StartTime))) as 'Excel_Time'
FROM rawdata
WHERE MainWindowTitle LIKE '%Excel%'
GROUP by user)t3 on rawdata.user = t3.user
The table looks like this:
WindowTitle | StartTime | EndTime | User
------------|-----------|---------|---------
Form1 | DateTime | DateTime| user1
Form2 | DateTime | DateTime| user2
... | ... | ... | ...
Form_n | DateTime | DateTime| user_n
The output should looks like this:
User | Keyword | SUM(EndTime-StartTime)
-------|-----------|-----------------------
User1 | 'Facebook'| 00:34:12
User1 | 'Outlook' | 00:12:34
User1 | 'Excel' | 00:43:13
User2 | 'Facebook'| 00:34:12
User2 | 'Outlook' | 00:12:34
User2 | 'Excel' | 00:43:13
... | ... | ...
User_n | ... | ...
And the question is, which is the fastest way in MySQL to do this?
I think your wildcard searches are probably what's slowing it down the most, since you can't really utilize indexes on those fields. Also if you can avoid doing sub-queries and just do a straight join, it might help, but the wildcard searches are far worse. Is there anyway you could change the table to have a categoryName or categoryID that can have an index and not require a wildcard search? Like "where categoryName = 'Outlook'"
To optimize the data in your tables, add a categoryID (ideally this would reference a separate table, but let's just use arbitrary numbers for this example):
alter table rawData add column categoryID int not null
alter table rawData add index (categoryID)
Then populate the categoryID field for the existing data:
update rawData set categoryID=1 where name like '%Outlook%'
update rawData set categoryID=2 where name like '%Facebook%'
-- etc...
Then change your insert to follow the same rules.
Then make your SELECT query like this (changed wild cards to categoryID):
SELECT rawdata.user, t1.Facebook_Time, t2.Outlook_Time, t3.Excel_time
FROM
rawdata left join
(SELECT user, sec_to_time(SuM(time_to_sec(EndTime-StartTime))) as 'Facebook_Time'
FROM rawdata
WHERE categoryID = 2
GROUP by user)t1 on rawdata.user = t1.user left join
(SELECT user, sec_to_time(SuM(time_to_sec(EndTime-StartTime))) as 'Outlook_Time'
FROM rawdata
WHERE categoryID = 1
GROUP by user)t2 on rawdata.user = t2.user left join
(SELECT user, sec_to_time(SuM(time_to_sec(EndTime-StartTime))) as 'Excel_Time'
FROM rawdata
WHERE categoryID = 3
GROUP by user)t3 on rawdata.user = t3.user