Mysql Subquery Syntax - mysql

I'm wondering why statements like select * as t appear in mysql subqueries like the following.
The following deletes the oldest 3 rows in a table according to a created_time column.
Why is this right
DELETE FROM mytable WHERE id = ANY
( SELECT * FROM ( SELECT id FROM mytable ORDER BY created_time ASC LIMIT 3')as t)
and not
DELETE FROM mytable WHERE id = ANY
(SELECT id FROM mytable ORDER BY created_time ASC LIMIT 3)
?
To me, the second form makes sense. It doesn't work and I'd like to understand why the first is necessary. Specifically, what is t and what does as t do?

In many databases, a subquery in a from clause needs to have an explicit alias. The as is optional. I typically use as for columns and leave it out for tables:
DELETE FROM mytable
WHERE id = ANY ( SELECT * FROM ( SELECT id FROM mytable ORDER BY created_time ASC LIMIT 3') t)
Why you need the subquery is a vagary of MySQL. It doesn't allow the table reference in a delete or update to appear in a subquery clause. Oh, it does allow it in a subquery-within-a-subquery clause. So, it is pretty easy to work around this limitation.

Related

Delete a MySQL selection

I would like to delete my MySQL selection.
Here is my MySQL selection request:
SELECT *
FROM Items
WHERE id_user=1
ORDER
BY id_user
LIMIT 2,1
With this working request, I select the third item on my table which has as id_user: 1.
Now, I would like to delete the item that has been selected by my request.
I am looking for a same meaning request which would look like this :
DELETE FROM Items (
SELECT * FROM Items WHERE id_user=1 ORDER BY id_user LIMIT 2,1
)
The first thing to note is that there is an issue with your query. You are filtering on a unique value of id_user and sorting on the same column. As all records in the resultset will have the same id_user, the actual order of the resultset is undefined, and we cannot reliably tell which record comes third.
Assuming that you have another column to disanbiguate the resultset (ie some value that is unique amongst each group of records having the same id_user), say id, here is a solution to your question, that uses a self-join with ROW_NUMBER() to locate the third record in each group.
DELETE i
FROM items i
INNER JOIN (
SELECT
id,
id_user,
ROW_NUMBER() OVER(PARTITION BY id_user ORDER BY id) rn
FROM items
) c ON c.id = i.id AND c.id_user = i.id_user AND c.rn = 3
WHERE i.id_user=1 ;
Demo on DB Fiddle
You didn't provide the definition of your table. I guess it has a primary key column called id.
In that case you can use this
CREATE TEMPORARY TABLE doomed_ids
SELECT id FROM Items WHERE id_user = 1 ORDER BY id_user LIMIT 2,1;
DELETE FROM Items
WHERE id IN ( SELECT id FROM doomed_ids);
DROP TABLE doomed_ids;
It's a pain in the neck, but it works around the limitation of MySQL and MariaDB disallowing LIMITs in ... IN (SELECT ...) clauses.
You can use the select query to create a derived table and join it back to your main table to determine which record(s) to delete. Derived tables can use the limit clause.
Assuming that the PK is called id, the query would look as follows:
delete i from items i
inner join (SELECT id FROM Items
WHERE id_user=1
ORDER BY id_user LIMIT 2,1) i2 on i.id=i2.id
You need to substitute your PK in place of id. If you have a multi-column PK, then you need to select all the PK fields in the derived table and join on all of them.

How to update a column for a range of row numbers?

I need to update the column "myColumn" in "myTable" for rows 5 to 10. I thought I can do the following but apparently it's giving me a syntax error:
UPDATE myTable SET myColumn = 'mamal' LIMIT 5,10;
Thanks
SQL tables represent unordered sets. If you have a primary key, you can use that for the ordering.
MySQL does allow limit, but not offset, so you could update the first five rows:
UPDATE myTable
SET myColumn = 'mamal'
ORDER BY id
LIMIT 5;
But not with an offset.
You could get around this with a JOIN:
UPDATE mytable t JOIN
(SELECT id
FROM mytable tt
ORDER BY id
LIMIT 5, 10
) tt
ON tt.id = t.id
SET myColumn = 'mamal';
You can select and update column value based on primarykey of your table.
if you want to change rows limit means you can change limit values.
in below code id Is nothing but Primay Key of your table
UPDATE myTable SET myColumn='mamal' WHERE id IN ( SELECT id FROM ( SELECT id FROM myTable where id ORDER BY id ASC LIMIT 5,5 ) tmp )
TRY by using subquery and join as below and please replace the join column in <column> with real one
UPDATE myTable mt
INNER JOIN (SELECT <column> FROm myTable LIMIT 5,10) t ON t.<column> = mt.<column>
SET mt.myColumn = 'mamal'
Try this:
update myTable
set myColumn = 'mamal'
where your_primary_key
in (select * from (select your_primary_key from myTable limit 5,5) as t);
MySQL does support offset in a limit clause, but only for SELECT. So the correct syntax is: LIMIT offset,row_count. In this case it should be limit 5,5, not limit 5,10. Check: Select Syntax.
You can also use LIMIT in UPDATE, but you can only specify the row_count in this case. Check: Update Syntax.
If you don't have a primary key, just use any unique key.
For why you need a select * from here, it's just a trick to bypass the error discribed here.
I ran into the same problem and saw the answers but after researching I found that you can use "BETWEEN" to UPDATE the Table.
UPDATE myTable
SET myColumn = "mamal"
WHERE id BETWEEN 5 AND 10;

MySQL: First select 10 newest rows in descending PK order, then the rest of records alphabetically

I have a simple table USERS:
id | name
----+------
Can you help me with the query that would fetch all rows from the table and:
a) Place 10 rows with highest PK values on top, in id DESC order;
b) Place all remaining rows ordered by name ASC order.
Thank you!
This is a bit of a tricky question. The approach I would take is a join approach. Identify the primary keys for the first group using a join (this is happily fast because you are working with primary keys). Then use the match to that table for the order by:
select t.*
from table t left outer join
(select id
from table t
order by id desc
limit 10
) t10
on t.id = t10.id
order by t10.id desc,
t.name asc;
First question would be: do you really need this in one single query? I'm really not seeing the use case for such a query to be honest.
It'd be easier to just fetch the 10 biggest ids (storing somewhere the 10th biggest), and then fetch the rest in ascending name order (with a restriction on ids being smaller than the 10th biggest).
Otherwise in a single query, something like this would work, but it doesn't seem very efficient to me (maybe someone will have a better idea).
(
SELECT
id, name
from
USERS
ORDER BY id DESC LIMIT 0,10
)
UNION
(
SELECT
id, name
from
USERS
WHERE
id NOT IN (
SELECT id, name from USERS ORDER BY id DESC LIMIT 0,10
)
ORDER BY name ASC
)
(or maybe with a NOT EXISTS - the inner query will be different - instead of the NOT IN)

How to get unsorted result from a select query that uses an 'in'?

below is the query i have.
select * from tblQuestionTable where Paper='HTML' and QuestionId in (select QuestionId from tblTestOverview where TestId=1)
The sub query gives an unsorted result set, but the after querying the second select the result is sorted. How can i get the result in the same order as the subquery.
Any dataset your query is working with is by default unordered, whether it is a physical table or a derived one. Whatever order the server uses to read rows from it while actually executing the query is out of your control. That means you cannot reliably specify the order to be "same as in that subquery". Instead, why not just have a specific order in mind and specify it explicitly in the main query with an ORDER BY? For instance, like this:
SELECT *
FROM tblQuestionTable
WHERE Paper='HTML'
AND QuestionId IN (SELECT QuestionId FROM tblTestOverview WHERE TestId=1)
ORDER BY QuestionId
;
Having said that, here's something that might be close to what you are looking for. The ROW_NUMBER function assigns row numbers to the derived dataset in an undetermined order (ORDER BY (SELECT 1)). It may or may not be the order in which the server has read the rows, but you can use the assigned values to order the final result set by:
SELECT q.*
FROM tblQuestionTable AS q
INNER JOIN (
SELECT
QuestionId,
rn = ROW_NUMBER() OVER (ORDER BY (SELECT 1))
FROM tblTestOverview
WHERE TestId = 1
) AS o
ON o.QuestionId = q.QuestionId
ORDER BY o.rn ASC
;
select result for tblQuestionTable will be sorted based on its primary index by default unless specified. tblTestOverview select result also does the same. So you need to include the primary index key feild from tblTestOverview table in the select query for tblQuestionTable and specify an order by clause based on that field.

You can't specify target table 'wp_mail_queue' for update in FROM clause

I get this error:
You can't specify target table 'wp_mail_queue' for update in FROM
clause
Because of this query:
DELETE FROM wp_mail_queue
WHERE message_id = (SELECT message_id FROM wp_mail_queue ORDER BY id LIMIT 0, 1)
ORDER BY wp_mail_queue.id
LIMIT 100
And I suppose it results from the subquery. Replace DELETE with SELECT * and it returns exactly the entries that I want to delete. What I need is a way to rewrite this so that I won't need to query wp_mail_queue in that subquery, or avoid the subquery entirely.
My thoughts went first to variables, if I could somehow assign the subquery to one and then use that?
Try:
DELETE FROM wp_mail_queue
WHERE message_id = (SELECT * FROM (SELECT message_id FROM wp_mail_queue ORDER BY id LIMIT 0, 1) queue_table)
ORDER BY wp_mail_queue.id
LIMIT 100