MySQL: How to do a conditional update? - mysql

I'm trying to a create an update statement along the following lines:
TABLE car: id | owner_id | type | status
An owner can have multiple cars.
UPDATE car c
SET c.type = 1
WHERE c.owner_id IN ($ids)
AND c.status = [1 IF THIS OWNER HAS A CAR WITH 1, ELSE A CAR WITH 0] // ... ???
$ids is reasonably small (under 50 values).
It seems simple but I can't seem to get my head around it because I can't use a SELECT subquery with an UPDATE to the same table.
Anyone?
Thanks

You say you have tried a query that fails but unfortunately you have not posted it, so I'm going to guess that it is this:
UPDATE car c
SET c.type = 1
WHERE c.owner_id IN ($ids)
AND c.status = (SELECT MAX(c2.status) FROM car c2 WHERE c.owner_id = c2.owner_id)
Posting your actual query would be helpful, but for now I'm just going to assume that this is what you tried. If you have something slightly different, it doesn't matter... the principle is the same. It fails as you as you correctly pointed out because you cannot update and select from the same table in one query. One way to workaround this error is to wrap the select in another select:
UPDATE car c
SET c.type = 1
WHERE c.owner_id IN ($ids)
AND c.status = (SELECT * FROM
(SELECT MAX(c2.status) FROM car c2 WHERE c.owner_id = c2.owner_id)
AS T1)
Surprisingly, this will work even though it seems that it should be equivalent to the first query. Note though that the reason why the original query doesn't work is because it doesn't correctly lock the table. This workaround tricks MySQL into allowing the query anyway, but you should be aware that it may be unsafe to use in a multi-user environment.

Related

SQL - How to combine UPDATE with JOIN when needing the order of items [duplicate]

I have a table in MariaDB with different nodes, each node can have multiple hardware components, and I want to count how many hardware components each node has and store it in a column.
I have therefore created an empty column called "HW_Count". I can do this calculation through the following SELECT statement.
SELECT NodeID,COUNT(NodeId) OVER (PARTITION BY NodeId) FROM Hardware AS HW_Count
This returns a list of the following kind
NodeID HWCount
1 33
1 33
... ...
2 11
2 11
... ...
Now I want to UPDATE the empty column in the table called Hardware with tihs information, but I am not sure how I can write the correct UPDATE statement. I want to do something along the lines
UPDATE Hardware
SET HW_count =
COUNT(NodeId) OVER (PARTITION BY NodeId)
But it returns
"SQL ERROR (4015): Window function is allowed only in SELECT list and ORDER BY clause".
What is the correct way to update my column?
Thanks!
I managed to find the answer
UPDATE Hardware, (SELECT NodeID,COUNT(NodeID) AS `HW_Count` FROM
Hardware GROUP BY NodeID) AS dummyTable
SET Hardware.hw_count = Dummytable.Hw_count
WHERE Hardware.NodeID= dummytable.NodeID
Try this please:
UPDATE H1
SET HW_count = (SELECT COUNT(*) HW_count FROM Hardware WHERE NodeID = H1.NodeID GROUP BY NodeID)
FROM Hardware H1
INNER JOIN Hardware H2 ON H1.NodeID=H2.NodeID
I would recommend doing this using JOIN and GROUP BY:
UPDATE Hardware h JOIN
(SELECT NodeID, COUNT(NodeID) AS new_hw_count
FROM Hardware
GROUP BY NodeID
) hh
ON hh.NodeID = h.NodeID
SET h.hw_count = h.new_hw_count;

mySQL update a value

Modified some stuff from my pic so you guys can understand it
I have this database. I am trying to update a value from a table based on another value from an another table.
I want to update the SUM from salary like this :
( sum = presence * 5 )
This is what I've been trying to use ( unsuccessful )
update table salary
set suma.salary = users.presence * 5
FROM salary INNER JOIN users1 INNER JOIN presence on id_salary = id_presence
I am not sure what to do, I'd appreciate some help, Thanks
In MySQL to UPDATE tables with a join you use this syntax:
UPDATE table1, table2
SET table1.column = some expression
WHERE table1.column = table2.column
That said, even with the updated picture, in your SQL you are mentioning columns that I cannot understand in which table are to be found. You also have an inner join between salariu and users1, with no join condition. Could you please clean up the question and make everything clear?
Assuming you are making the updates to the db structure you were talking about, then you can start working on this one maybe:
UPDATE salary, presence
SET salary.sum = SUM(presence.hours) * 5
WHERE presence.id = salary.id
AND <some filter on the month that depends on salary.date>
Another way, but I'm not sure it is supported in all RDBMS, would be something like this:
UPDATE salary
SET sum = (
SELECT SUM(presence.hours) * 5
FROM user, presence
WHERE presence.id = salary.id
AND <some filter on the month that depends on salary.date>
)

MySql update two tables at once

I have two tables that need the exact same values for denormalization purposes.
Here's the query.
first table
UPDATE Table_One
SET win = win+1, streak = streak+1, score = score+200
WHERE userid = 1 AND lid = 1 LIMIT 1
second table
UPDATE Table_Two
SET win = win+1, streak = streak+1, score = score+200
WHERE userid = 1 LIMIT 1
As you can see the only difference between both tables is their name and table two doesn't have the field lid
Anyway to combine both updates to just one?
It should be possible with a multi-table update, as described in the documentation.
http://dev.mysql.com/doc/refman/5.5/en/update.html
UPDATE Table_One a INNER JOIN Table_Two b ON (a.userid = b.userid)
SET
a.win = a.win+1, a.streak = a.streak+1, a.score = a.score+200,
b.win = b.win+1, b.streak = b.streak+1, b.score = b.score+200
WHERE a.userid = 1 AND a.lid = 1 AND b.userid = 1
Note: Multi-table doesn't support LIMIT, so this could cause more grief depending on the details.
Stored procedures or transactions may be a nicer solution.
If there is a one to one or one to many relation from Table_One to Table_Two, this would work:
UPDATE Table_One T1, Table_Two T2
SET T1.win = T1.win+1, T1.streak = T1.streak+1, T1.score = T1.score+200,
T2.win = T2.win+1, T2.streak = T2.streak+1, T2.score = T2.score+200
WHERE T1.userid = 1 AND T1.lid = 1 AND T2.userid = T1.userid;
If you can join the tables, then you could create a view of two tables, then update via that view. In your example it looks like userid might be a suitable key.
In creating the view, you'd need to stick to the following guidelines.
They’re two separate queries and so must be treated as such. Sorry to say it, but if you’re updating two tables with identical data, there’s probably a better way to design your database. Remember to keep your programming DRY.
Edit: Should retract that; you can use it for multiple tables, but you can’t use ORDER BY or LIMIT.

Right way to do this query

I have a table
form (
int id )
webformsystemflags ( int id, int formid int sysflagid )
sysflag ( int id, name char(10) )
form table is a table which has all the forms
webform is a table which has the forms which have flags applied to it. It has a foreign key formid which is id to the form table and sysflagid which is foreign key to the sys flag table
sys flag is the table which contains the flags. Lets say I have flags defined as 1,2,3
I can have forms which don't have all the flags applied to it, some may have 1, some may have 2 or some may have 3 applied to it or some may have none.
How can I find all the forms which have either flag 1 or flag 2 or flag 3 applied to it ?
This is a common trick to find EXCLUSION. The value I have below of "FlagYouAreExpectingTo_NOT_Exist" is explicitly the one you expect NOT to be there. Here's how it works.
Get every form and LEFT JOIN to the Web System Flags table WITH FINDING the matching form, and flag setting you DO NOT want. If it finds a valid entry for the form and flag, the "formid" in the (wsf) table will exist. So, we want all that DON'T exist, hence the closing WHERE wsf.formid is null.
It will be NULL for those where it is NOT already flagged.
select
f.ID
from
forms f
left join webformsystemflags wsf
on f.id = wsf.formid
AND wsf.sysflagid = FlagYouAreExpectingTo__NOT__Exist
where
wsf.formid is null
You could use a subquery:
SELECT * FROM `form` WHERE `id` IN (SELECT `formid` FROM `webformsystemflags`)
Careful with subqueries on huge databases though. You could do the same thing with joins but this is an easy solution that will get you going.
Or for all results that DO NOT have a certain flag:
SELECT * FROM `form` WHERE `id` IN (SELECT `formid` FROM `webformsystemflags` WHERE `sysflagid` != 1 OR `sysflagid` != 2)
or a join method:
SELECT f.*, r.`sysflagid` FROM `form` f LEFT JOIN `webformsystemflags` r ON r.`formid` = f.`id` WHERE r.`sysflagid` != null
will get you the forms and the related flags. However, it will not get ALL flags in one row if the form has multiple flags on it. That one you may need to do a concat on the flags, but this answer is already growing unnecessarily complex.
*LAST EDIT *
Ok nutsandbolts - You need to update your question cause the two of us have overshot ourselves in a number of different queries and it isn't really helping to come back saying it doesnt give the right results. The right results can easily be reached by simply examining the queries we have provided and using the general logic behind them to compose the query that is right for you.
So my last suggestion - you say you want a query that will return a form IF it has a certain flag applied to it AND that is does NOT have other flags applied to it.
Here it is supposing you wanted all forms with a flag of 1 AND NOT 2 or 3 or none:
SELECT f.*, r.`sysflagid` FROM `form` f LEFT JOIN `webformsystemflags` r ON r.`formid` = f.`id` WHERE r.`sysflagid` =1 AND r.`formid` NOT IN (SELECT `formid` FROM `webformsystemflags` WHERE `sysflagid` = 2 OR `sysflagid` = 3)
Because your webformsystemflags is relational this query will NOT return any forms that do not exist in the webformsystemflags table - so you don't need to consider null.
If this is not what you're looking for I strongly suggest you rewrite your question with absolute and perfect clarity on your needs cause after this one I'm out of this conversation. Much luck to you though. Have fun.
You can use an exists clause to pull records like this:
select a.*
from form a
where exists (select 1
from webformsystemflags
where formid = a.id
and sysflagid IN (1,2,3))
This won't give you the associated flag. If you want that:
select a.*, b.sysflagid
from form a
join (select formid, sysflagid
from webformsystemflags
where sysflagid in (1,2,3)) b
on a.id = b.formid
There are many different ways to solve this.
EDIT: By reading a comment on the other answer it seems the question was unclear. You want the result forms that only have ONE flag? i.e. the form has flag 1 but not 2 or 3?
edit2: if you really just want a true/false query pulling only the true (has a flag):
select a.*, b.sysflagid
from form a
join webformsystemflags b on a.id = b.formid
If you want forms without flags:
select a.*
from form a
left join webformsystemflags b on a.id = b.formid
where b.formid is null
edit3: Based on comment, forms with one flag and not one of the others:
select a.*
from form a
where exists (select 1 from webformsystemflags where formid = a.id and sysflagid = 1)
and (
not exists (select 1 from webformsystemflags where formid = a.id and sysflagid = 2)
or
not exists (select 1 from webformsystemflags where formid = a.id and sysflagid = 3)
)

Deleting with Max

This is based on my previous question.
I have the following table
Table1
JobPositionId | JobPositionName
1 | Sound
2 | Lights
3 | Sound
4 | Ground
How can I delete row three (Name = sound, and max position)
Use:
DELETE FROM TABLE t1
JOIN (SELECT x.jobpositionname,
MAX(jobPositonId) AS max_id
FROM TABLE x
GROUP BY x.jobpositionname) t2
WHERE t1.jobPositonId = t2.max_id
AND t1.jobpositionname = t2.jobpositionname
AND t2.jobpositionname = 'Sound'
As I mentioned in your previous question, using this won't trigger a MySQL 1093 error:
DELETE FROM TABLE
WHERE JobPositionId = SELECT x.id
FROM (SELECT MAX(JobPositionId) AS id
FROM TABLE
WHERE JobPositionName = 'Sound') x
DELETE FROM
Table1
WHERE
JobPositionId = (
SELECT
MAX(JobPositionId)
FROM
Table1
WHERE
JobPositionName = 'Sound'
)
Sorry if this doesn't take into account your "previous question" thought I'd just look at this one.
DELETE FROM Table1 WHERE jobpositionid = (SELECT MAX(jobpositionid) FROM table1 WHERE name = 'Sound');
It seems like what you are trying to do is to delete all duplicate JobPositionNames, leaving only the one with the lowest JobPositionId.
I had to do something very similar recently and found the SQL statements getting so complicated, it was much much easier (if much less efficient) to do it in SQL.
So in case this is a poluted database you're trying to clean, just write a script that does this and be done with it (and set some unique indexes to prevent it from happening again).
If this happens all the time, and needs to be done periodicaly, fix the code that does this.