Select limited number of rows based on unique column value - mysql

I have a table in the following format
ID SOURCE_ID
1 1
2 1
3 1
4 2
5 3
6 3
7 4
8 4
9 4
10 4
11 4
12 1
13 1
14 3
15 3
16 3
17 3
18 2
19 2
I want to be able to select 5 records MAX for each unique source_id.
So I should end up having returned 5 rows for source_id = 1, 5 rows for souce_id = 2, and so on.
Any ideas? Thank you in advance.

E.g.:
SELECT id
, source_id
FROM
( SELECT id
, source_id
, CASE WHEN #prev = source_id THEN #i:=#i+1 ELSE #i:=1 END i
, #prev := source_id prev
FROM my_table
, (SELECT #prev:=null,#i:=0) vars
ORDER
BY source_id
, id
) x
WHERE i <=5
ORDER
BY id;

Related

SQL : Increment a new column whenever there is a multiple of 5 else dont

I came across this question in a round of interview. A table has the following column.
ID
1
2
3
4
5
6
7
8
9
11
12
13
14
15
16
17
18
19
20
22
23
24
26
The question is to create a new column that starts with '1' and increments on the next ID whenever there is a multiple of 5. So the expected output is
ID
Result
1
1
2
1
3
1
4
1
5
1
6
2
7
2
8
2
9
2
11
2
12
2
13
2
14
2
15
2
16
3
17
3
18
3
19
3
20
3
22
4
23
4
24
4
26
4
You can combine two window functions: LAG() and SUM(). For example:
select id,
1 + sum(case when lid % 5 = 0 then 1 else 0 end) over(order by id) as v
from (
select *, lag(id) over(order by id) as lid from t
) x
order by id
Result:
id v
-- -
1 1
2 1
3 1
4 1
5 1
6 2
7 2
8 2
9 2
11 2
12 2
13 2
14 2
15 2
16 3
17 3
18 3
19 3
20 3
22 4
23 4
24 4
26 4
See running example at DB Fiddle.
For MySQL 5+ you may use, for example
SELECT id, (#result := COALESCE( #result + !(id % 5), 1 )) - !(id % 5) result
FROM t
CROSS JOIN (SELECT #result := NULL) init_variable
ORDER BY id
For MySQL 8+ use
SELECT id, 1 + SUM(!(id % 5)) OVER (ORDER BY id ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING) resuls
FROM t
You can do this without a subquery:
select t.*,
1 + sum( (id % 5) = 0 ) over (order by id) - (id % 5 = 0)
from t
order by id;
What is the logic here? Calculate the cumulative sum of "5"s up to this row. Then subtract out the value on this row, because the increment takes effect on the next row.
It is also tempting to write this using a window frame clause, but that ends up being a wee bit more complicated because the first value is NULL:
select t.*,
1 + coalesce(sum( (id % 5) = 0 ) over (order by id rows between unbounded preceding and 1 preceding), 0)
from t
order by id;
Here is a db<>fiddle.

MySQL reorder Number values

I have a table with values
ID(PK) MAIN_ID SUB_ID
1 2 2
2 2 3
5 1 1
3 1 3
9 1 4
8 1 5
4 4 2
7 4 3
11 4 4
10 4 6
6 4 7
12 4 8
I want to reorder them in such a way that MAIN_ID's will contain 1 , 2 , 3 , 4 and SUB_IDs will container 1 , 2 , 3 , 4 , 5 etc. Basically i want to remove the missing numbers and make the MAIN_ID and SUB_ID follow numerical order without missing numbers
Desired result as requested
ID(PK) MAIN_ID SUB_ID
1 2 1(Prev 2)
2 2 2(Prev 3)
5 1 1
3 1 2(Prev 3)
9 1 3(Prev 4)
8 1 4(Prev 5)
4 3(Prev 4) 1(Prev 2)
7 3(Prev 4) 2(Prev 3)
11 3(Prev 4) 3(Prev 4)
10 3(Prev 4) 4(Prev 6)
6 3(Prev 4) 5(Prev 7)
12 3(Prev 4) 6(Prev 8)
```
Do you need this:
UPDATE test
JOIN ( SELECT ID,
DENSE_RANK() OVER (ORDER BY MAIN_ID) dr,
ROW_NUMBER() OVER (PARTITION BY MAIN_ID ORDER BY SUB_ID, ID) rn
FROM test ) t USING (ID)
SET test.MAIN_ID = dr, test.SUB_ID = rn;
?
fiddle

Need help to group the available order details table to arrive at the distribution of count(order_line_item) , Qty and count(Qty)

I have a order table with the following information
Order ID, Product ID, Quantity ordered
OID PID Qty
1 10 1
1 20 2
2 10 2
2 40 4
3 50 1
3 20 3
4 30 1
4 90 2
4 90 5
5 10 2
5 20 2
5 70 5
5 60 1
6 80 2
If I run the following query
select `Qty`, count(`Qty`)
from `table`
group by `Qty`
I get the distribution of quantities in the table, which is
Qty count(`Qty`)
1 4
2 6
3 1
4 1
5 2
I want to find the distribution of quantity at order_line_item level too
That is how many orders which have one line item, had items with 1 quantity, 2 quantity and so one, something like
Count(Order_line_item) Qty Count(Qty)
1 2 1
2 1 2
2 2 2
2 3 1
2 4 1
3 1 1
3 2 1
3 5 1
4 1 1
4 2 2
4 5 1
What modification should i make in the above query to achieve this
Try this query
SELECT count_order_line_items, `Qty`, count(*)
FROM (
SELECT count(*) over (partition by `OID`) as count_order_line_items,
`Qty`
FROM Table1
) x
GROUP BY count_order_line_items, `Qty`
Demo: https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=07dfb27a7d434eca1f0b9641aadd53c8
If your mysql version is less than 8 then try this one
SELECT count_order_line_items, `Qty`, count(*)
FROM Table1 t1
JOIN (
SELECT `OID`, count(*) as count_order_line_items
FROM Table1
GROUP BY `OID`
) t2 ON t1.`OID` = t2.`OID`
GROUP BY count_order_line_items, `Qty`
Demo: https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=28c291a4693f31029a103f5c41a97d77

Getting row number based on cate (MySql)

I have the following table and data using a query
SELECT b.*, (#rownum :=#rownum +1) AS row_number
FROM notices b, (SELECT #rownum := 0 ) row
Result:
id cate rownumber
-------------------
1 5 1
2 5 2
3 6 3
4 5 4
5 5 5
6 5 6
7 5 7
I want to output the result below without using row_number() or other function since my rdbms cannot support these.
id cate rownumber
-------------------
1 5 1
2 5 2
3 6 1
4 5 3
5 5 4
6 5 5
7 5 6
To simulate ROW_NUMBER with a partition in MySQL you can simply introduce another session variable to keep track of the cate group value as you iterate over the records in notices.
SET #rn = NULL;
SET #cate = NULL;
SELECT id, cate, rn
FROM
(
SELECT
#rn:=CASE WHEN #cate = cate THEN #rn + 1 ELSE 1 END AS rn,
#cate:=cate,
id,
cate
FROM notices
ORDER BY
cate, id
) t
ORDER BY cate, rn;
Demo here:
Rextester

How to calulate SUM previous and current row value in MySQL table?

My table is "Activity_table", which have 4 columns. How can I create a function, which SUM each Peroson 2 previous and current activities?
My Activity_table
ID PID AID Act
1 1 1 12
2 1 2 32
3 2 1 5
4 1 3 21
5 2 2 12
6 2 3 19
7 1 4 11
8 2 4 6
PID-PersonID; AID-ActivitieID; Act-Activitie Value
My target:
ID PID AID Act SUM
1 1 1 12 12
2 1 2 32 44
3 2 1 5 5
4 1 3 21 65
5 2 2 12 17
6 2 3 19 36
7 1 4 11 64
8 2 4 6 37
Sum1=12; Sum2=32+12; Sum3=5; Sum4=21+32+12; Sum5=12+5; Sum6=19+12+5; Sum7=11+21+32; Sum8=6+19+12; Thank you,
To use the two previous and the current row,
SELECT ID,PID,AID,Act,
(SELECT SUM(Act)
FROM Activity_table
WHERE ID <= a.ID
AND ID >= a.ID - 2
AND PID = a.PID)
FROM Activity_table a;
You are taking the SUM according to the PID right? Then the last two rows of your target should be modified as,
7 1 4 11 **76**
8 2 4 6 **42**
The most flexible query is for your requirement is,
SELECT ID,PID,AID,Act,
(SELECT SUM(Act)
FROM Activity_table
WHERE ID <= a.ID
AND PID = a.PID)
FROM Activity_table a;
Then if you need only the flow of SUM of a particular PID, you can change it like this,
SELECT ID,PID,AID,Act,
(SELECT SUM(Act)
FROM Activity_table
WHERE ID <= a.ID
AND PID = a.PID)
FROM Activity_table a
WHERE PID = 2;
Result:
ID PID AID Act SUM
3 2 1 5 5
5 2 2 12 17
6 2 3 19 36
8 2 4 6 42
Sorry for the previous wrong Answers (deleted).
This produces the correct answer based on your example:
SET #LastPID = 0, #Act1 = 0,#Act2 = 0,#Act3 = 0;
SELECT ID,PID,AID,Act,`SUM` FROM
(
SELECT ID,PID,AID,Act,#Act3 := IF(#LastPID != PID, 0,#Act2),#Act2 := IF(#LastPID != PID, 0,#Act1), #Act1 := Act, #Act1 + #Act2 + #Act3 `SUM`, #LastPID := PID
FROM Activity_table
ORDER BY PID,ID
) sm
ORDER BY ID
;