Conditional UPDATE if new_value <= value_from_different_row - mysql

Is there an elegant solution to perform an UPDATE in MySQL if and only if the value to be assigned to column X in my row is greater or equal to the same column X's value in another (specific) row?
I'm using a table with 2 columns as a key => value store, and I have something like this:
key | value
---------------------------
period_a_begin | 2014-01-01
period_a_end | 2014-01-15
period_b_begin | 2014-01-20
period_b_end | 2014-02-15
How can I make sure that the new value for period_a_end is greater or equal to the value of period_a_begin value and less or equal to the value of period_b_begin in the same SQL query that I use to update period_a_end?
I've tried using subqueries, but without success:
UPDATE configtable
SET configtable.value=:myvalue
WHERE configtable.key="period_a_end"
AND :myvalue >= (SELECT configtable.value FROM configtable WHERE configtable.key = "period_a_begin")
AND :myvalue <= (SELECT configtable.value FROM configtable WHERE configtable.key = "period_b_begin");
I keep getting the following error:
You can't specify target table configtable for update in FROM clause
While I understand why that error pops up, I fail to find a way to find a way to make this work without removing the whole check from my prepared statement. :/
Any ideas?

This isn't so elegant, but it should work. You can use an additional level of nested queries:
UPDATE configtable
SET configtable.value = :myvalue
WHERE configtable.key="period_a_end" AND
:myvalue >= (select value from (SELECT ct.value FROM configtable ct WHERE ct.key = "period_a_begin") ct) AND
:myvalue <= (select value from (SELECT ct.value FROM configtable ct WHERE ct.key = "period_b_begin") ct);

In the end, I've chosen to implement my check in PHP using multiple queries, packing the whole check into a single SQL transaction. But beware that transaction to not work with the MyISAM storage engine, so you'll have to use InnoDB or some other engine that supports transactions.
Besides being faster then stored procedures, this solution also seems more portable (at least on the database side) to me.

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;

UPDATE order in PostgreSQL

I have a mysql command:
update table_demo SET flag= 1 where flag=0 ORDER BY id ASC LIMIT 10
and need the same command in Postgres, I get this error:
ERROR: syntax error at or near 'ORDER'
To update 10 first rows (that actually need the update):
UPDATE table_demo t
SET flag = 1
FROM (
SELECT table_demo_id -- use your actual PK column(s)
FROM table_demo
WHERE flag IS DISTINCT FROM 1
ORDER BY id
LIMIT 10
FOR UPDATE
) u
WHERE u.table_demo_id = t.table_demo_id;
FOR UPDATE (row level locks) are only needed to protect against concurrent write access. If your transaction is the only one writing to that table, you don't need it.
If flag is defined NOT NULL, you can use WHERE flag <> 0.
Related answers with more explanation and links:
Update top N values using PostgreSQL
How do I (or can I) SELECT DISTINCT on multiple columns?

Subqueries with EXISTS vs IN - MySQL

Below two queries are subqueries. Both are the same and both works fine for me. But the problem is Method 1 query takes about 10 secs to execute while Method 2 query takes under 1 sec.
I was able to convert method 1 query to method 2 but I don't understand what's happening in the query. I have been trying to figure it out myself. I would really like to learn what's the difference between below two queries and how does the performance gain happen ? what's the logic behind it ?
I'm new to these advance techniques. I hope someone will help me out here. Given that I read the docs which does not give me a clue.
Method 1 :
SELECT
*
FROM
tracker
WHERE
reservation_id IN (
SELECT
reservation_id
FROM
tracker
GROUP BY
reservation_id
HAVING
(
method = 1
AND type = 0
AND Count(*) > 1
)
OR (
method = 1
AND type = 1
AND Count(*) > 1
)
OR (
method = 2
AND type = 2
AND Count(*) > 0
)
OR (
method = 3
AND type = 0
AND Count(*) > 0
)
OR (
method = 3
AND type = 1
AND Count(*) > 1
)
OR (
method = 3
AND type = 3
AND Count(*) > 0
)
)
Method 2 :
SELECT
*
FROM
`tracker` t
WHERE
EXISTS (
SELECT
reservation_id
FROM
`tracker` t3
WHERE
t3.reservation_id = t.reservation_id
GROUP BY
reservation_id
HAVING
(
METHOD = 1
AND TYPE = 0
AND COUNT(*) > 1
)
OR
(
METHOD = 1
AND TYPE = 1
AND COUNT(*) > 1
)
OR
(
METHOD = 2
AND TYPE = 2
AND COUNT(*) > 0
)
OR
(
METHOD = 3
AND TYPE = 0
AND COUNT(*) > 0
)
OR
(
METHOD = 3
AND TYPE = 1
AND COUNT(*) > 1
)
OR
(
METHOD = 3
AND TYPE = 3
AND COUNT(*) > 0
)
)
An Explain Plan would have shown you why exactly you should use Exists. Usually the question comes Exists vs Count(*). Exists is faster. Why?
With regard to challenges present by NULL: when subquery returns Null, for IN the entire query becomes Null. So you need to handle that as well. But using Exist, it's merely a false. Much easier to cope. Simply IN can't compare anything with Null but Exists can.
e.g. Exists (Select * from yourtable where bla = 'blabla'); you get true/false the moment one hit is found/matched.
In this case IN sort of takes the position of the Count(*) to select ALL matching rows based on the WHERE because it's comparing all values.
But don't forget this either:
EXISTS executes at high speed against IN : when the subquery results is very large.
IN gets ahead of EXISTS : when the subquery results is very small.
Reference to for more details:
subquery using IN.
IN - subquery optimization
Join vs. sub-query.
Method 2 is fast because it is using EXISTS operator, where I MySQL do not load any results.
As mentioned in your docs link as well, that it omits whatever is there in SELECT clause. It only checks for the first value that matches the criteria, once found it sets the condition TRUE and moves for further processing.
On the other side Method 1 has IN operator which loads all possible values and then matches it. Condition is set TRUE only when exact match is found which is time consuming process.
Hence your method 2 is fast.
Hope it helps...
The EXISTS operator is a Boolean operator that returns either true or false. The EXISTS operator is often used the in a subquery to test for an “exist” condition.
SELECT
select_list
FROM
a_table
WHERE
[NOT] EXISTS(subquery);
If the subquery returns any row, the EXISTS operator returns true, otherwise, it returns false.
In addition, the EXISTS operator terminates further processing immediately once it finds a matching row. Because of this characteristic, you can use the EXISTS operator to improve the performance of the query in some cases.
The NOT operator negates the EXISTS operator. In other words, the NOT EXISTS returns true if the subquery returns no row, otherwise it returns false.
You can use SELECT *, SELECT column, SELECT a_constant, or anything in the subquery. The results are the same because MySQL ignores the select_list that appears in the SELECT clause.
The reason is that the EXISTS operator works based on the “at least found” principle. It returns true and stops scanning table once at least one matching row found.
On the other hands, when the IN operator is combined with a subquery, MySQL must process the subquery first and then uses the result of the subquery to process the whole query.
The general rule of thumb is that if the subquery contains a large volume of data, the EXISTS operator provides a better performance.
However, the query that uses the IN operator will perform faster if the result set returned from the subquery is very small.
For detail explanations and examples: MySQL EXISTS - mysqltutorial.org
The second Method is faster because you've got this like there "WHERE t3.reservation_id = t.reservation_id". In the first case your subquery has to do a full scan into the table to verify the information. However at the 2o Method the subquery knows exactly what it is looking for and once it is found is checked the having condition then.
Their Official Documentation.SubQuery Optimization with Exists

Update multiple rows with multiple values and multiple conditions mysql

I am facing a complex situation of SQL queries. The task is to update multiple rows, with multiple values and multiple conditions. Following is the data which I want to update;
Field to update: 'sales', condition fields: 'campid' and 'date':
if campid = 259 and date = 22/6/2011 then set sales = $200
else if campid = 259 and date = 21/6/2011 then set sales = $210
else if campid = 260 and date = 22/6/2011 then set sales = $140
else if campid = 260 and date = 21/6/2011 then set sales = $150
I want to update all these in one query.
Try this:
UPDATE your_table SET sales =
CASE
WHEN campid = 259 AND date = 22/6/2011 THEN 200
WHEN campid = 259 AND date = 21/6/2011 THEN 210
WHEN campid = 259 AND date = 22/6/2011 THEN 140
WHEN campid = 259 AND date = 21/6/2011 THEN 150
ELSE sales
END
Naturally I don't know if date field is really DATE or DATETIME, so I left query showing what you can do, but maybe you have to fix dates comparison according to data type.
If date field is DATE (as it should) you can write AND date = '2011-06-22' and so on.
Note ELSE condition: it's necessary to avoid records not falling inside other cases will be set to NULL.
Rather than write a sql query that is far too complicated and time involved, I believe you would be better off spending your time writing a data access object to handle these rather simple manipulations on a per record basis. This makes later maintenance of the code, along with development of new code using your data access objects far easier than a one time use, intricate sql query.
You certainly should not do these in a single query. Instead, if what you aim for is to update them atomically, all at the same time, you should issue several UPDATE statements in a single transaction.
You do not say which MySQL version you use, and not which storage engine. Assuming InnoDB - which is the standard in recent versions of MySQL and should generally be used for transactional systems - and also assuming you are doing this from the command line client, you would
mysql> set autocommit=0;
mysql> UPDATE ....;
mysql> UPDATE ....;
mysql> ...
mysql> commit;
You can then reenable autocommit if you like by repeating the first line, but with a value of 1:
mysql> set autocommit=1;

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.