So I've got 3 tables and I need to calculate a value from the first 2 and then update a field in the third table with that value, and I'm coming across an error.
UPDATE Characters AS c
SET c.Total_DKP = (
(SELECT c.Initial_DKP
FROM c
WHERE c.Name='harrian')-
(SELECT SUM(a.DKP_Change)
FROM Attendance AS a
WHERE a.Name = 'harrian')
)
WHERE c.Name = 'harrian' ;
it gives the error
Table 'harrian.c' doesn't exist
but when I run
UPDATE Characters AS c
SET c.Total_DKP = ( SELECT SUM(a.DKP_Change)
FROM Attendance AS a
WHERE a.Name = 'harrian'
)
WHERE c.Name = 'harrian'
I have no issues, can anyone tell me whats wrong with the first block?
Here is the full query-
UPDATE Characters AS c
SET c.Total_DKP = (
(SELECT c.Inital_DKP FROM Characters AS c WHERE c.Name = 'harrian')
+(SELECT SUM(a.DKP_Change) FROM Attendance AS a WHERE a.Name = 'harrian')
+(SELECT SUM(b.Cost) FROM Raid_Drops AS b WHERE b.Player_Name = 'harrian')
)
WHERE c.Name = 'harrian' ;
I'm now getting this error
You can't specify target table 'c' for update in FROM clause
Your first subquery is:
SELECT c.Initial_DKP FROM c WHERE c.Name='harrian'
Instead of FROM c I think you mean FROM Characters:
SELECT Initial_DKP FROM Characters WHERE Name='harrian'
But if that is the case, the whole UPDATE is better written as
UPDATE Characters AS c
SET c.Total_DKP = c.Initial_DKP - (
SELECT SUM(a.DKP_Change)
FROM Attendance AS a
WHERE a.Name = 'harrian'
)
WHERE c.Name = 'harrian';
Because an update statement cannot use the table it's updating in a subquery.
If there can be no more than one row with Name = 'harrian' in table Characters (e.g if Name is UNIQUE), you can use:
UPDATE Characters AS c
SET c.Total_DKP = (
c.Inital_DKP
+ (SELECT SUM(a.DKP_Change) FROM Attendance AS a WHERE a.Name = 'harrian')
+ (SELECT SUM(b.Cost) FROM Raid_Drops AS b WHERE b.Player_Name = 'harrian')
)
WHERE c.Name = 'harrian' ;
Some parenthesis above are not needed. Also, to avoid the cases where Attendance or Raid_Drops have no rows with Name = 'harrian' and the NULL that will be produced by the SUM() over the empty tables, you need to convert the Nulls to zeros:
UPDATE Characters AS c
SET c.Total_DKP =
c.Inital_DKP
+ COALESCE( (SELECT SUM(a.DKP_Change) FROM Attendance AS a
WHERE a.Name = 'harrian')
), 0)
+ COALESCE( (SELECT SUM(b.Cost) FROM Raid_Drops AS b
WHERE b.Player_Name = 'harrian')
), 0)
WHERE c.Name = 'harrian' ;
Related
I am trying to change the value of a column table with a sum function, this is my code:
For example
c.total = (10-2-3) - (3)
c.total = 2
update tabC c
JOIN tabB b ON b.c_id = c.id
set c.total = (c.v1 - c.v2 - c.v3) - IF(sum(b.payment) is not null, sum(b.payment), 0)
where c.id= 983;
but I get the following error:
ERROR 1111 (HY000): Invalid use of group function
I think the error is sum, but how can I solve that?
Thanls in advance
You need to join with a subquery that uses GROUP BY.
UPDATE tabC c
LEFT JOIN (
SELECT c_id, SUM(payment) AS total_payment
FROM tabB
GROUP BY c_id) AS b ON b.c_id = c.id
SET c.total = (c.v1 - c.v2 - c.v3) - IFNULL(b.total_payment, 0)
WHERE c.id = 983
You should usee a nested subquery whit aggregated resutl for join
update tabC c
JOIN (
select c_id, ifnull(sum(payment),0) tot_payment
from tabB
group by c_id
) b ON b.c_id = c.id
set c.total = (c.v1 - c.v2 - c.v3) - b.tot_payment
where c.id= 983;
For example have such structure:
CREATE TABLE clicks
(`date` varchar(50), `sum` int, `id` int)
;
CREATE TABLE marks
(`click_id` int, `name` varchar(50), `value` varchar(50))
;
where click can have many marks
So example data:
INSERT INTO clicks
(`sum`, `id`, `date`)
VALUES
(100, 1, '2017-01-01'),
(200, 2, '2017-01-01')
;
INSERT INTO marks
(`click_id`, `name`, `value`)
VALUES
(1, 'utm_source', 'test_source1'),
(1, 'utm_medium', 'test_medium1'),
(1, 'utm_term', 'test_term1'),
(2, 'utm_source', 'test_source1'),
(2, 'utm_medium', 'test_medium1')
;
I need to get agregated values of click grouped by date which contains all of selected values.
I make request:
select
c.date,
sum(c.sum)
from clicks as c
left join marks as m ON m.click_id = c.id
where
(m.name = 'utm_source' AND m.value='test_source1') OR
(m.name = 'utm_medium' AND m.value='test_medium1') OR
(m.name = 'utm_term' AND m.value='test_term1')
group by date
and get 2017-01-01 = 700, but I want to get 100 which means that only click 1 has all of marks.
Or if condition will be
(m.name = 'utm_source' AND m.value='test_source1') OR
(m.name = 'utm_medium' AND m.value='test_medium1')
I need to get 300 instead of 600
I found answer in getting distinct click_id by first query and then sum and group by date with condition whereIn, but on real database which is very large and has id as uuid this request executes extrimely slow. Any advices how to get it work propely?
You can achieve it using below queries:
When there are the three conditions then you have to pass the HAVING count(*) >= 3
SELECT cc.DATE
,sum(cc.sum)
FROM clicks AS cc
INNER JOIN (
SELECT id
FROM clicks AS c
LEFT JOIN marks AS m ON m.click_id = c.id
WHERE (
m.NAME = 'utm_source'
AND m.value = 'test_source1'
)
OR (
m.NAME = 'utm_medium'
AND m.value = 'test_medium1'
)
OR (
m.NAME = 'utm_term'
AND m.value = 'test_term1'
)
GROUP BY id
HAVING count(*) >= 3
) AS t ON cc.id = t.id
GROUP BY cc.DATE
When there are the three conditions then you have to pass the HAVING count(*) >= 2
SELECT cc.DATE
,sum(cc.sum)
FROM clicks AS cc
INNER JOIN (
SELECT id
FROM clicks AS c
LEFT JOIN marks AS m ON m.click_id = c.id
WHERE (
m.NAME = 'utm_source'
AND m.value = 'test_source1'
)
OR (
m.NAME = 'utm_medium'
AND m.value = 'test_medium1'
)
GROUP BY id
HAVING count(*) >= 2
) AS t ON cc.id = t.id
GROUP BY cc.DATE
Demo: http://sqlfiddle.com/#!9/fe571a/35
Hope this works for you...
You're getting 700 because the join generates multiple rows for the different IDs. There are 3 rows in the mark table with ID=1 and sum=100 and there are two rows with ID=2 and sum=200. On doing the join where shall have 3 rows with sum=100 and 2 rows with sum=200, so adding these sum gives 700. To fix this you have to aggregate on the click_id too as illustrated below:
select
c.date,
sum(c.sum)
from clicks as c
inner join (select * from marks where (name = 'utm_source' AND
value='test_source1') OR (name = 'utm_medium' AND value='test_medium1')
OR (name = 'utm_term' AND value='test_term1')
group by click_id) as m
ON m.click_id = c.id
group by c.date;
DEMO SQL FIDDLE
I found the right way myself, which works on large amounts of data
The main goal is to make request generate one table with subqueries(conditions) which do not depend on amount of data in results, so the best way is:
select
c.date,
sum(c.sum)
from clicks as c
join marks as m1 ON m1.click_id = c.id
join marks as m2 ON m2.click_id = c.id
join marks as m3 ON m3.click_id = c.id
where
(m1.name = 'utm_source' AND m1.value='test_source1') AND
(m2.name = 'utm_medium' AND m2.value='test_medium1') AND
(m3.name = 'utm_term' AND m3.value='test_term1')
group by date
So we need to make as many joins as many conditions we have
I am trying to make a two SELECT statement using 2 CONCAT in 1 SELECT query.The query I have is:
SELECT a.id
, a.wo_number
, a.sheet
, a.serial
, a.machine_code machine_group
, c.name machine_name_ws
, a.crew_est
, a.manhour_est
, a.crew_est * a.manhour_est crew_kali_manhour
, CONCAT(
(SELECT machine_code FROM table_b WHERE wo_number = a.wo_number AND sheet_no = a.sheet AND step = a.serial)
) machine_code_actual
, d.name machine_name_actual
, CONCAT(
(SELECT SUM(act_hours) FROM table_b WHERE wo_number = a.wo_number AND sheet_no = a.sheet AND step = a.serial)
) act_hours
, b.status_man
FROM table_a a
LEFT
JOIN table_b b
ON b.wo_number = a.wo_number
LEFT
JOIN table_c c
ON c.machine_group = a.machine_code
LEFT
JOIN table_c_but_used_as_d d
ON d.code = b.machine_code
WHERE a.wo_number = 'I7519009'
GROUP
BY a.id
ORDER
BY a.sheet
, a.serial;
I'm getting #1242 - Subquery returns more than 1 row error, I don't know how to fix this. Can anyone help me with this?
As always,
Any help will be much appreciated,
Thank You.
I have the below query to find the row prior to MAX row. i feel like i am missing something, can somebody please help with it. I ammlooking forward to get the b.usercode_1 as row prior to a.usercode_1 not the min or any other random row but the ROW prior to the MAX.
Please suggest.
Select distinct
c.ssn
, c.controlled_group_Status CG_status
, c.last_name || ' , '|| c.first_name FULL_NAME
, a.usercode_1 Current_REG
, a.eff_date effective_since1
, b.usercode_1 PRIOR_REG
, b.eff_date effective_since2
, d.term_eff_date
from employee_eff_date c
, emp_cg_data a
, emp_cg_data b
, emp_ben_elects d
where c.control_id = 'XYZ'
and c.controlled_group_Status <> 'D'
and c.eff_date = (select max( c1.eff_date)
from emp_cg_data c1
where c.control_id = c1.control_id
and c.ssn = c1.ssn)
and a.control_id = c.control_id
and a.ssn = c.ssn
and a.eff_date = (select max(a1.eff_date )
from emp_cg_data a1
where a.control_id = a1.control_id
and a.ssn = a1.ssn)
and a.usercode_1 = 'REG26'
and b.control_id = c.control_id
and b.ssn = c.ssn
and b.eff_date = (select max( b1.eff_date)
from emp_cg_data b1
where b.control_id = b1.control_id
and b.ssn = b1.ssn
and b1.eff_date < a.eff_date)
and b.usercode_1 like 'REG%'
and d.control_id = c.control_id
and d.ssn = c.ssn
and d.life_event_date = (select max( d1.life_event_date)
from emp_ben_elects d1
where d.control_id = d1.control_id
and d.ssn = d1.ssn)
and d.le_seq_no= (select max( d1.le_seq_no)
from emp_ben_elects d1
where d.control_id = d1.control_id
and d.ssn = d1.ssn
and d.life_event_date = d1.life_event_date)
and d.term_eff_date is null
;
NOTE: this is not a complete answer... its a helpful suggestion of what you should start with.
you are doing a Cartesian Product of the four tables, filtered by a WHERE... so something like this
Implicit Join -- generally not a good practice as it can be very difficult to keep the where filters apart from the join conditions.
SELECT *
FROM tableA a, TableB b
WHERE b.id = a.id
another way to write a JOIN (the more generally accepted way)
SELECT *
FROM tableA a
JOIN tableB b ON b.id = a.id
Use the ON clause to join two tables together.
You should change your joins to this format so that others can read your query and understand it better.
suggestion to solve your problem
a fairly simple way to get the second to last row is to use a row counter.
so something like
SELECT *, #row_count := #row_count + 1
FROM tableA a
JOIN tableB b on b.id = a.id AND -- any other conditions for the join.
CROSS JOIN (SELECT #row_count := 0) t
then from here you can get the MAX row, whether thats the ID or something else. and then get the #row_num -1. aka the previous row.
I created a query to update a field. But I need to change the value of the last record. In my ps_order_history table there's the date_add column. Its type is datetime. How could I get the last record if I can't use the max() function in where clause? This is what I got until now (there's some fixed parameters, but it's just to test. I'll change this in the PHP code):
UPDATE ps_order_history AS h
INNER JOIN ps_order_detail d ON d.id_order = h.id_order
INNER JOIN ps_orders o ON o.id_order = h.id_order
SET h.id_order_state = 18
WHERE d.product_name = "Academia Mastermaq"
AND o.id_customer = (
SELECT id_customer
FROM ps_customer
WHERE firstname = "Cristiano"
AND lastname = "Ferreira dos Santos"
)
AND max(h.date_add)
Thanks.
you can do that by using Order by xxx DESC LIMIT 1
Got it! I've discovered about variables in MySQL and used a one to store the max id_order_history (I realized I didn't need the date_add column because the max id_order_history is the last data). After, I updated the table.
This is the query:
SET #maxid =
(
SELECT max(h.id_order_history)
FROM ps_order_history h
WHERE h.id_order =
(
SELECT max(p.id_order)
FROM ps_orders p
INNER JOIN ps_order_detail d ON d.id_order = p.id_order
WHERE p.id_customer =
(
SELECT id_customer
FROM ps_customer
WHERE firstname = '$firstname'
AND lastname = '$lastname'
LIMIT 1
)
AND d.product_name = '$nome_produto'
AND p.module = 'pagseguro'
LIMIT 1
)
);
UPDATE ps_order_history h
SET h.id_order_state = $status
WHERE h.id_order_history = #maxid;