How to find the pre-last(penultimate) value in SQL? - mysql

I have a table
/*CREATE TABLE Purchases (
id INTEGER AUTO_INCREMENT PRIMARY KEY,
transaction_id INTEGE ,
user_id INTEGER,
purchase_date datetime,
product_type VARCHAR(30),
price INTEGER ,
);
*/
And I need to find the prelast purchase of unique users. have no clue how to to this. Better if would be MYSQL.
I'm trying to serch for the last. But even that seems bad
SELECT
user_id,
LAST_VALUE(transaction_id) OVER (
ORDER BY purchase_date
RANGE BETWEEN
UNBOUNDED PRECEDING AND
UNBOUNDED FOLLOWING
) last_purchase
FROM
purchases;
Please help me with the seraching prelast purcase(stransaction_id) of the unique visitor(user_id)

If "pre-last" means the second-to-last (i.e. penultimate) then use row_number():
select p.*
from (select p.*,
row_number() over (partition by user_id order by purchase_date desc) as seqnum
from purchases p
) p
where seqnum = 2;

Number the rows per user by date descending. Keep all rows numbered #2.
select *
from
(
select p.*, row_number() over (partition by user_id order by purchase_date desc) as rn
from purchases p
) numbered
where rn = 2;

Invert the order, then use limit and offset to select the second row.

Related

Deleting rows using a limit and offset without using IN clause

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

I need to get last created eligible rider ids and pinged rider ids accordeing to a orderId using a sql query

I need to get my data set as this table
I am trying to get eligible set like this, need to group_concat pinged set also
x.id IN (SELECT MAX(x.id) FROM x WHERE ping rider id IS NULL GROUP BY orderId)
You can assign a group based on the cumulative number of non-null values in eligible_riders. Then aggregate and take the last value:
select og.*
from (select order_id, grp, max(eligible_riders) as eligible_riders,
group_concat(rider_id) as riders,
row_number() over (partition by order_id order by min(id) desc) as seqnum
from (select t.*,
sum(eligible_riders <> '') over (partition by order_id order by id) as grp
from t
) t
group by order_id, grp
) og
where seqnum = 1;
Hmmm . . . You could also do this with a correlated subquery, which might look a bit simpler:
select order_id, max(eligible_riders) as eligible_riders,
group_concat(rider_id) as riders
from t
where t.id >= (select max(t2.id)
from t t2
where t2.order_id = t.order_id and
t2.eligible_riders <> ''
)
group by order_id;
For performance, you want an index on (order_id, eligible_riders).

SUM where other field is MAX value

I have a table like the one above, I want to calculate total of amount with largest log_id and group by user_id.
the results will be as below :
this is my example code but this is not work :(
CREATE TABLE IF NOT EXISTS example (
`id` INT,
`log_id` INT,
`user_id` INT,
`amount` INT
);
INSERT INTO example VALUES
(1,1,10,4),
(2,2,10,8),
(3,3,10,2),
(4,3,10,6),
(5,1,12,9),
(6,2,12,4),
(7,1,13,7),
(8,1,14,2),
(9,2,14,6),
(10,1,15,7),
(11,2,15,4),
(12,3,15,9),
(13,3,15,6);
select max(log_id) as log_id, user_id, amount from example group by user_id, amount
Any ideas?
Thanks, xmush
You can go for RANK function and get data.
Schema (MySQL v8.0)
CREATE TABLE test
(
id INT,
logid int,
userid int,
amount INT
);
insert into test(id, logid, userid, amount)
values (1, 1, 12,9),
(2,2,12,4);
Query #1
WITH testcte AS (
SELECT *, RANK() OVER (PARTITION BY userid order by logid desc) as rnk from test
)
SELECT * FROM testcte WHERE rnk = 1;
id
logid
userid
amount
rnk
2
2
12
4
1
View on DB Fiddle
Here's a 'traditional' (pre-8.0) approach...
Grab the rows holding the largest log_id for each user...
SELECT x.*
FROM example x
JOIN
( SELECT user_id
, MAX(log_id) log_id
FROM example
GROUP
BY user_id
) y
ON y.user_id = x.user_id
AND y.log_id = x.log_id
Aggregate the resulting data set...
SELECT x.log_id
, x.user_id
, SUM(amount) total
FROM example x
JOIN
( SELECT user_id, MAX(log_id) log_id FROM example GROUP BY user_id ) y
ON y.user_id = x.user_id
AND y.log_id = x.log_id
GROUP
BY x.log_id
, x.user_id
ORDER
BY user_id;

How can I RANK from AVG sql?

I'm having trouble retrieving the rankings from a single line that has some uuid from that:
SELECT uuid , AVG(nodebuff+debuff+archer+builduhc+uhc+gapple)/6 as Average
from elo_ranked group by uuid
order by AVG(nodebuff+debuff+archer+builduhc+uhc+gapple)/6 desc
limit 3
I specify that the above function works and has the expected result.
Is this what you want?
SELECT uuid, AVG(nodebuff+debuff+archer+builduhc+uhc+gapple)/6 as Average ,
RANK() OVER (ORDER BY AVG(nodebuff+debuff+archer+builduhc+uhc+gapple)/6 DESC) as ranking
FROM elo_ranked
GROUP BY uuid
ORDER BY AVG(nodebuff+debuff+archer+builduhc+uhc+gapple)/6 desc
LIMIT 3;
EDIT:
To rank a specific user, use a subquery:
SELECT u.*
FROM (SELECT uuid, AVG(nodebuff+debuff+archer+builduhc+uhc+gapple)/6 as Average ,
RANK() OVER (ORDER BY AVG(nodebuff+debuff+archer+builduhc+uhc+gapple)/6 DESC) as ranking
FROM elo_ranked
GROUP BY uuid
) u
WHERE uuid = ?;
Also, I'm not sure if you need the aggregation. That would only be needed if a user had multiple rows in the elo_ranked table. If not needed, then you should use:
SELECT u.*
FROM (SELECT uuid, (nodebuff+debuff+archer+builduhc+uhc+gapple)/6 as Average ,
RANK() OVER (ORDER BY (nodebuff+debuff+archer+builduhc+uhc+gapple)/6 DESC) as ranking
FROM elo_ranked
) u
WHERE uuid = ?;
The GROUP BY has a lot of overhead, so this should be faster (unless MySQL has sophisticated optimizations to avoid the aggregation when grouping by a primary key).

How to get the ID of the max counted user in Order table

I have a table with
orderNumber(pk) , customerNumber , comment
I have to count the maximum order placed by a user and show its user ID and MAX count . I have following Query
It shows the count Right but it takes the first CustomerNumber in the table
SELECT maxCount.customerNumber , MAX(`counted`) FROM
(
SELECT customerNumber, COUNT(*) AS `counted`
FROM `orders`
GROUP BY `customerNumber`
)as maxCount
Thanks & regards
Just use ORDER BY with your inner query:
SELECT customerNumber, COUNT(*) AS `counted`
FROM `orders`
GROUP BY `customerNumber`
ORDER BY COUNT(*) DESC
LIMIT 1
If you want to return all customer numbers in the event of a tie, you can use a HAVING clause with a subquery which identifies the maximum count:
SELECT customerNumber, COUNT(*) AS counted
FROM orders
GROUP BY customerNumber
HAVING COUNT(*) = (SELECT MAX(t.counted) FROM (SELECT COUNT(*) AS counted
FROM orders
GROUP BY customerNumber) t)
Demo here:
SQLFiddle