UPDATE table JOIN table SET only updates unique id's - mysql

I want to update Table B with sum(aa) from Table A where A.id=B.id and into the column set by A.d (E.g. A:223, d1, 5 updates B: id 223, column d1 = d1 + 5).
TABLE A TABLE B
id d aa id d1 d4 d7
--- +-------+---- ----+-------+-------+-----
223 | d1 | 5 221 | 0 | 5 | 0 <
224 | d1 | 5 222 | 0 | 5 | 0 <
225 | d1 | 5 223 | 5 | 0 < | 0 <
226 | d1 | 5 224 | 5 | 0 < | 0 <
| | 225 | 5 | 0 < | 0 <
221 | d4 | 5 226 | 5 | 0 < | 0 <
222 | d4 | 5 < = missing
223 | d4 | 5
224 | d4 | 5
225 | d4 | 5
226 | d4 | 5
| | EXPECTED:
221 | d7 | 5 id d1 d4 d7
222 | d7 | 4 ----+-------+-------+------
223 | d7 | 5 221 | 0 | 5 | 5
224 | d7 | 5 222 | 0 | 5 | 5
224 | d7 | 5 223 | 5 | 5 | 5
225 | d7 | 5 224 | 5 | 5 | 10
226 | d7 | 5 225 | 5 | 5 | 5
226 | d7 | 5 226 | 5 | 5 | 15
226 | d7 | 5
Query: (As I can't use dynamic column names I need a CASE WHEN THEN)
UPDATE `TABLE_B` `B`
JOIN(
SELECT `id`,`d`,SUM(`aa`)`aa`
FROM `TABLE_A` GROUP BY `id`,`d`
) `A`
ON `A`.`id`=`B`.`id`
SET
`d1`= CASE `B`.`d` WHEN "d1" THEN `A`.`d1`+`B`.`aa` ELSE `A`.`d1` END,
`d2`= CASE `B`.`d` WHEN "d2" THEN `A`.`d2`+`B`.`aa` ELSE `A`.`d2` END,
`d3`= CASE `B`.`d` WHEN "d3" THEN `A`.`d3`+`B`.`aa` ELSE `A`.`d3` END,
`d4`= CASE `B`.`d` WHEN "d4" THEN `A`.`d4`+`B`.`aa` ELSE `A`.`d4` END,
`d5`= CASE `B`.`d` WHEN "d5" THEN `A`.`d5`+`B`.`aa` ELSE `A`.`d5` END,
`d6`= CASE `B`.`d` WHEN "d6" THEN `A`.`d6`+`B`.`aa` ELSE `A`.`d6` END,
`d7`= CASE `B`.`d` WHEN "d7" THEN `A`.`d7`+`B`.`aa` ELSE `A`.`d7` END
The problem is this only updates unique id's. It takes 223, 224, 225, 226 from d1, then only 221, 222 from d4 and nothing from d7, instead of updating 16 values (4x d1, 6x d4, 6x d7).
So where in the JOIN am I doing wrong?
Fiddle

I got a solution by using two subqueries to first make a full table with all columns in it and only one row per id:
SELECT
`id`,
CASE `d` WHEN "d1" THEN SUM(`aa`) ELSE 0 END AS `a1`,
CASE `d` WHEN "d4" THEN SUM(`aa`) ELSE 0 END AS `a4`,
CASE `d` WHEN "d7" THEN SUM(`aa`) ELSE 0 END AS `a7`
FROM `TABLE_A`
GROUP BY `id`,`d`
Giving (example only for id's 221 and 226):
id d1 d4 d7
----+---+---+----
221 | 0 | 5 | 0
221 | 0 | 0 | 5
226 | 5 | 0 | 0
226 | 0 | 5 | 0
226 | 0 | 0 | 15
Then make sure there is only one row for each id:
SELECT
`id`,
SUM(`a1`) `a1`,
SUM(`a4`) `a4`,
SUM(`a7`) `a7`
FROM(
.... previous select ...
)
GROUP BY `id`
Giving (example only for id's 221 and 226):
id d1 d4 d7
----+---+---+----
221 | 0 | 5 | 5
226 | 5 | 5 | 15
Now I could update Table B:
UPDATE `TABLE_B` `B`
JOIN(
SELECT
`id`,
SUM(`a1`) `a1`,
SUM(`a4`) `a4`,
SUM(`a7`) `a7`
FROM(
SELECT
`id`,
CASE `d` WHEN "d1" THEN SUM(`aa`) ELSE 0 END AS `a1`,
CASE `d` WHEN "d4" THEN SUM(`aa`) ELSE 0 END AS `a4`,
CASE `d` WHEN "d7" THEN SUM(`aa`) ELSE 0 END AS `a7`
FROM `TABLE_A`
GROUP BY `id`,`d`
) `C`
GROUP BY `id`
)`A`
ON `A`.`id`=`B`.`id`
SET
`d1`=`d1`+`a1`,
`d4`=`d4`+`a4`,
`d7`=`d7`+`a7`
Maybe there are better solutions, but this works.

Related

Adding Amt1 and Amt2 values to the output column value of previous record

Input:
dated amount Amt1 Amt2
1/1/2017 100 0 10
1/2/2017 100 10 0
1/4/2017 100 0 0
1/6/2017 100 300 10
1/10/2017 100 0 20
1/11/2017 100 350 650
1/12/2017 100 0 234
Output:
dated amount Amt1 Amt2 Output Column
1/1/2017 100 0 10 100
1/2/2017 100 10 0 110
1/4/2017 100 0 0 120
1/6/2017 100 300 10 120
1/10/2017 100 0 20 430
1/11/2017 100 350 650 450
1/12/2017 100 0 234 1450
Output column is calculated with adding Amt1 and Amt2 values to the Output Column value of previous record.
Example: Output Column of
first record is as it is of Amount column,
second record will get from first record value of output column and Amt1 and Amt2 of first record i.e 100+0+10=110,
third record is from 110+10+0=120
fourth record is from 120+0+0=120
fifth record is from 120+300+10=430 ...
There are lots of examples of how to calculate running totals on this site and here's one which uses a variable. I am concerned that the purpose of the amount column is not defined but this solution works with the data provided for installation with mysql lower than version 8 (it will work with version 8 or above but there are better ways of doing it there). #tcadidot0 no hard coding required.
drop table if exists t;
create table t
( dated date, amount int, Amt1 int, Amt2 int);
insert into t values
(str_to_date('1/1/2017','%d/%m/%Y') , 100 , 0 , 10),
(str_to_date('1/2/2017','%d/%m/%Y') , 100 , 10 , 0),
(str_to_date('1/4/2017','%d/%m/%Y') , 100 , 0 , 0),
(str_to_date('1/6/2017','%d/%m/%Y') , 100 , 300 , 10),
(str_to_date('1/10/2017','%d/%m/%Y') , 100 , 0 , 20),
(str_to_date('1/11/2017','%d/%m/%Y') , 100 , 350 , 650),
(str_to_date('1/12/2017','%d/%m/%Y') , 100 , 0 , 234);
select t.dated,t.amount,t.amt1,t.amt2,
if(t.dated = (select min(t1.dated) from t t1),#op:=amount,
#op:=#op +
(select amt1 + amt2 from t t1 where t1.dated < t.dated order by t1.dated desc limit 1)
) op
from t
cross join (select #op:=0) o
order by dated;
+------------+--------+------+------+------+
| dated | amount | amt1 | amt2 | op |
+------------+--------+------+------+------+
| 2017-01-01 | 100 | 0 | 10 | 100 |
| 2017-02-01 | 100 | 10 | 0 | 110 |
| 2017-04-01 | 100 | 0 | 0 | 120 |
| 2017-06-01 | 100 | 300 | 10 | 120 |
| 2017-10-01 | 100 | 0 | 20 | 430 |
| 2017-11-01 | 100 | 350 | 650 | 450 |
| 2017-12-01 | 100 | 0 | 234 | 1450 |
+------------+--------+------+------+------+
7 rows in set (0.00 sec)

How to update the same row with condition in MySql?

Here's data
KeyID | Queue | Pay
65 1 0
60 2 0
58 3 1
57 4 1
55 5 0
54 6 0
53 7 1
50 8 1
if the data like this , I need a single MySql query to update it to be like below data table which update only Queue column.
KeyID | Queue | Pay
65 0 0
60 0 0
58 1 1
57 2 1
55 0 0
54 0 0
53 3 1
50 4 1
I have try this
update tabl1
set Queue = case when Pay = 0 then Queue=Queue-1 else Queue
But Queue number not look like this.
Please suggest.
Thank you in advance
We can try joining to a subquery which computes the sequence:
UPDATE yourTable k1
INNER JOIN
(
SELECT KeyID, rn
FROM
(
SELECT t1.KeyID,
(SELECT SUM(t2.Pay = 1) FROM yourTable t2
WHERE t2.KeyID >= t1.KeyID) rn
FROM yourTable t1
) t
) k2
ON k1.KeyID = k2.KeyID
SET
Queue = CASE WHEN Pay = 0 THEN 0 ELSE k2.rn END;
To see how the above logic is working, here is what the intermediate join table looks like:
KeyID | Queue | Pay | rn
65 | 1 | 0 | 0
60 | 2 | 0 | 0
58 | 3 | 1 | 1
57 | 4 | 1 | 2
55 | 5 | 0 | 2
54 | 6 | 0 | 2
53 | 7 | 1 | 3
50 | 8 | 1 | 4
That is, the innermost correlated subquery generates the queue sequence by counting the number of times Pay is 1.
Note that if you are using MySQL 8+, then there is a much simpler query using analytic functions:
UPDATE yourTable
SET Queue = CASE WHEN Pay = 0
THEN 0
ELSE SUM(Pay = 1) OVER (ORDER BY KeyID DESC) END;

MYSQL Update rows with duplicate value but oldest date

Tried many suggestions to get this to work, difficult to explain so below is the data I have and the result I want to achieve.
I want to update the 'Active' Column to 0 if its not the MAX Ldate.
ID | SNumber | Ldate | Active
4804 188 2015-11-17 1
4806 189 2015-11-25 1
4807 190 2015-11-25 1
4808 191 2015-11-19 1
4809 192 2015-11-19 1
4820 193 2015-11-17 1
4821 193 2016-06-08 1
4830 194 2015-11-17 1
4831 194 2016-06-08 1
4828 195 2015-11-17 1
4829 195 2016-06-08 1
ID SNumber Ldate Active
4804 188 2015-11-17 1
4806 189 2015-11-25 1
4807 190 2015-11-25 1
4808 191 2015-11-19 1
4809 192 2015-11-19 1
4820 193 2015-11-17 0
4821 193 2016-06-08 1
4830 194 2015-11-17 0
4831 194 2016-06-08 1
4828 195 2015-11-17 0
4829 195 2016-06-08 1
I can get all rows with the MAX Ldate by "select ID, SNumber, Ldate from (select * from tbl order by SNumber, Ldate desc) x group by SNumber"
Thanks for taking the time to look!
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(id SERIAL PRIMARY KEY
,SNumber INT NOT NULL
,Ldate DATE NOT NULL
);
INSERT INTO my_table VALUES
(4804,188,'2015-11-17'),
(4806,189,'2015-11-25'),
(4807,190,'2015-11-25'),
(4808,191,'2015-11-19'),
(4809,192,'2015-11-19'),
(4820,193,'2015-11-17'),
(4821,193,'2016-06-08'),
(4830,194,'2015-11-17'),
(4831,194,'2016-06-08'),
(4828,195,'2015-11-17'),
(4829,195,'2016-06-08');
SELECT x.*
, COALESCE(x.ldate = y.ldate,0) active
FROM my_table x
LEFT
JOIN
( SELECT snumber
, MAX(ldate) ldate
FROM my_table
GROUP
BY snumber
) y
ON y.snumber = x.snumber
AND y.ldate = x.ldate;
+------+---------+------------+--------+
| id | SNumber | Ldate | active |
+------+---------+------------+--------+
| 4804 | 188 | 2015-11-17 | 1 |
| 4806 | 189 | 2015-11-25 | 1 |
| 4807 | 190 | 2015-11-25 | 1 |
| 4808 | 191 | 2015-11-19 | 1 |
| 4809 | 192 | 2015-11-19 | 1 |
| 4820 | 193 | 2015-11-17 | 0 |
| 4821 | 193 | 2016-06-08 | 1 |
| 4830 | 194 | 2015-11-17 | 0 |
| 4831 | 194 | 2016-06-08 | 1 |
| 4828 | 195 | 2015-11-17 | 0 |
| 4829 | 195 | 2016-06-08 | 1 |
+------+---------+------------+--------+
I can't think why you'd store this, but it's easy enough to change the above to an UPDATE. It might look something like this (obviously, you'd need to alter the table design above first)...
UPDATE my_table x
LEFT
JOIN
( SELECT snumber
, MAX(ldate) ldate
FROM my_table
GROUP
BY snumber
) y
ON y.snumber = x.snumber
AND y.ldate = x.ldate
SET x.active = 0
WHERE y.snumber IS NULL;
But I think I would normally use an INNER JOIN for an UPDATE, in which case it might look like this (perhaps bound up inside a transaction)...
UPDATE my_table SET active = 0;
UPDATE my_table x
JOIN
( SELECT snumber
, MAX(ldate) ldate
FROM my_table
GROUP
BY snumber
) y
ON y.snumber = x.snumber
AND y.ldate = x.ldate
SET x.active = 1;

Match column name to data in MYSQL

I have data like in table.
Item | 7/7/15 | 7/8/15 | 7/9/15
1 | 23 | 24 | 25
2 | 26 | 74 | 96
and
I have table which has,
Item | Date | Number
1 | 7/9/15 | 56
1 | 7/7/15 | 75
1 | 7/8/15 | 63
I want to find sum of Number from 7/7/15 to 7/8/15 from table 1 and sum of the number from second table.
My table should look like
Item | StartDate | EndDate | no. | TotalNumber
item 7/7/15 7/8/15 7/9/15
1 23 24 25
2 26 74 96
item date number
1 7/9/15 56
1 7/7/15 75
1 7/8/15 63
.
SELECT
i1.Item,
'7/7/15' AS "StartDate",
'7/8/15' AS "EndDate",
(SELECT SUM(`7/7/15`)+SUM(`7/8/15`) FROM table1 WHERE item=i1.item) AS no,
(SELECT SUM(number) FROM table2 WHERE item=i1.item) "TotalNumber"
FROM
table2 i2
RIGHT OUTER JOIN table1 i1 on i1.item=i2.item;
item startdate enddate no TotalNumber
1 7/7/15 7/8/15 47 194
1 7/7/15 7/8/15 47 194
1 7/7/15 7/8/15 47 194
2 7/7/15 7/8/15 100
.
It's working..

how to find moving average for all the days for the past 30 days with gap in date set to 0

I want to find moving average of the past 30 days. for example
Today's date is 17/11/15, I have data only the days
Original Data
date qty
06/11/15 5
08/11/15 7
09/11/15 8
10/11/15 12
11/11/15 34
15/11/15 45
16/11/15 12
17/11/15 7
Find 30 day Moving Average Data - Need to get movingAvg just like in below table -movingAvg column
date qty movingAvg
17/10/15 0 0
18/10/15 0 0
19/10/15 0 0
20/10/15 0 0
21/10/15 0 0
22/10/15 0 0
23/10/15 0 0
24/10/15 0 0
25/10/15 0 0
26/10/15 0 0
27/10/15 0 0
28/10/15 0 0
29/10/15 0 0
30/10/15 0 0
31/10/15 0 0
01/11/15 0 0
02/11/15 0 0
03/11/15 0 0
04/11/15 0 0
05/11/15 0 0
06/11/15 5 0.14
07/11/15 0 1.4
08/11/15 7 0.4
09/11/15 8 0.67
10/11/15 12 1.06
11/11/15 34 2.2
12/11/15 0 2.2
13/11/15 0 2.2
14/11/15 0 2.2
15/11/15 45 3.7
16/11/15 12 4.1
17/11/15 7 4.33
Please help me out with the mysql query for this or if there is any alternate for this.
There are lots of ways to skin this cat. Here's one, which uses a calendar table with all plausible dates (a depressingly small data set)...
CREATE TABLE calendar (dt DATE NOT NULL PRIMARY KEY);
-- INSERT values for all plausible dates. Lots of tutorials on this.
CREATE TABLE my_table
(date DATE NOT NULL
,qty INT NOT NULL
,PRIMARY KEY (date)
);
INSERT INTO my_table VALUES
('2015/11/06',5),
('2015/11/08',7),
('2015/11/09',8),
('2015/11/10',12),
('2015/11/11',34),
('2015/11/15',45),
('2015/11/16',12),
('2015/11/17',7);
SELECT a.*
, AVG(b.qty) average
FROM
( SELECT c.dt
, COALESCE(x.qty,0) qty
FROM calendar c
LEFT
JOIN my_table x
ON x.date = c.dt
WHERE c.dt BETWEEN (SELECT MAX(date) - INTERVAL 31 DAY FROM my_table) AND (SELECT MAX(date) FROM my_table)
) a
JOIN
( SELECT c.dt
, COALESCE(x.qty,0) qty
FROM calendar c
LEFT
JOIN my_table x
ON x.date = c.dt
WHERE c.dt BETWEEN (SELECT MAX(date) - INTERVAL 31 DAY FROM my_table) AND (SELECT MAX(date) FROM my_table)
) b
ON b.dt <= a.dt
GROUP
BY a.dt;
+------------+------+---------+
| dt | qty | average |
+------------+------+---------+
| 2015-10-15 | 0 | 0.0000 |
| 2015-10-16 | 0 | 0.0000 |
| 2015-10-17 | 0 | 0.0000 |
| 2015-10-18 | 0 | 0.0000 |
| 2015-10-19 | 0 | 0.0000 |
| 2015-10-20 | 0 | 0.0000 |
| 2015-10-21 | 0 | 0.0000 |
| 2015-10-22 | 0 | 0.0000 |
| 2015-10-23 | 0 | 0.0000 |
| 2015-10-24 | 0 | 0.0000 |
| 2015-10-25 | 0 | 0.0000 |
| 2015-10-26 | 0 | 0.0000 |
| 2015-10-27 | 0 | 0.0000 |
| 2015-10-28 | 0 | 0.0000 |
| 2015-10-29 | 0 | 0.0000 |
| 2015-10-30 | 0 | 0.0000 |
| 2015-10-31 | 0 | 0.0000 |
| 2015-11-01 | 0 | 0.0000 |
| 2015-11-02 | 0 | 0.0000 |
| 2015-11-03 | 0 | 0.0000 |
| 2015-11-04 | 0 | 0.0000 |
| 2015-11-05 | 0 | 0.0000 |
| 2015-11-06 | 5 | 0.2174 |
| 2015-11-07 | 0 | 0.2083 |
| 2015-11-08 | 7 | 0.4800 |
| 2015-11-09 | 8 | 0.7692 |
| 2015-11-10 | 12 | 1.1852 |
| 2015-11-11 | 34 | 2.3571 |
| 2015-11-12 | 0 | 2.2759 |
| 2015-11-13 | 0 | 2.2000 |
| 2015-11-14 | 0 | 2.1290 |
| 2015-11-15 | 45 | 3.4688 |
| 2015-11-16 | 12 | 3.7273 |
| 2015-11-17 | 7 | 3.8235 |
+------------+------+---------+
Use MySQL str_to_date or INTERVAL
select date,qty,avg(qty) from table
WHERE str_to_date(date,'%d/%m/%y') >= DATE(NOW()) - INTERVAL 30 DAY
GROUP BY date
Generating a range of dates, and joining that against the original data. Joining twice, once to get that days qty and once to get the average for the last 30 days.
SELECT sub0.aDate, IFNULL(od1.qty, 0), AVG(IFNULL(od2.qty, 0))
FROM
(
SELECT DATE_SUB( CURRENT_DATE(), INTERVAL (units.a + tens.a * 10) DAY) AS aDate, DATE_SUB( CURRENT_DATE(), INTERVAL ((units.a + tens.a * 10) + 29) DAY) AS prevDate
FROM
(SELECT 0 a UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) units
CROSS JOIN
(SELECT 0 a UNION SELECT 1 UNION SELECT 2) tens
) sub0
INNER JOIN
(
SELECT DATE_SUB( CURRENT_DATE(), INTERVAL (units.a + tens.a * 10) DAY) AS aDate
FROM
(SELECT 0 a UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) units
CROSS JOIN
(SELECT 0 a UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5) tens
) sub1
ON sub1.aDate BETWEEN sub0.prevDate AND sub0.aDate
LEFT OUTER JOIN original_data od1 ON od1.`date` = sub0.aDate
LEFT OUTER JOIN original_data od2 ON od2.`date` = sub1.aDate
GROUP BY aDate, od1.qty
ORDER BY aDate
Try this....
SELECT * FROM your_table
WHERE date >= DATEADD(day,-30, now()) AND date <= now()
Hope this helps..