MySQL update with two subqueries - mysql

I'm trying to update one column of MySQL table with subquery that returns a date, and another subquery for the WHERE clause.
Here is it:
UPDATE wtk_recur_subs_temp
SET wtk_recur_date = (SELECT final_bb.date
FROM final_bb, wtk_recur_subs
WHERE final_bb.msisdn = wtk_recur_subs.wtk_recur_msisdn)
WHERE wtk_recur_subs_temp.wtk_recur_msisdn IN (select final_bb.msisdn
from final_bb)
The response from the MySQL engine is "Subquery returns more than 1 row".

Use:
UPDATE wtk_recur_subs_temp,
final_bb,
wtk_recur_subs
SET wtk_recur_subs_temp.wtk_recur_date = final_bb.date
WHERE final_bb.msisdn = wtk_recur_subs.wtk_recur_msisdn
AND wtk_recur_subs_temp.wtk_recur_msisdn = final_bb.msisdn
The error is because:
SET wtk_recur_date = (SELECT final_bb.date
FROM final_bb, wtk_recur_subs
WHERE final_bb.msisdn = wtk_recur_subs.wtk_recur_msisdn)
...the final_bb.date value is all the date values where the final_bb and wtk_recur_subs msisdn column values match.

This may come as an utter shock to you, but one of your subqueries is returning more than one row!
This isn't permitted in the circumstance you've set up. Each of those two subqueries must return one and only one row. Or no rows.
Perform each subquery on it's own and determine which one is returning more than one row. If they shouldn't return more than one row, your data may be wrong. If they should return more than one row, you'll either want to modify the data so they don't (as I assume you expect), or add a LIMIT clause. Or add an aggregate function (like MAX) outside the query to do something proper with the multiple rows being returned.

Related

Optimize derived table in select

I have sql query:
SELECT tsc.Id
FROM TEST.Services tsc,
(
select * from DICT.Change sp
) spc
where tsc.serviceId = spc.service_id
and tsc.PlanId = if(spc.plan_id = -1, tsc.PlanId, spc.plan_id)
and tsc.startDate > GREATEST(spc.StartTime, spc.startDate)
group by tsc.Id;
This query is very, very slow.
Explain:
Can this be optimized? How to rewrite this subquery for another?
What is the point of this query? Why the CROSS JOIN operation? Why do we need to return multiple copies of id column from Services table? And what are we doing with the millions of rows being returned?
Absent a specification, an actual set of requirements for the resultset, we're just guessing at it.
To answer your questions:
Yes, the query could be "optimized" by rewriting it to the resultset that is actually required, and do it much more efficiently than the monstrously hideous SQL in the question.
Some suggestions: ditch the old-school comma syntax for the join operation, and use the JOIN keyword instead.
With no join predicates, it's a "cross" join. Every row matched from one side matched to every row from the right side.) I recommend including the CROSS keyword as an indication to future readers that the absence of an ON clause (or, join predicates in the WHERE clause) is intentional, and not an oversight.
I'd also avoid an inline view, unless there is a specific reason for one.
UPDATE
The query in the question is updated to include some predicates. Based on the updated query, I would write it like this:
SELECT tsc.id
FROM TEST.Services tsc
JOIN DICT.Change spc
ON tsc.serviceid = spc.service_id
AND tsc.startdate > spc.starttime
AND tsc.startdate > spc.starttdate
AND ( tsc.planid = spc.plan_id
OR ( tsc.planid IS NOT NULL AND spc.plan_id = -1 )
)
Ensure that the query is making use of suitable index by looking at the output of EXPLAIN to see the execution plan, in particular, which indexes are being used.
Some notes:
If there are multiple rows from spc that "match" a row from tsc, the query will return duplicate values of tsc.id. (It's not clear why or if we need to return duplicate values. IF we need to count the number of copies of each tsc,id, we could do that in the query, returning distinct values of tsc.id along with a count. If we don't need duplicates, we could return just a distinct list.
GREATEST function will return NULL if any of the arguments are null. If the condition we need is "a > GREATEST(b,c)", we can specify "a > b AND a > c".
Also, this condition:
tsc.PlanId = if(spc.plan_id = -1, tsc.PlanId, spc.plan_id)
can be re-written to return an equivalent result (I'm suspicious about the actual specification, and whether this original condition actually satisfies that adequately. Without example data and sample of expected output, we have to rely on the SQL as the specification, so we honor that in the rewrite.)
If we don't need to return duplicate values of tsc.id, assuming id is unique in TEST.Services, we could also write
SELECT tsc.id
FROM TEST.Services tsc
WHERE EXISTS
( SELECT 1
FROM DICT.Change spc
ON spc.service_id = tsc.serviceid
AND spc.starttime < tsc.startdate
AND spc.starttdate < tsc.startdate
AND ( ( spc.plan_id = tsc.planid )
OR ( spc.plan_id = -1 AND tsc.planid IS NOT NULL )
)
)

MS Access update query asks for parameter value - query depending on select query

The UPDATE query below interprets qry_Breakd_DYFYHAS_CountRecsDateRange.CountRecords_InDateRange to be a parameter and asks for a parameter value.
The qry_Breakd_DYFYHAS_CountRecsDateRange query works, and gives the number: 8.
Contents of Update query:
UPDATE tbl_total_listenings_calcs
SET tbl_total_listenings_calcs.Total_listenings_tbl_Data_DateRange = qry_Breakd_DYFYHAS_CountRecsDateRange.CountRecords_InDateRange;
Contents of "qry_Breakd_DYFYHAS_CountRecsDateRange" query:
SELECT count(*) AS CountRecords_InDateRange
FROM tbl_Data, tbl_DateFromTo
WHERE (((tbl_Data.Date_Listening) Between tbl_DateFromTo.Date_From And tbl_DateFromTo.Date_To));
Seeking advice and suggestions on what to change in the two queries as applicable to get the update query working.
That UPDATE references only tbl_total_listenings_calcs, and it is not joined to any other table or query. So when Access sees qry_Breakd_DYFYHAS_CountRecsDateRange.CountRecords_InDateRange, all it knows is that is not one of the query's data sources, so assumes it must be a parameter.
Use DLookup() to fetch the CountRecords_InDateRange value from the qry_Breakd_DYFYHAS_CountRecsDateRange query.
UPDATE tbl_total_listenings_calcs
SET Total_listenings_tbl_Data_DateRange =
DLookup
(
"CountRecords_InDateRange",
"qry_Breakd_DYFYHAS_CountRecsDateRange"
);
You could check whether it works with a subquery instead of DLookup() ...
UPDATE tbl_total_listenings_calcs
SET Total_listenings_tbl_Data_DateRange =
(
SELECT CountRecords_InDateRange
FROM qry_Breakd_DYFYHAS_CountRecsDateRange
);
However, sometimes Access treats an UPDATE with a subquery as "not updateable". I'm not certain what would happen in this case. But DLookup() will surely work.

Select column to update based on value

What I am trying to do is reduce the time needed to aggregate data by producing a roll-up table of sorts. When I insert a record, an after insert trigger is fired which will update the correct row. I would update all of the columns of the roll-up table if I need to, but since there are 25 columns in the table and each insert will only update 2 of them, I would rather be able to dynamically select the columns to update. My current update statement in the after insert trigger looks similar to this:
update peek_at_chu.organization_data_state_log odsl
inner join ( select
lookUpID as org_data_lookup,
i.interval_id,
peek_at_chu.Get_Time_Durration_In_Interval1('s', new.start_time, new.end_time, i.start_time, i.end_time) as time_in_int,
new.phone_state_id
from
(peek_at_chu.interval_info i
join peek_at_chu.interval_step int_s on i.interval_step_id = int_s.interval_step_id)) as usl on odsl.org_date_lookup_id = usl.org_data_lookup
and odsl.interval_id = usl.interval_id
set
total_seconds = total_seconds + usl.time_in_int,
case new.phone_state_id
when 2 then
available_seconds = available_seconds + time_in_int
end;
In this, lookUpID is a variable previously declared in the trigger. The field that will dictate which field of the roll-up table to update is new.phone_state_id. The phone_state_id's are not consistent, that is some numbers are skipped in this table, so an update based on column number is out the window unless I create a mapping.
The case option throws an error but I am hoping to use something similar to that instead of 25 if statements if I can.
You have to update all the columns, but use a conditional to determine whether to give it a new value or keep the old value:
set total_seconds = total_seconds + usl.time_in_int,
available_seconds = IF(new.phone_state_id = 2, available_seconds + time_in_int, available_seconds)
Repeat the pattern in the last line for all the other columns that need to be updated conditionally.

Update mysql cell after fetching related cell value via select?

SQL:
$mysqli->query("UPDATE results
SET result_value = '".$row[0]['logo_value']."'
WHERE logo_id = '".$mysqli->real_escape_string($_GET['logo_id'])."'
AND user_id = '".$user_data[0]['user_id']."'");
This results table also contains result_tries I'd like to fetch before doing update, so I can use it to modify result_value... Is there a way to do it in a single shot instead of first doing select and than doing update?
Is this possible?
Basically:
UPDATE results SET result_value = result_value + $row[0][logo_value]
for just a simple addition. You CAN use existing fields in the record being updated as part of the update, so if you don't want just addition, there's not too many limits on what logic you can use instead of just x = x + y.

How to set a column value equal to the value in another table?

I am trying to figure out how to update a row in one table, setting a column value equal to a value in a different table. Here's an example:
movies:
movie_id | movie_price
movies_attended:
attended_id | attended_movie_id | attended_movie_price
Now, this is kind of a stupid example, but supposed that for some reason there is a row in movies_attended that does not have the correct attended_movies_price in it and so it needs to be updated.
How should a query be written to update the movies_attended table, setting movies_attended.attended_movie_price = movies.movie_price?
I tried something similar to the following, but it did not work:
update movies_attended, movies
set movies_attended.attended_movie_price = movies.movie_price
where movies_attended.attended_movie_id = movies.movie_id
AND attended_id = [the id of the row we want to update]
When you say "it did not work", do you mean that it reported 0 rows updated, or did the statement cause the database raise an exception?
Your example statement appears to be of the form:
UPDATE movies_attended a
JOIN movies m
ON a.attended_movie_id = m.movie_id
SET a.attended_movie_price = m.movie_price
WHERE a.attended_id = ?
(We typically prefer the JOIN ... ON ... style syntax to the comma join operator and the join predicates in the WHERE clause.)
I have no explanation as to why this statement would "not work".
It's possible this would report 0 rows affected, if no rows satisfy the predicates. It would also report 0 rows affected if the rows that would be changed do not require any changes... that is, the existing value in attended_movie_price already matches the value being assigned to it.
Normally, before running an update statement like that, I write it as a SELECT first, and see what values are returned...
By replacing the UPDATE keyword with SELECT ... FROM, and removing the SET clause:
SELECT m.movie_price AS new_val
, a.attended_movie_price AS old_val
, a.attended_id
FROM UPDATE movies_attended a
JOIN movies m
ON a.attended_movie_id = m.movie_id
WHERE a.attended_id = ?
This is actually a bad database design. You don't need movie price in two tables.
But, if you just need this, it goes something along this:
UPDATE movies_attended
INNER JOIN
movies
ON movies_attended.attended_movie_id = movies.movie_id
SET movies_attended.attended_movie_price = movie.movie_price