MySQL UPDATE -- ignore records not in CASE statement - mysql

How do you ignore records not inside the case statement when using CASE/WHEN/THEN?
For example, this statement will update three matching records as expected but will make all student records that do not match a WHEN/THEN clause to NULL
UPDATE table SET student = (CASE WHEN student = '10' THEN '100412'
WHEN student = '17' THEN '100295'
WHEN student = '26' THEN '100981'
END)
WHERE year = '2019';
How can you skip over records not inside the CASE statement and only change records that have a matching clause?

For skipping records not in case statement, you can use something like this
UPDATE table SET student = (CASE WHEN student = '10' THEN '100412'
WHEN student = '17' THEN '100295'
WHEN student = '26' THEN '100981'
END)
WHERE year = '2019' AND student IN ('10','17','26');

There a two solutions I think :
Add a where clause
UPDATE table SET student = (CASE WHEN student = '10' THEN '100412'
WHEN student = '17' THEN '100295'
WHEN student = '26' THEN '100981'
END)
WHERE year = '2019' AND student IN ('10','17','26');
Use else statement but that will scan the whole table for nothing :
UPDATE table SET student = (CASE WHEN student = '10' THEN '100412'
WHEN student = '17' THEN '100295'
WHEN student = '26' THEN '100981'
ELSE student
END)
WHERE year = '2019';

You could use a default case for that:
UPDATE table SET student = (CASE WHEN student = '10' THEN '100412'
WHEN student = '17' THEN '100295'
WHEN student = '26' THEN '100981'
ELSE student
END)
WHERE year = '2019';
Otherwise, if you want to minimize the load, just add all known student values to the WHERE clause. This triggers an update only on those rows that are really affected by a change

You can use in or = operator
WHERE year = '2019' AND (student = '10' or student = '17' or student ='26');

Related

Mysql multiple SELECT and Count query

Can you advice in whats the best way to combine multiple select and count into the one query
For example here are some queries that are from a test_table and work perfect on their own
SELECT name, count(*) AS 'Overall' FROM test_test WHERE country_prefix = '44' AND area_code = '203' GROUP BY `city_name`;
SELECT name, count(*) AS 'unallocated' FROM test_tab le WHERE country_prefix = '44' AND area_code = '203' AND removed != '1' AND destination-value = '1234' GROUP BY `city_name`;
There's a few more queries i'll be running but I think if I get two working together ill be able to do the rest
I was thinking something like this:
SELECT name, (SELECT count(*) FROM test_table WHERE country_prefix = '44' AND area_code = '203' GROUP BY `city_name') AS "Overall", (SELECT count(*) AS 'unallocated' FROM test_table WHERE country_prefix = '44' AND city_code = '203' AND removed != '1' AND destination-value = '1234' GROUP BY 'city_name')
But unfortunately doesn't work
Would the best way to do this be using sum case when, something like this:
sum(case when destination-value = '1234' then 1 else 0 end) AS unallocated
You can do it with conditional aggregation:
SELECT city_name,
COUNT(CASE WHEN country_prefix = '44' AND area_code = '203' THEN 1 END) AS Overall,
COUNT(CASE WHEN country_prefix = '44' AND area_code = '203' AND removed != '1' AND destination-value = '1234' THEN 1 END) AS unallocated
FROM tablename
GROUP BY city_name;
or since the 2 cases contain common conditions, these conditions can be moved to a WHERE clause:
SELECT city_name,
COUNT(*) AS Overall,
COUNT(CASE WHEN removed != '1' AND destination-value = '1234' THEN 1 END) AS unallocated
FROM tablename
WHERE country_prefix = '44' AND area_code = '203'
GROUP BY city_name;
In Mysql it's possible to simplify the code with the function SUM() instead of COUNT():
SELECT city_name,
COUNT(*) AS Overall,
SUM(removed != '1' AND destination-value = '1234') AS unallocated
FROM tablename
WHERE country_prefix = '44' AND area_code = '203'
GROUP BY city_name;

selecting multiple fields using in where clause

I have a MySQL query and this is used to bring up certain events. The event_type and etype will change depending on what someone chooses in the form.
Below is what is added to the query based on a form on the webpage.
and event_type = '54' and etype = 'SP'
the full query is
select tevent.event_name, tevent.event_type, min(e_dates.event_date) as eventdate,
tevent.status, tevent.etype
from (tevent LEFT JOIN event_dates on tevent.eventid=event_dates.eventid)
Where status <> 'delete'
AND YEAR(e_dates.event_date) >= YEAR( CURDATE( ) ) and event_type = '54' and etype = 'SP')
group by tevent.eventid
order by (case when tevent.status = 'closed' and e_dates.event_date >= curdate() then 0 else 1 end),
(case when find_in_set(`status`, 'open,pending,approved') then 0 else 1 end),
e_dates.event_date asc, tevent.eventid ASC
This works perfectly for what I need. I shows all the events that are a certain event types and event category.
However, I want ALL queries to include the following statement
((event_type = '54' and etype = 'SM') or (event_type = '50' and
event_prelim = '2'))
the above statement will add the seminars to all event calendars, but will also show each particular event type based on what the person chooses.
I suppose your WHERE clause could look like this
WHERE status <> 'delete'
AND YEAR(e_dates.event_date) >= YEAR(CURDATE())
AND (
event_type NOT IN ('50','54')
OR event_type IS NULL
OR (event_type = '54' AND etype IN ('SP','SM'))
OR (event_type = '50' AND event_prelim = '2')
)
AND's are evaluated before the OR's.
So when using both AND's and OR's in the criteria, putting the parentheses does matter.

How to order table results by another rows value

I have created a query to select the record column in a table where other rows exist but i want to add an order by which orders by another rows value.
SELECT
record
FROM
record_attributes
GROUP BY
record
HAVING
sum(type = 'lead' AND option_name = 'pupil_cap' AND value > 0) > 0 AND
sum(type = 'lead' AND option_name = 'car_type' AND value = 'Manual') > 0 AND
sum(type = 'lead' AND option_name = 'instructor_areas_covered' AND value = '26') > 0 AND
sum(type = 'lead' AND option_name = 'diary_updates' AND value = '1') > 0
How can I order these results by another query in the same table with the following data:
ORDER BY record_attributes.value WHERE record_attributes.type = 'lead' AND record_attributes.option_name = 'instructor_rank'
And if the row doesn't exist, then use this:
ORDER BY record_attributes.value WHERE record_attributes.type = 'lead' AND record_attributes.option_name = 'instructor_start_date'
Not sure I understood correctly.
But something like this?
...
ORDER BY
MAX(CASE WHEN type = 'lead' AND option_name = 'instructor_rank' THEN value END),
MAX(CASE WHEN type = 'lead' AND option_name = 'instructor_start_date' THEN value END)
You can test it here on rextester.

My two queries work separately but when I nest one inside of the other it no longer works

My first query is:
SELECT
SUM(CASE WHEN (Transactions.RegFunction = '1' AND Transactions.RegYear = "2017") THEN RegAmt END) AS GroupCurrsumFee,
SUM(CASE WHEN (Transactions.RegFunction = '1' AND Transactions.RegYear = "2017") THEN Transactions.LMSCAmt END) AS IndCurrsumFee
FROM AllTransactions
My second query is:
SELECT GroupAmt
FROM GroupFees
WHERE
'2016-11-01' BETWEEN BeginDate AND EndDate
AND RegYear = "2016"
AND GROUPID = "14"
AND RegFunction = 1;
When I run that query it returns the below:
| GroupAmt |
| 5.00 |
When I nest the second query inside of the first so that it can return that data in a column alias it does not show up. I have the two queries combined and written as the below:
SELECT
SUM(CASE WHEN (Transactions.RegFunction = '1' AND Transactions.RegYear = "2017") THEN RegAmt END) AS GroupCurrsumFee,
SUM(CASE WHEN (Transactions.RegFunction = '1' AND Transactions.RegYear = "2017") THEN Transactions.LMSCAmt END) AS IndCurrsumFee,
(SELECT GroupAmt FROM GroupFees
WHERE
'2016-11-01' BETWEEN BeginDate AND EndDate
AND RegYear = "2016"
AND GROUPID = "14"
AND RegFunction = 1) AS GroupFee
FROM AllTransactions
Use a join instead. Given the possibility that query might return NULL I suggest a left join with join condition that is always true. (Yes a bit "hacky".) Note, there is no guarantee that subquery returns just one row. If it does your overall result may not be what you are expecting.
SELECT SUM(CASE
WHEN (
Transactions.RegFunction = '1'
AND Transactions.RegYear = "2017"
)
THEN RegAmt
END) AS GroupCurrsumFee
, SUM(CASE
WHEN (
Transactions.RegFunction = '1'
AND Transactions.RegYear = "2017"
)
THEN Transactions.LMSCAmt
END) AS IndCurrsumFee
, GroupFee.GroupAmt
FROM AllTransactions
LEFT JOIN (
SELECT GroupAmt
FROM GroupFees
WHERE '2016-11-01' BETWEEN BeginDate
AND EndDate
AND RegYear = '2016'
AND GROUPID = '14'
AND RegFunction = 1
) AS GroupFee on 1=1

Update two tables at once, using values from first table

So I need to Update table scores and use the updated value of column won to update the second table tbl_users. So far the code updates scores, but uses the old value of won for the second table update:
UPDATE scores a
left join tbl_users b on
a.uid = b.userID
SET a.won = CASE
WHEN a.nright = '0' THEN '0'
WHEN a.nright = '1' THEN '25'
WHEN a.nright = '2' THEN '50'
WHEN a.nright = '3' THEN '100'
WHEN a.nright = '4' THEN '200'
WHEN a.nright = '5' THEN '400'
WHEN a.nright = '6' THEN '700'
WHEN a.nright = '7' THEN '1000'
END,
b.pts=b.pts+a.won,
b.pts_total=b.pts_total+a.won
WHERE a.uid=$user AND b.userID=$user
What you want to do is explicitly documented as correct:
The second assignment in the following statement sets col2 to the
current (updated) col1 value, not the original col1 value. The result
is that col1 and col2 have the same value. This behavior differs from
standard SQL.
UPDATE t1 SET col1 = col1 + 1, col2 = col1;
I assume that the issue is the multi-table update, where the set pulls the value from the earlier table.
You may be able to fix this using variables. I am not 100% sure, but the following is worth a try:
UPDATE scores s JOIN
tbl_users u
ON s.uid = .uuserID
SET s.won = (#w := (CASE WHEN s.nright = '0' THEN '0'
WHEN s.nright = '1' THEN '25'
WHEN s.nright = '2' THEN '50'
WHEN s.nright = '3' THEN '100'
WHEN s.nright = '4' THEN '200'
WHEN s.nright = '5' THEN '400'
WHEN s.nright = '6' THEN '700'
WHEN s.nright = '7' THEN '1000'
END)
),
u.pts = u.pts + #w,
u.pts_total = u.pts_total + #w
WHERE s.uid = $user ;
The documentation strongly suggests that the set clauses are processed in order for a single table. Alas, it is not clear whether this is always true for multiple tables.
If not, you can use two updates.