I want to delete rows with an offset, so I am forced to use a nested query since its not support in the raw DELETE clause.
I know this would worked (ID is the primary key):
DELETE FROM customers
WHERE ID IN (
SELECT ID
FROM customers
WHERE register_date > '2012-01-01'
ORDER BY register_date ASC
LIMIT 5, 10
);
However, this is unsupported in my version as I get the error
This version of MariaDB doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'.
Server version: 10.4.22-MariaDB
What can I do to achieve the same result as above that is supported in my version.
CREATE TABLE customers (
ID INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(32) NOT NULL,
REGISTER_DATE DATETIME NOT NULL
);
Join the table to a subquery that uses ROW_NUMBER() window function to sort the rows and filter the rows that you want to be deleted:
DELETE c
FROM customers c
INNER JOIN (
SELECT *, ROW_NUMBER() OVER (ORDER BY register_date) rn
FROM customers
WHERE register_date > '2012-01-01'
) t ON t.ID = c.ID
WHERE t.rn > 5 AND t.rn <= 15; -- get 10 rows with row numbers 6 - 15
See the demo.
If I did not miss something a simple delete with join will do the job...
delete customers
from (select *
from customers
WHERE register_date > '2012-01-01'
order by register_date asc
limit 5, 2) customers2
join customers on customers.id = customers2.id
Here is a demo for your version of MariaDB
You could try assigning a rank to your rows with the ROW_NUMBER window function, then catch those rows whose rank position is between 5 and 15.
DELETE FROM customers
WHERE ID IN (
SELECT *
FROM (SELECT ID,
ROW_NUMBER() OVER(
ORDER BY IF(register_date>'2012-01-01', 0, 1)
register_date ) AS rn
FROM customers) ranked_ids
WHERE rn > 4
AND rn < 16
);
This would safely avoid the use of LIMIT, though achieves the same result.
EDIT. Doing it with a join.
DELETE FROM customers c
INNER JOIN (SELECT ID,
ROW_NUMBER() OVER(
ORDER BY IF(register_date>'2012-01-01', 0, 1)
register_date ) AS rn
FROM customers) ranked_ids
WHERE
) ids_to_delete
ON c.ID = ids_to_delete.ID
AND ids_to_delete.rn > 4
AND ids_to_delete.rn < 16
Related
I am trying to write a query for a condition:
If >=80 percent (4 or more rows as 4/5*100=80%) of the top 5 recent rows(by Date Column), for a KEY have Value =A or =B, then change the flag from fail to pass for the entire KEY.
Here is the input and output sample:
I have highlighted recent rows with green colour in the sample.
Can someone help me in this?
I tried till finding the top 5 recent rows by the foll code:
select * from(
select *, row_number() over (partition by "KEY") as 'RN' FROM (
select * from tb1
order by date desc))
where "RN"<=5
Couldnt figure what to be done after this
Test this:
WITH
-- enumerate rows per key group
cte1 AS ( SELECT *,
ROW_NUMBER() OVER (PARTITION BY `key` ORDER BY `date` DESC) rn
FROM sourcetable ),
-- take 5 recent rows only, check there are at least 4 rows with A/B
cte2 AS ( SELECT `key`
FROM cte1
WHERE rn <= 5
GROUP BY `key`
HAVING ( SUM(`value` = 'A') >= 4
OR SUM(`value` = 'B') >= 4 )
-- AND SUM(rn = 5) )
-- update rows with found key values
UPDATE sourcetable
JOIN cte2 USING (`key`)
SET flag = 'PASS';
5.7 version – Ayn76
Convert CTEs to subqueries. Emulate ROW_NUMBER() using user-defined variable.
This is my query... SELECT * FROM comments WHERE content_id in (525, 537) LIMIT 60 This is the SS of result:
here content_id = 537 is selected 5 times.
(comment_id is UNIQUE key )..
My question is: How to limit selected rows by 2, where values of content_id is same...
Maximum two duplicate records for each content_id... like in this picture:
If you are running MySQL 8.0, you can do this with row_number():
select comment_id, content_id
from (
select t.*, row_number() over(partition by content_id order by comment_id) rn
from mytable t
) t
where rn <= 2
In earlier versions, one solution is a correlated subquery:
select t.*
from mytable t
where (
select count(*)
from mytable t1
where t1.content_id = t.content_id and t1.comment_id < t.comment_id
) < 2
This is the essential setup of the table (only the DDL for relevant columns is present). MySQL version 8.0.15
The intent is to show an average of date difference interval between orders.
CREATE TABLE final (
prim_id INT(11) NOT NULL AUTO_INCREMENT,
order_ID INT(11) NOT NULL,
cust_ID VARCHAR(45) NOT NULL,
created_at DATETIME NOT NULL,
item_name VARCHAR(255) NOT NULL,
cust_name VARCHAR(255) NOT NULL,
PRIMARY KEY (prim_id),
COLLATE='latin1_swedish_ci'
ENGINE=InnoDB
AUTO_INCREMENT=145699
Additional information:
cust ID -> cust_name (one-to-many)
cust_ID -> order_ID (one-to-many)
order ID -> item_name (one-to-many)
order ID -> created_at (one-to-one)
prim_id -> *everything* (one-to-many)
I've thought of using min(created_at) and max(created_at) but that will exclude all the orders between oldest and newest. I need a more refined solution.
The end result should be like this:
Information about average time intervals between all orders, (not just min and max because there are quite often times, more than two) measured in days, next to a column showing the client's name (cust_name).
If I get this right you might use a subquery getting the date of the previous order. Use datediff() to get the difference between the dates and avg() to get the average of that differences.
SELECT f1.cust_id,
avg(datediff(f1.created_at,
(SELECT f2.created_at
FROM final f2
WHERE f2.cust_id = f1.cust_id
AND (f2.created_at < f1.created_at
OR f2.created_at = f1.created_at
AND f2.order_id < f1.order_id)
ORDER BY f2.created_at DESC,
f2.order_id DESC
LIMIT 1)))
FROM final f1
GROUP BY f1.cust_id;
Edit:
If there can be more rows for one order ID, as KIKO Software mentioned we need to do the SELECT from the distinct set of orders like:
SELECT f1.cust_id,
avg(datediff(f1.created_at,
(SELECT f2.created_at
FROM (SELECT DISTINCT f3.cust_id,
f3.created_at,
f3.order_id
FROM final f3) f2
WHERE f2.cust_id = f1.cust_id
AND (f2.created_at < f1.created_at
OR f2.created_at = f1.created_at
AND f2.order_id < f1.order_id)
ORDER BY f2.created_at DESC,
f2.order_id DESC
LIMIT 1)))
FROM (SELECT DISTINCT f3.cust_id,
f3.created_at,
f3.order_id
FROM final f3) f1
GROUP BY f1.cust_id;
This may fail if there can be two rows for an order with different customer IDs or different creation time stamps. But in that case the data is just complete garbage and needs to be corrected before anything else.
2nd Edit:
Or alternatively getting the maximum creation timestamp per order if these can differ:
SELECT f1.cust_id,
avg(datediff(f1.created_at,
(SELECT f2.created_at
FROM (SELECT max(f3.cust_id) cust_id,
max(f3.created_at) created_at,
f3.order_id
FROM final f3
GROUP BY f3.order_id) f2
WHERE f2.cust_id = f1.cust_id
AND (f2.created_at < f1.created_at
OR f2.created_at = f1.created_at
AND f2.order_id < f1.order_id)
ORDER BY f2.created_at DESC,
f2.order_id DESC
LIMIT 1)))
FROM (SELECT max(f3.cust_id) cust_id,
max(f3.created_at) created_at,
f3.order_id
FROM final f3
GROUP BY f3.order_id) f1
GROUP BY f1.cust_id;
Background:
I run a platform which allows users to follow creators and view their content.
The following query successfully displays 50 posts ordered by popularity. There is also some other logic to not show posts the user has already saved/removed, but that is not relevant for this question.
Problem:
If one creator is particularly popular (high popularity), the top 50 posts returned will nearly all be by that creator.
This skews the results as ideally the 50 posts returned will not be in favor of one particular author.
Question:
How can I limit it so the author (which uses the field posted_by) is returned no more than 5 times. It could be less, but definitely no more than 5 times should one particular author be returned.
It should still be finally ordered by popularity DESC
SELECT *
FROM `source_posts`
WHERE `posted_by` IN (SELECT `username`
FROM `source_accounts`
WHERE `id` IN (SELECT `sourceid`
FROM `user_source_accounts`
WHERE `profileid` = '100'))
AND `id` NOT IN (SELECT `postid`
FROM `user_posts_removed`
WHERE `profileid` = '100')
AND `live` = '1'
AND `added` >= Date_sub(Now(), INTERVAL 1 month)
AND `popularity` > 1
ORDER BY `popularity` DESC
LIMIT 50
Thank you.
Edit:
I am using MySQL version 5.7.24, so unfortunately the row_number() function will not work in this instance.
In MySQL 8+, you would simply use row_number():
select sp.*
from (select sp.*,
row_number() over (partition by posted_by order by popularity desc) as seqnum
from source_posts sp
) sp
where seqnum <= 5
order by popularity desc
limit 50;
I'm not sure what the rest of your query is doing, because it is not described in your question. You can, of course, add additional filtering criteria or joins.
EDIT:
In earlier versions, you can use variables:
select sp.*
from (select sp.*,
(#rn := if(#p = posted_by, #rn + 1,
if(#p := posted_by, 1, 1)
)
) as rn
from (select sp.*
from source_posts sp
order by posted_by, popularity desc
) sp cross join
(select #p := '', #rn := 0) params
) sp
where rn <= 5
order by popularity desc
limit 50;
Could try the row number function. Using that, it would assign each employee a distinct "id." So if one employee had 50 records, only those with a row_number (named as "rank") less than or equal to 5 would be returned.
Select *
from(
SELECT `source_posts.*`, row_number() over (partition by `username` order by `popularity` desc) as rank
FROM `source_posts`
WHERE `posted_by` IN (SELECT `username`
FROM `source_accounts`
WHERE `id` IN (SELECT `sourceid`
FROM `user_source_accounts`
WHERE `profileid` = '100'))
AND `id` NOT IN (SELECT `postid`
FROM `user_posts_removed`
WHERE `profileid` = '100')
AND `live` = '1'
AND `added` >= Date_sub(Now(), INTERVAL 1 month)
AND `popularity` > 1
ORDER BY `popularity` DESC
LIMIT 50 `enter code here`)
where rank <= 5
I would like to get values without the smallest and the biggest ones, so without entry with 2 and 29 in column NumberOfRepeating.
My query is:
SELECT Note, COUNT(*) as 'NumberOfRepeating'
WHERE COUNT(*) <> MAX(COUNT(*))AND COUNT(*) <> MIN(COUNT(*))
FROM Note GROUP BY Note;
SELECT Note, COUNT(*) as 'NumberOfRepeating'
FROM Notes
GROUP BY Note
HAVING count(*) <
(
SELECT max(t.maxi)
FROM (select
Note, COUNT(Note) maxi FROM Notes
GROUP BY Note
) as t
)
AND
count(*) >
(
SELECT min(t.min)
FROM (select
Note, COUNT(Note) min FROM Notes
GROUP BY Note
) as t
)
try this code.
One method would use order by and limit, twice:
select t.*
from (select t.*
from t
order by NumberOfRepeating asc
limit 99999999 offset 1
) t
order by NumberOfRepeating desc
limit 99999999 offset 1;
Try this code,
Select * from Note where NumberOfRepeating < (select MAX(NumberOfRepeating) from Note ) AND NumberOfRepeating > (select MIN(NumberOfRepeating) from Note );
Here in the code, as in your table Note is the name of the table, and NumberOfRepeating is the column name, as in your table.
Try this. It should work
SELECT *
FROM ( SELECT Note, COUNT(*) as 'NumberOfRepeating'
FROM Notes
GROUP BY Note
ORDER BY NumberOfRepeating DESC
LIMIT 1, 2147483647
) T1
ORDER BY T1.NumberOfRepeating
LIMIT 1, 2147483647