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
Related
I want to fetch data from two table and apply arithmetic operation on the column.
This is wha I tried :
String sql = "SELECT SUM(S.san_recover-C.amount) as total
FROM sanction S
LEFT JOIN collection C ON S.client_id = C.client_id
WHERE S.client_id=?";
This code is working only when there is value in both tables, but if there is no value in one of two tables there is no result.
SELECT SUM(S.san_recover - C.amount) as total
FROM sanction S
LEFT JOIN collection C ON S.client_id = C.client_id
WHERE S.client_id = ?
The problem with your query lies in the SUM() function. When the left join does not bring back records, then c.amount is NULL. When substracting NULL from something, you get a NULL result, which then propagates across the computation, and you end up with a NULL result for the SUM().
You probably want COALESCE(), like so:
SELECT SUM(S.san_recover - COALESCE(C.amount, 0)) as total
FROM sanction S
LEFT JOIN collection C ON S.client_id = C.client_id
WHERE S.client_id = ?
Where there is a possibility that a client may exist in one table but no another a full join would be appropriate but since mysql does not have such a thing then a union in a sub query will do
drop table if exists sanctions,collections;
create table sanctions(client_id int, amount int);
create table collections(client_id int, amount int);
insert into sanctions values
(1,10),(1,10),(2,10);
insert into collections values
(1,5),(3,10);
Select sum(Samount - camount)
From
(Select sum(amount) Samount, 0 as camount from sanctions where client_id =3
Union all
Select 0,sum(amount) as camount from collections where client_id =3
) s
;
+------------------------+
| sum(Samount - camount) |
+------------------------+
| -10 |
+------------------------+
1 row in set (0.00 sec)
If you want to do this for all clients
Select client_id,sum(Samount - camount) net
From
(Select client_id,sum(amount) Samount, 0 as camount from sanctions group by client_id
Union all
Select client_id,0,sum(amount) as camount from collections group by client_id
) s
group by client_id
;
+-----------+------+
| client_id | net |
+-----------+------+
| 1 | 15 |
| 2 | 10 |
| 3 | -10 |
+-----------+------+
3 rows in set (0.00 sec)
I want to find ranking and then update their rank column according to their id.
My data is as follows (Table Member).
MEMBER_ID | LOAN_AMOUNT | Rank
1 | 2,000.00 | 0
2 | 1,000.00 | 0
3 | 4,000.00 | 0
4 | 1,000.00 | 0
Now I want to find their ranking and update rank column according to it.
My general query is something like it:
UPDATE
Member AS dest,
(
SELECT RANK() OVER(ORDER BY t.loan_amount DESC) as [rank],
t.memeber_id,t.loan_amount
FROM Member
) AS src
SET
dest.rank = src.rank
INNER JOIN dest.memeber_id = src.memeber_id
Is there any faster way to update my table?
In mysql the update join sintax should be
UPDATE
Member AS dest
INNER JOIN
(
SELECT RANK() OVER(ORDER BY t.loan_amount DESC) as [rank],
t.memeber_id,t.loan_amount
FROM Member
) src ON dest.memeber_id = src.memeber_id
SET dest.rank = src.rank
anyway you should avoid store data easly calcuated on fly .. in the same time you perform the select can easly obtain also the rank()
I have tow tables tbl_product_checkout and tbl_product_checkout_status in which I want to get the last row from tbl_product_checkout_status
//tbl_product_checkout
product_checkout_id user_id product_checkout_order_no
-----------------------------------------------------------
1 1 ORD123456
//tbl_product_checkout_status
checkout_status_id product_checkout_id checkout_status_check
------------------------------------------------------------------
1 1 Dispatched
2 1 Delivered
I have tried using the following query
SELECT *
FROM tbl_product_checkout pc
LEFT
JOIN tbl_product_checkout_status cs
ON cs.product_checkout_id = pc.product_checkout_id
WHERE pc.user_id = 1
GROUP
BY pc.product_checkout_id
ORDER
BY cs.checkout_status_id DESC
but the output for above query is,
user_id product_checkout_order_no checkout_status_check
-------------------------------------------------------------
1 ORD123456 Dispatched
but I want the result as,
user_id product_checkout_order_no checkout_status_check
-------------------------------------------------------------
1 ORD123456 Delivered
Add a where = max sub query eg
DROP TABLE IF EXISTS tbl_product_checkout,tbl_product_checkout_status;
CREATE TABLE tbl_product_checkout(product_checkout_id INT, user_id INT, product_checkout_order_no VARCHAR(20));
INSERT INTO tbl_product_checkout VALUES
( 1 , 1 , 'ORD123456');
CREATE TABLE tbl_product_checkout_status(checkout_status_id INT, product_checkout_id INT, checkout_status_check VARCHAR(20));
INSERT INTO tbl_product_checkout_status VALUES
( 1 , 1 , 'Dispatched'),
( 2 , 1 , 'Delivered');
SELECT * FROM
tbl_product_checkout T1
LEFT JOIN tbl_product_checkout_status T2 ON T1.PRODUCT_CHECKOUT_ID = T2.PRODUCT_CHECKOUT_ID
WHERE T2.CHECKOUT_STATUS_ID = (
SELECT MAX(T3.CHECKOUT_STATUS_ID)
FROM tbl_product_checkout_status T3
WHERE T3.PRODUCT_CHECKOUT_ID = T2.PRODUCT_CHECKOUT_ID
)
;
Result
+---------------------+---------+---------------------------+--------------------+---------------------+-----------------------+
| product_checkout_id | user_id | product_checkout_order_no | checkout_status_id | product_checkout_id | checkout_status_check |
+---------------------+---------+---------------------------+--------------------+---------------------+-----------------------+
| 1 | 1 | ORD123456 | 2 | 1 | Delivered |
+---------------------+---------+---------------------------+--------------------+---------------------+-----------------------+
1 row in set (0.00 sec)
I think your group by mess up your desired outcome. I worked on your given database schema and cretaed a fiddle and managed to get your desired outcome. So your sql should be something like this:
SELECT * FROM tbl_product_checkout as pc
LEFT JOIN tbl_product_checkout_status as cs ON
cs.product_checkout_id = pc.product_checkout_id
WHERE pc.user_id = 1 ORDER BY cs.checkout_status_id DESC limit 1
By using limit 1, you will get last row as we ordered by DESC.
Keep in mind that i removed date part since there was no date on your example code.
Check Out Fiddle
Lets say that I have a table ( MS-ACCESS / MYSQL ) with two columns ( Time 'hh:mm:ss' , Value ) and i want to get most frequent value for each group of row.
for example i have
Time | Value
4:35:49 | 122
4:35:49 | 122
4:35:50 | 121
4:35:50 | 121
4:35:50 | 111
4:35:51 | 122
4:35:51 | 111
4:35:51 | 111
4:35:51 | 132
4:35:51 | 132
And i want to get most frequent value of each Time
Time | Value
4:35:49 | 122
4:35:50 | 121
4:35:51 | 132
Thanks in advance
Remark
I need to get the same result of this Excel solution : Get the most frequent value for each group
** MY SQL Solution **
I found a solution(Source) that works fine with mysql but i can't get it to work in ms-access:
select cnt1.`Time`,MAX(cnt1.`Value`)
from (select COUNT(*) as total, `Time`,`Value`
from `my_table`
group by `Time`,`Value`) cnt1,
(select MAX(total) as maxtotal from (select COUNT(*) as total,
`Time`,`Value` from `my_table` group by `Time`,`Value`) cnt3 ) cnt2
where cnt1.total = cnt2.maxtotal GROUP BY cnt1.`Time`
Consider an INNER JOIN to match the two derived table subqueries rather than a list of subquery select statements matched with WHERE clause. This has been tested in MS Access:
SELECT MaxCountSub.`Time`, CountSub.`Value`
FROM
(SELECT myTable.`Time`, myTable.`Value`, Count(myTable.`Value`) AS CountOfValue
FROM myTable
GROUP BY myTable.`Time`, myTable.`Value`) As CountSub
INNER JOIN
(SELECT dT.`Time`, Max(CountOfValue) As MaxCountOfValue
FROM
(SELECT myTable.`Time`, myTable.`Value`, Count(myTable.`Value`) AS CountOfValue
FROM myTable
GROUP BY myTable.`Time`, myTable.`Value`) As dT
GROUP BY dT.`Time`) As MaxCountSub
ON CountSub.`Time` = MaxCountSub.`Time`
AND CountSub.CountOfValue = MaxCountSub.MaxCountOfValue
you can do this by query like this:
select time, value
from (select value, time from your_table
group by value , time
order by count(time) desc
) temp where temp.value = value
group by value
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!