UPDATE order in PostgreSQL - mysql

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?

Related

MySQL update query where in subquery slow

I'm having issues with MySQL query running extremely slow. It takes about 2 min for each UPDATE to process.
This is the query:
UPDATE msn
SET is_disable = 1
WHERE mid IN
(
SELECT mid from link
WHERE rid = ${param.rid}
);
So my question is, I would like to know how the performance of the UPDATE statement will be affected if the result of the subquery is 0 or NULL. Because I think that maybe the process is slow because the result of the subquery is 0 or NULL.
Thanks a lot in advance.
The issue here is that the subquery following IN has to execute, whether or not it returns any records. I would probably express your update using exists logic:
UPDATE msn m
SET is_disable = 1
WHERE EXISTS (SELECT 1 FROM link l WHERE m.mid = l.mid AND l.rid = ${param.rid});
Then, add the following index to the link table:
CREATE INDEX idx ON link (mid, rid);
You could also try and compare against this version of the index:
CREATE INDEX idx ON link (rid, mid);

MySQL query giving error You can't specify target table

I have this query.
UPDATE messages
SET seen_by = CONCAT( seen_by, '3,')
WHERE id IN (SELECT id FROM messages WHERE NOT FIND_IN_SET(3, seen_by) AND chat_id = 350)
But when I try to execute it, it gives me this error
You can't specify target table 'messages' for update in FROM clause
I want to update the same table what I have used in my WHERE clause. How can I do that?
You don't even need a subquery.
Just replace WHERE id IN (SELECT id FROM messages WHERE xyz) by WHERE xyz.
This is more straight forward and doesn't have to first load all IDs into RAM (if it would even have allowed you).

Using LIMIT on MySql deletion across a two column duplicate

I have a large MySql table from which I need to delete duplicates - to qualify as a duplicate, a row much match another row on two columns:
SELECT * FROM JwDistanceSurnames n1, JwDistanceSurnames n2
WHERE n1.JwDistanceSurnameId > n2.JwDistanceSurnameId
AND n1.Surname1 = n2.Surname1
AND n1.Surname2 = n2.Surname2
LIMIT 1000;
Because it is a large table, I'd like to do it in batches. My understanding is that I ought to be able to use LIMIT to achieve this. However, this does not execute, citing a syntax error:
DELETE n1 FROM JwDistanceSurnames n1, JwDistanceSurnames n2
WHERE n1.JwDistanceSurnameId > n2.JwDistanceSurnameId
AND n1.Surname1 = n2.Surname1
AND n1.Surname2 = n2.Surname2
LIMIT 1000;
What's the error? Is it not possible to use this simple approach to batching here?
MCVE:
CREATE TABLE `JwDistanceSurnames` (
`JwDistanceSurnameId` int(11) NOT NULL AUTO_INCREMENT,
`Surname1` varchar(999) DEFAULT NULL,
`Surname2` varchar(999) DEFAULT NULL,
`JwScore` double NOT NULL,
PRIMARY KEY (`JwDistanceSurnameId`),
KEY `Surname1` (`Surname1`),
KEY `Surname2` (`Surname2`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
INSERT INTO `JwDistanceSurnames`
(`JwDistanceSurnameId`, `Surname1`, `Surname2`, `JwScore`)
VALUES (null,'williamsom' ,'williamson' ,0.959999999999998);
Repeat the insert a few times. Then run the delete. The expected output is a single row, with the given values. Which of the rows kept is not important.
The error is:
Error Code: 1064. You have an error in your SQL syntax; check the
manual that corresponds to your MySQL server version for the right
syntax to use near 'ORDER BY n1.JwDistanceSurnameId LIMIT 1000' at
line 5
From this SO question, it appears that LIMIT cannot be used in a DELETE statement when more than one table is being referenced. One trick around this is to use LIMIT in a subquery to identify records for deletion, and then join back to the target table:
DELETE t1
FROM JwDistanceSurnames t1
INNER JOIN
(
SELECT n1.JwDistanceSurnameId
FROM JwDistanceSurnames n1
INNER JOIN JwDistanceSurnames n2
ON n1.JwDistanceSurnameId > n2.JwDistanceSurnameId
WHERE n1.Surname1 = n2.Surname1 AND n1.Surname2 = n2.Surname2
ORDER BY <some_column> -- IMPORTANT! without this you may get random records
LIMIT 1000
) t2
ON t1.JwDistanceSurnameId = t2.JwDistanceSurnameId;
So the subquery labelled t2 uses LIMIT to identify batches of 1000 records at a time for deletion, and then we use another join to actually label those target records.
Also note that using LIMIT without ORDER BY is not really a well-defined thing, because SQL tables are modelled on unordered sets of records. If you have some business logic determining which order the batches should be deleted, then consider adding an ORDER BY clause (unless it truly does not matter, which would seem unlikely to me).
I think you can use another way for find of dublicates
SELECT n.*
FROM JwDistanceSurnames n
JOIN
(
SELECT Surname1,Surname2,MIN(JwDistanceSurnameId) min_JwDistanceSurnameId
FROM JwDistanceSurnames
GROUP BY Surname1,Surname2
) l
ON n.Surname1=n.Surname1 AND n.Surname2=n.Surname2 AND n.JwDistanceSurnameId>l.min_JwDistanceSurnameId

Update first row in SQL using LIMIT

I'm having a trouble with something that looks like simple thing. I'm trying to find first row that satisfies WHERE part of query and UPDATE it.
UPDATE Donation SET Available=0 WHERE Available != 0 and BloodGroup='" + bloodGroup + "' LIMIT 1"
bloodGroup is variable that gets filled automatically using C# and it keeps string value of selected blood group.
When I try to run this I get incorrect syntax near 'limit'.
What I'm doing wrong? Is it possible using LIMIT like during UPDATE query?
During debugging I got query like this:
UPDATE Donation SET Available=0 WHERE Available != 0 AND BloodGroup='AB-' LIMIT 1
Because C# is often used with SQL Server, perhaps the question is mistagged. The syntax looks fine for MySQL.
In SQL Server, you can do this as:
UPDATE TOP (1) Donation
SET Available = 0
WHERE Available <> 0 AND BloodGroup = 'AB-';
Note that this chooses an arbitrary matching row, as does your original query (there is no order by).
It is not safe to use limit in update queries.
Please refer
http://bugs.mysql.com/bug.php?id=42415
The documentation states that any UPDATE statement with LIMIT clause is considered unsafe since the order of the rows affected is not defined: http://dev.mysql.com/doc/refman/5.1/en/replication-features-limit.html
However, if "ORDER BY PK" is used, the order of rows is defined and such a statement could be logged in statement format without any warning.
You can use like this way limit in Update Queries like these
UPDATE messages SET test_read=1
WHERE id IN (
SELECT id FROM (
SELECT id FROM messages
ORDER BY date_added DESC
LIMIT 5, 5
) tmp
);
Also please
Can you try it? way of getting row_number
UPDATE Donation d1 join (SELECT id,(SELECT #Row:=0) as row,(#Row := #Row + 1) AS row_number FROM Donation where Available <> 0 AND BloodGroup='AB-') d2
ON d1.id=d2.id
SET d1.Available='three'
WHERE d1.Available <> 0 AND d1.BloodGroup='AB-' AND d2.row_number='1'

Conditional UPDATE if new_value <= value_from_different_row

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.