MySQL INSERT on duplicate key UPDATE with SELECT - mysql

I have a query like the following
INSERT INTO connections (`c_id`,`in`,`out`,`ip`,`userID`)
VALUES (
(
SELECT c_id
FROM connections
WHERE (a bunch of conditions)
ORDER BY c_id DESC LIMIT 1
),
'1373799802',
0,
INET_ATON('127.0.0.1'),
4
)
ON DUPLICATE KEY UPDATE `out` = 1
Which throws the following error
1093 - You can't specify target table 'connections' for update in FROM clause
Obviously I can't have a SELECT clause within the insert into on duplicate update syntax, but I'd really rather do that instead of have 2 queries running. Can anyone tell me how I can do this?

Try like this instead:
INSERT INTO connections (`c_id`,`in`,`out`,`ip`,`userID`)
VALUES (
(
SELECT p.c_id
FROM (select * from connections) p
WHERE (a bunch of conditions)
ORDER BY p.c_id DESC LIMIT 1
),
'1373799802',
0,
INET_ATON('127.0.0.1'),
4
)
ON DUPLICATE KEY UPDATE `out` = 1
This issue seems due to a bug in mysql version 4.1.7 which states that
you can't update the same table which you use in the SELECT part
see Here
Not sure if this is the same version you are using as well.

try this
INSERT INTO connections (`c_id`,`in`,`out`,`ip`,`userID`)
SELECT c_id ,'1373799802', 0, INET_ATON('127.0.0.1'),4
FROM connections
WHERE (a bunch of conditions)
ORDER BY c_id DESC LIMIT 1
ON DUPLICATE KEY UPDATE `out` = 1

The following code inside your query:
SELECT c_id
FROM connections
WHERE (a bunch of conditions)
ORDER BY c_id DESC
LIMIT 1
actually results in a table and not a single value. For a successful attempt, try this:
INSERT INTO connections (`c_id`,`in`,`out`,`ip`,`userID`)
SELECT c_id,
'1373799802',
0,
INET_ATON('127.0.0.1'),
4
FROM connections
WHERE (a bunch of conditions)
ORDER BY c_id DESC LIMIT 1
ON DUPLICATE KEY
UPDATE `out` = 1

Related

How to insert into a row, a value from other table?

I have 3 Mysql tables.
A table with the classes and the labs and their id.
A table with the teachers_list and their subject.
A table which is going to be the schedule.**
I want to randomly assign one of the physicists to one of the physics labs on my third table which is going to be the schedule.
INSERT INTO schedule(teacher_name, class_id)
VALUES (select teacher_name from teachers_list where subject="Physicist” order by rand() limit 1,
select id from lab_list where lab="Physics_lab" order by rand() limit 1);
**This one doesn't work :(
Can you help me?**
I think that you want the insert ... select syntax, along with a subquery:
insert into schedule(teacher_name, class_id)
select
(
select teacher_name
from teachers_list
where subject = 'Physicist'
order by rand()
limit 1
),
id
from lab_list
where lab = 'Physics_lab'

MySQL - INSERT INTO … ON DUPLICATE KEY UPDATE with values select from the same table

I have two tables, one is posts(ID, edited_date, is_public, ... etc), another one is post_pagination(index, post_id) for storing the pagination data of posts. In post_pagination, index is primary key.
One step in the pagination process, I need to check the sorted result and post_pagination table, and do INSERT/UPDATE, like:
INSERT INTO post_pagination(`index`, post_id)
SELECT * /* full outer join new sorted result and pagination table */
FROM (
WITH
cte1 AS (
SELECT ROW_NUMBER() OVER(ORDER BY edited_date DESC, ID DESC) AS new_rank, ID
FROM posts
WHERE is_public = 1
),
cte2 AS (
SELECT `index`
FROM post_pagination
)
SELECT `index` AS dummy_rank, ID
FROM cte2
LEFT JOIN cte1 on `index` = new_rank
UNION ALL
SELECT new_rank AS dummy_rank, ID
FROM cte2
RIGHT JOIN cte1 on `index` = new_rank
WHERE `index` IS NULL
) AS a
ORDER BY dummy_rank
ON DUPLICATE KEY UPDATE post_id = ID
The first time run this query, post_pagination is empty, so MySQL insert all data into the table.
index post_id
1 390
2 391
3 392
4 393
5 307
it works well.
When I run the second time, I expect all data will not be changed, but it update all post_id field to the last value of the result.
index post_id
1 307
2 307
3 307
4 307
5 307
I did a few tests, it seems like inserting values into a table by using values select from the same table would cause this problem, but I can't figure out why
Now I simply fix this problem by modifying the last line of the query to:
ON DUPLICATE KEY UPDATE post_id = ID + 0
Is there any better way to solve this issue?
Can you instead use a temporary table?
CREATE TEMPORARY TABLE `post_pagination_temp` (
`index` INTEGER(1) AUTO_INCREMENT PRIMARY KEY,
`post_id` INTEGER(1)
);
INSERT INTO `post_pagination_temp` (`post_id`)
SELECT `ID` FROM `posts` ORDER BY `edited_date` DESC, `ID` DESC;
INSERT INTO `post_pagination`
SELECT * FROM `post_pagination_temp`
ON DUPLICATE KEY UPDATE `index` = VALUES(`index`);
This assumes you have the correct index on post_pagination, i.e. the only unique being the primary key on post_id.

Correct way to delete 'older' rows in MySQL

My table has a TIME field.
I want to keep only 5 newest rows.
Can I delete the old rows without using SELECT?
I think logic should be something like this:
DELETE FROM tbl WHERE row_num > 5 ORDER BY TIME
How can I implement this in MySQL whitout using SELECT to get list of TIME values?
Without proper ORDER BY clause, SQL result set have to be considered as unordered.
You have to provide a column to explicitly store your rows sequence numbers. This could be a time stamp or the auto_increment column of your table.
Please keep in mind you could have concurrent access to your table as well. What should be the expected behavior if someone else is inserting while you are deleting? As far as I can tell this could lead to situation where you keep only the "5 latest rows" + "those inserted on the other transaction".
If your have the time column for that purpose on your table and a PRIMARY KEY (or some other UNIQUE NOT NULL column) you could write:
DELETE tbl FROM tbl LEFT JOIN (SELECT * FROM tbl ORDER BY tm DESC LIMIT 5) AS k
ON (tbl.pk) = (k.pk)
WHERE k.`time` IS NULL;
If you have composite primary key (a,b) You could write:
DELETE tbl FROM tbl LEFT JOIN (SELECT * FROM tbl ORDER BY tm DESC LIMIT 5) AS k
ON (tbl.a,tbl.b) = (k.a,k.b)
WHERE k.tm IS NULL;
DELETE FROM TBL
WHERE ROW_NUM = (SELECT ROW_NUM FROM TBL LIMIT 6, 99999)
ORDER BY TIME DESC;
This will delete records from 6, 7, 8, 9, 10, ....., 200005
Because LIMIT range starts here from 6 to 9999 records, means 200005
Maybe this would be an alternative:
DELETE FROM tbl
WHERE primary_key NOT IN (SELECT primary_key
FROM tbl
ORDER BY time
DESC LIMIT 5)
If you want to exclude the top 5 rows, use something like:
DELETE FROM table WHERE primary_key IN
(SELECT primary_key FROM table LIMIT 1 OFFSET 5,1000000)
100000 can be a very large no

Select a value from MySQL database only in case it exists only once

Lets say I have a MySQL table that has the following entries:
1
2
3
2
5
6
7
6
6
8
When I do an "SELECT * ..." I get back all the entries. But I want to get back only these entries, that exist only once within the table. Means the rows with the values 2 (exists two times) and 6 (exists three times) have to be dropped completely out of my result.
I found a keyword DISTINCT but as far as I understood it only avoids entries are shown twice, it does not filters them completely.
I think it can be done somehow with COUNT, but all I tried was not really successful. So what is the correct SQL statement here?
Edit: to clarify that, the result I want to get back is
1
3
5
7
8
You can use COUNT() in combination with a GROUP BY and a HAVING clause like this:
SELECT yourCol
FROM yourTable
GROUP BY yourCol
HAVING COUNT(*) < 2
Example fiddle.
You want to mix GROUP BY and COUNT().
Assuming the column is called 'id' and the table is called 'table', the following statement will work:
SELECT * FROM `table` GROUP BY id HAVING COUNT(id) = 1
This will filter out duplicate results entirely (e.g. it'll take out your 2's and 6's)
Three ways. One with GROUP BY and HAVING:
SELECT columnX
FROM tableX
GROUP BY columnX
HAVING COUNT(*) = 1 ;
one with a correlated NOT EXISTS subquery:
SELECT columnX
FROM tableX AS t
WHERE NOT EXISTS
( SELECT *
FROM tableX AS t2
WHERE t2.columnX = t.columnX
AND t2.pk <> t.pk -- pk is the primary key of the table
) ;
and an improvement on the first way (if you have a primary key pk column and an index on (columnX, pk):
SELECT columnX
FROM tableX
GROUP BY columnX
HAVING MIN(pk) = MAX(pk) ;
select id from foo group by id having count(*) < 2;

Mysql delete order by

I have a table and I only display the latest 30 rows by order by ID.
I'm trying to delete any rows after the 30 newest rows by using this query below.
DELETE FROM table WHERE type = 'test' ORDER BY id DESC LIMIT 30, 60
I keep getting this error below
#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 ' 60' at line 1
What am I doing wrong?
Try this one,
DELETE FROM table
WHERE ID IN
(
SELECT ID
FROM
(
SELECT ID
FROM table
WHERE Type = 'TEST'
ORDER BY ID
LIMIT 30,60
) a
)
Second edit: While MySQL supports LIMIT in delete statements, it does not allow an OFFSET. This means that you cannot skip the first 30 rows.
Make a subselect on id (or any other primary key):
DELETE FROM table WHERE id IN (SELECT id FROM table WHERE type = 'test' ORDER BY id DESC LIMIT 30, 60)
This is not possible this way.
You could try it with a nested select statement, somewhat like this:
DELETE FROM table
WHERE type = 'test'
AND ID IN (SELECT id from table where type = 'test' order by id desc limit 30 )
Try like this
DELETE FROM table WHERE id in(SELECT id FROM table WHERE type = "test" order by id desc limit 30, 60)
I was unable to use the limit clause in the sub-query, so the solution I use, somewhat messy, is:-
select group_concat(id) into #idList from
(
select id from table order by id desc limit 0,30
) as saveIds;
delete from table where not find_in_set(id,#idList)
Alternatively,
select group_concat(id) into #idList from
(
select id from table order by id desc limit 30
) as saveIds;
delete from table where find_in_set(id,#idList)