This is my table:
PACKAGE_ID ITEM_ID
1 1
1 2
1 3
2 4
2 5
3 6
4 7
4 8
I want a new column called count that count 1 to N according to package ID. E.g.:
PACKAGE_ID ITEM_ID COUNT
1 1 1
1 2 2
1 3 3
2 4 1
2 5 2
3 6 1
4 7 1
4 8 2
Thanks!
p.s: I'm using MariaDb 10.1
You can use window function.
SELECT PACKAGE_ID, ITEM_ID
, ROW_NUMBER() OVER (PARTITION BY PACKAGE_ID ORDER BY PACKAGE_ID, ITEM_ID) AS THE_COUNT
FROM your_table
In pre 8.0 MySQL, the fastest method is probably variables:
select t.*,
(#rn := if(#p = package_id, #rn + 1,
if(#p := package_id, 1, 1)
)
) as counter
from t cross join
(select #rn := 0, #p := -1) params
order by package_id, item_id;
Obviously an index on (package_id, item_id) would benefit this query.
In MySQL 8+ or similar versions of MariaDB, use row_number():
You can use subquery :
select *, (select count(1)
from table t1
where t1.PACKAGE_ID = t.PACKAGE_ID and
t1.ITEM_ID <= t.ITEM_ID
) as COUNT
from table t;
Related
I have Price_trend table in that m_id and c_id are foreign keys. I need m_id column 3,7 top two records.
Her is my db-fiddle
table: price_trend
id
c_id
m_id
date
1
1
3
2022-12-08
2
1
3
2022-12-06
3
1
3
2022-12-05
4
1
7
2022-12-03
5
1
7
2022-12-02
6
1
7
2022-12-01
My Attempt:
I have written the query up to this point, but am stuck on what to do next
select * from price_trend where c_id=1 and m_id in(3,7) limit 4;
I want result:
id
c_id
m_id
date
1
1
3
2022-12-08
2
1
3
2022-12-06
4
1
7
2022-12-03
5
1
7
2022-12-02
On MySQL 8+ we can use the ROW_NUMBER() window function:
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY m_id ORDER BY date DESC) rn
FROM price_trend
WHERE c_id = 1 AND m_id IN (3, 7)
)
SELECT id, c_id, m_id, date
FROM cte
WHERE rn <= 2
ORDER BY m_id, date DESC;
ROW_NUMBER() assigns a sequence 1, 2, 3 to each record in a given m_id group, with the most recent record being assigned 1, the next most recent 2, and so on. In the outer query we then restrict to only records having the first two row numbers.
In MySQL 5.X, you can use variables to generate the ranking index. Once you get it, you can select ranking values less or equal to 2.
SET #rownum = 0;
SET #prevmid = NULL;
SELECT id, c_id, m_id, date_
FROM (SELECT id, c_id, date_,
IF(m_id = #prevmid, #rownum := #rownum + 1, #rownum := 1) AS rn,
IF(m_id = #prevmid, m_id, #prevmid := m_id) AS m_id
FROM price_trend
WHERE c_id = 1 AND m_id IN (3, 7)
ORDER BY id) cte
WHERE rn <= 2
Check the demo here.
Table: signin
username
class
aaa
1
aaa
1
bbb
1
bbb
1
ccc
1
ddd
2
ddd
2
eee
2
There are two classes. How do I get first TWO username who has the most total count for each class(class-1 & class-2) like below ↓.
username
class
total
aaa
1
2
bbb
1
2
ddd
2
2
eee
2
1
EDIT: Is there any way not using over partition by?
In older version of MySQL you need to generate the ranking manually without any ranking function. Below code will work.
Schema and insert statements:
create table signin(username varchar(50),class int);
insert into signin values('aaa', 1);
insert into signin values('aaa', 1);
insert into signin values('bbb', 1);
insert into signin values('bbb', 1);
insert into signin values('ccc', 1);
insert into signin values('ddd', 2);
insert into signin values('ddd', 2);
insert into signin values('eee', 2);
Query:
select username,class,total
from
(
select username,class,count(*) total ,
#rn := IF(#prev = class, #rn + 1, 1) AS rn,
#prev := class
from signin
JOIN (SELECT #prev := NULL, #rn := 0) AS vars
group by class,username
order by class,username,count(*) desc
)t
where rn<=2
Output:
username
class
total
aaa
1
2
bbb
1
2
ddd
2
2
eee
2
1
db<fiddle here
Rank them by count in descending order and display only the first two. Sample data in lines #1 - 10; query begins at line #11.
SQL> with signin (username, class) as
2 (select 'aaa', 1 from dual union all
3 select 'aaa', 1 from dual union all
4 select 'bbb', 1 from dual union all
5 select 'bbb', 1 from dual union all
6 select 'ccc', 1 from dual union all
7 select 'ddd', 2 from dual union all
8 select 'ddd', 2 from dual union all
9 select 'eee', 2 from dual
10 )
11 select username, class, total
12 from (select username, class, count(*) total,
13 rank() over (partition by class order by count(*) desc) rnk
14 from signin
15 group by username, class
16 )
17 where rnk <= 2
18 order by class, username;
USE CLASS TOTAL
--- ---------- ----------
aaa 1 2
bbb 1 2
ddd 2 2
eee 2 1
SQL>
With rank() ranking window function and common table expression you can easily achieve that.
To get the usernames with most count in a class I have used rank()over (partition by class order by count(*) desc) along with group by class,username
Schema and insert statements:
create table signin(username varchar(50),class int);
insert into signin values('aaa', 1);
insert into signin values('aaa', 1);
insert into signin values('bbb', 1);
insert into signin values('bbb', 1);
insert into signin values('ccc', 1);
insert into signin values('ddd', 2);
insert into signin values('ddd', 2);
insert into signin values('eee', 2);
Query:
with cte as
(
select username,class,count(*) total,
rank()over (partition by class order by count(*) desc) rn
from signin
group by class,username
)
select username,class,total from cte where rn<=2
Output:
username
class
total
aaa
1
2
bbb
1
2
ddd
2
2
eee
2
1
db<>fiddle here
In older versions of MySQL, you would use variables. But you have to be careful with the syntax.
The basic idea is aggregation and then enumeration:
select uc.*
from (select uc.*,
(#rn := if(#c = class, #rn + 1,
if(#c := class, 1, 1)
)
) as seqnum
from (select username, class, count(*) as cnt
from signin
group by username, class
order by class, cnt desc
) uc cross join
(select #c := '', #rn := 0) params
) uc
where seqnum <= 2;
There are three very important things to remember when using variables in a select:
They are deprecated. In more recent versions, you should be using window functions for this type of operation.
MySQL does not guarantee the order of evaluation of expressions in a SELECT. So, you should handle all variables in a single expression, which is why the #rn := is rather complicated.
MySQL does not guarantee that the ORDER BY is evaluated before the variable assignments. That is why the ORDER BY is in the subquery.
I have a table department_courses with following structure :
id department_id name
-- ------------- -----
1 11 Abcd
2 11 Bghg
3 11 Lopps
4 13 Abvgf
So from this table I need to count the position of the subquery. I mean to say , The position of the name Lopps for department_id is 3 . How to get this in mysql query?
If you only need to do this for one row, then a single query is simpler:
select count(*)
from department_courses dc
where dc.id <= (select dc2.id
from department_courses dc2
where dc2.name = 'Lopps'
);
If you want to assign a row number to all rows, then variables are probably a better method.
Try:
select row_num
from (
select t.*, #r := #r + 1 row_num
from department_courses t,
(select #r := 0) r
) x
where x.name = 'Lopps'
x.department_id = 3
I have a table for my users scores like this:
id | kills
----------
2 | 1
1 | 1
1 | 5
1 | 3
2 | 4
2 | 5
3 | 5
I want to get the first 2 rows of each player which have more than 2 kills. So the result should look like this
id | kills
----------
1 | 5
1 | 3
2 | 4
2 | 5
3 | 5
I tried this but it doesn't work:
SELECT *
FROM user_stats us
WHERE
(
SELECT COUNT(*)
FROM user_stats f
WHERE f.id=us.id AND f.kills > 2
) <= 2;
I suspect that you just want the two largest values for users that have kills > 2. If so, use variables:
select us.*
from (select us.*,
(#rn := if(#i = id, #rn + 1,
if(#i := id, 1, 1)
)
) as seqnum
from user_stats us cross join
(select #rn := 0, #i := -1) params
where us.kills > 2
order by us.id, kills desc
) us
where seqnum <= 2;
select * from user_stats
where (id,kills) in (select id, max(kills) from user_stats where kills > 2 group by id
union
select id, min(kills) from user_stats where kills > 2 group by id)
Try this. I am coming from Oracle, where rownum is a count of rows selected. This should have the same effect.
select #rownum:=#rownum+1, us.*
from user_stats us , (select #rownum := 0) r
where id in (
select id from user_stats f
group by id
having count(*) > 2
)
and #rownum < 3;
based on response of vkp. Take min and max when id has more then 1 kill
select id, max(kills)
from user_stats
group by id
having count(kills) > 2
union
select id, min(kills)
from user_stats
group by id
having count(kills) > 2
order by id
I currently have a query that counts the 'parent_id' value for each row and adds it within each row.
For example, if I have 4 records that have the has the value '1432' under 'parent_id' it will show the count value 4 under the 'count' column.
I am trying to limit the amount of rows based on the count number.
For example, let's say we want to exceed the number of records per 'parent_id' to 2. if the 'parent_id' has reached the third record, it just passes on and it won't return that record.
Example of existing table:
ID parent_id count(parent_id)
1 1234 2
2 1234 2
3 3221 3
4 3221 3
5 3221 3
6 5432 1
7 4312 1
The result I'd like to get is:
ID parent_id count(parent_id)
1 1234 2
2 1234 2
3 3221 2
4 3221 2
5 5432 1
6 4312 1
This is a select per group query:
SELECT id, parent_id, rn
FROM (
SELECT #rn:=CASE WHEN #parent_id=parent_id
THEN #rn+1
ELSE 1
END AS rn
, #parent_id:=parent_id AS parent_id
, id
FROM t, (SELECT #rn:=0,#parent_id:='') AS u
ORDER BY parent_id,id
) as s
WHERE rn <= 2
FIDDLE
Mihai's answer may work, but it is not guaranteed to work. The problem is that MySQL does not guarantee the order of evaluation of expressions in the select. And, there are even occasions where it does not evaluate them in the expected order.
So, when using variables, it is safest to put all assignments in a single expression:
SELECT id, parent_id, rn
FROM (SELECT (#rn := if(#parent_id = parent_id, #rn + 1,
if(#parent_id := parent_id, 1, 1)
)
) as rn,
t.*
FROM t CROSS JOIN
(SELECT #rn := 0, #parent_id := '') params
ORDER BY parent_id, id
) t
WHERE rn <= 2;