How to display rows in table but skip multiple rows selection using MYSQL database - mysql

i have one table like this :
Balance Table
id
date
item
desc
debit
credit
1
2021-07-01
june's balance
50
2
2021-07-01
Apple
sell
10
3
2021-07-01
Lemon
buy
10
4
2021-07-05
Strawberry
sell
75
5
2021-07-05
Strawberry
sell
25
6
2021-07-12
Blueberry
buy
10
7
2021-08-01
july's Balance
140
8
2021-08-01
Apple
sell
25
9
2021-08-02
Strawberry
buy
5
10
2021-08-08
Lemon
sell
25
11
2021-08-27
Lemon
sell
35
12
2021-08-30
Blueberry
buy
20
13
2021-09-01
aug's Balance
200
14
2021-09-01
Lemon
sell
20
15
2021-09-07
Blueberry
sell
20
16
2021-09-07
Strawberry
sell
20
17
2021-09-20
Lemon
sell
20
18
2021-09-20
Blueberry
sell
20
then I added a balance column for the monthly data with query like this :
select t.date, t.item, t.desc, t.debit, t.credit,
(#s := #s + t.debit - t.credit) as balance
from balance t cross join
(select #s := 0) p where MONTH(t.date) = 7 //for july
order by t.date
and the result is as follows :
July results
date
item
desc
debit
credit
balance
2021-07-01
June's Balance
50
50
2021-07-01
apple
sell
10
60
2021-07-01
lemon
buy
10
50
2021-07-05
strawberry
sell
75
125
2021-07-06
strawberry
sell
25
150
2021-07-12
blueberry
buy
10
140
August results
date
item
desc
debit
credit
balance
2021-08-01
July's Balance
140
140
2021-08-01
apple
sell
25
165
2021-08-02
strawberry
buy
5
160
2021-08-08
lemon
sell
25
185
2021-08-27
lemon
sell
35
220
2021-08-30
blueberry
buy
20
200
and September results
date
item
desc
debit
credit
balance
2021-09-01
Aug's Balance
200
200
2021-09-01
lemon
sell
20
220
2021-09-07
blueberry
sell
20
240
2021-09-07
strawberry
sell
20
260
2021-09-20
lemon
sell
20
280
2021-09-20
blueberry
sell
20
300
the question is, how to get a table like below, where the desc row used is only June's Balance without July's Balance and Aug's Balance.. thanks for the help
date
item
desc
debit
credit
balance
2021-07-01
June's Balance
50
50
2021-07-01
apple
sell
10
60
2021-07-01
lemon
buy
10
50
2021-07-05
strawberry
sell
75
125
2021-07-06
strawberry
sell
25
150
2021-07-12
blueberry
buy
10
140
2021-08-01
apple
sell
25
165
2021-08-02
strawberry
buy
5
160
2021-08-08
lemon
sell
25
185
2021-08-27
lemon
sell
35
220
2021-08-30
blueberry
buy
20
200
2021-09-01
lemon
sell
20
220
2021-09-07
blueberry
sell
20
240
2021-09-07
strawberry
sell
20
260
2021-09-20
lemon
sell
20
280
2021-09-20
blueberry
sell
20
300

I partially loaded up a table in db-fiddle.com.
This will display the running balances with whatever starting month you choose ('2021-07-01' in this case):
select t.date,
ifnull(t.item, '') as item,
t.desc,
ifnull(t.debit, 0) as debit,
ifnull(t.credit, 0) as credit,
(#s := #s + ifnull(t.debit, 0) - ifnull(t.credit, 0)) as balance
from balance t cross join (select #s := 0) p
where t.date >= '2021-07-01' and (t.date = '2021-07-01' or t.item is not null)
order by t.date
date
desc
item
debit
credit
balance
2021-07-01
june's balance
50
0
50
2021-07-01
sell
Apple
10
0
60
2021-07-01
buy
Lemon
0
10
50
2021-07-05
sell
Strawberry
75
0
125
2021-08-01
sell
Apple
25
0
150
2021-08-02
buy
Strawberry
0
5
145
2021-08-08
sell
Lemon
25
0
170

You could adapt your where clause to exclude all Balances that aren't in the start date.
But your concept doesn't include years, which you must include, when the considered time line goes over a year
select t.`date`, t.item, t.desc, t.debit, t.credit,
(#s := IFNULL(#s,0) + IFNULL(t.debit,0) - IFNULL(t.credit,0)) as balance
from balance t cross join
(select #s := 0) p
where (`desc` LIKE '% Balance' AND MONTH(t.date) = (SELECT MIN(MONTH(`date`)) FROM balance)) or `desc` NOT LIKE '% Balance'
order by t.`date`
date | item | desc | debit | credit | balance
:--------- | :--------- | :------------- | :---- | :----- | ------:
2021-07-01 | null | june's balance | 50 | null | 50
2021-07-01 | Apple | sell | 10 | null | 60
2021-07-01 | Lemon | buy | null | 10 | 50
2021-07-05 | Strawberry | sell | 75 | null | 125
2021-07-05 | Strawberry | sell | 25 | null | 150
2021-07-12 | Blueberry | buy | null | 10 | 140
2021-08-01 | Apple | sell | 25 | null | 165
2021-08-02 | Strawberry | buy | null | 5 | 160
2021-08-08 | Lemon | sell | 25 | null | 185
2021-08-27 | Lemon | sell | 35 | null | 220
2021-08-30 | Blueberry | buy | null | 20 | 200
2021-09-01 | Lemon | sell | 20 | null | 220
2021-09-07 | Blueberry | sell | 20 | null | 240
2021-09-07 | Strawberry | sell | 20 | null | 260
2021-09-20 | Lemon | sell | 20 | null | 280
2021-09-20 | Blueberry | sell | 20 | null | 300
db<>fiddle here

Related

how to perform delete in mysql table using avg() of another table?

table_a
user_id score
1 0.33
2 0.34
3 0.35
11 0.90
88 0.80
7 0.10
8 0.11
10 0.09
12 0.80
17 0.80
18 0.80
19 0.80
20 0.80
table_b
user_id canon_id
1 1000
2 1000
3 1000
11 4344
88 4344
7 2023
8 2023
10 2023
12 3333
17 3333
18 3333
19 3333
20 3333
In the above case, how can I delete records from table_b where associated table_a.user_ids from table_b.canon_id have avg(score) < 0.50. In this case, canon_id 2023 and associated user_ids 7,8,10 avg(score) is 0.10 hence it should get deleted.
Join table_b to a query that returns all the canon_ids with associated with average < 0.5:
delete b
from table_b b inner join (
select b.canon_id
from table_b b inner join table_a a
on a.user_id = b.user_id
group by b.canon_id
having avg(a.score) < 0.5
) t on t.canon_id = b.canon_id;
See the demo.
Results:
| user_id | canon_id |
| ------- | -------- |
| 11 | 4344 |
| 88 | 4344 |
| 12 | 3333 |
| 17 | 3333 |
| 18 | 3333 |
| 19 | 3333 |
| 20 | 3333 |

get the data for last month if next month no data is there

I want to get the data of last month if no data is there in the next month.
suppose data is:
stock_item month stock
pepsi 4 100
pepsi 5 120
pepsi 9 80
coca cola 4 90
coca cola 6 100
coca cola 8 120
the output should be like:
stock_item month stock
pepsi 4 100
pepsi 5 120
pepsi 6 120
pepsi 7 120
pepsi 8 120
pepsi 9 80
pepsi 10 80
coca cola 4 90
coca cola 5 90
coca cola 6 100
coca cola 7 100
coca cola 8 120
coca cola 9 120
coca cola 10 120
Month is between 4 and 10 because indian fiscal year starts in April and current month is October, so 10.
Here's a partial answer for how to accomplish this for a single stock.
First, create a table for the months you want and populate it. This gives us something to iterate over.
create table stock_months (
int month_num not null
);
insert into stock_months values (4), (5), (6), (7), (8), (9), (10);
Now we can select months from this table and left outer join with stocks to get all months.
select stock_item, month_num, stock
from stock_months sm
left outer join stocks s
on sm.month_num = s.month and s.stock_item = 'pepsi'
+------------+-----------+-------+
| stock_item | month_num | stock |
+------------+-----------+-------+
| pepsi | 4 | 100 |
| pepsi | 5 | 120 |
| pepsi | 9 | 80 |
| NULL | 6 | NULL |
| NULL | 7 | NULL |
| NULL | 8 | NULL |
| NULL | 10 | NULL |
+------------+-----------+-------+
We can use variables to remember the values of the previous row.
select
case when stock_item is null then
#stock_item
else
#stock_item := stock_item
end as stock_item,
month_num,
case when stock is null then
#stock
else
#stock := stock
end as stock
from stock_months sm
left outer join stocks s
on sm.month_num = s.month and s.stock_item = 'pepsi'
order by month_num;
+------------+-----------+-------+
| stock_item | month_num | stock |
+------------+-----------+-------+
| pepsi | 4 | 100 |
| pepsi | 5 | 120 |
| pepsi | 6 | 80 |
| pepsi | 7 | 80 |
| pepsi | 8 | 80 |
| pepsi | 9 | 80 |
| pepsi | 10 | 80 |
+------------+-----------+-------+
Why didn't that work? Because the select iterates through the results of the join in whatever order it likes. Then it sorts. In this case it did the rows with stock_item first.
We need to sort the joined rows first. We can do that by selecting from a subselect which is already in order.
select
case when stock_item is null then
#stock_item
else
#stock_item := stock_item
end as stock_item,
month_num,
case when stock is null then
#stock
else
#stock := stock
end as stock
from (
select stock_item, month_num, stock
from stock_months sm
left outer join stocks s
on sm.month_num = s.month and s.stock_item = 'pepsi'
order by month_num
) stocks;
+------------+-----------+-------+
| stock_item | month_num | stock |
+------------+-----------+-------+
| pepsi | 4 | 100 |
| pepsi | 5 | 120 |
| pepsi | 6 | 120 |
| pepsi | 7 | 120 |
| pepsi | 8 | 120 |
| pepsi | 9 | 80 |
| pepsi | 10 | 80 |
+------------+-----------+-------+
I'm not sure how to go from here to all stock items.

mysql grouped ranking with ties

so i have data in a table like this:
id total group_id
1897 738 1
2489 716 2
2325 715 3
1788 702 2
1707 699 3
2400 688 3
2668 682 2
1373 666 1
1494 666 1
1564 660 1
2699 659 1
1307 648 4
1720 645 4
2176 644 1
1454 644 4
2385 639 3
1001 634 2
2099 634 4
1006 632 1
2587 630 3
1955 624 3
1827 624 4
2505 623 4
2062 621 3
1003 618 1
2286 615 4
2722 609 4
how can i rank the ids per group based on the total and giving the same rank when there is a tie?
i have tried this solution below but it doesnt take care of the ties.
SELECT g1.admission_no
, g1.total
, g1.stream_id
, COUNT(*) AS rn
FROM ranktest AS g1
JOIN ranktest AS g2
ON (g2.total, g2.admission_no) >= (g1.total, g1.admission_no)
AND g1.stream_id = g2.stream_id
GROUP BY g1.admission_no
, g1.stream_id
, g1.total
ORDER BY g1.stream_id
, total ;
expected
id total group_id rank
1897 738 1 1
2489 716 2 1
2325 715 3 1
1788 702 2 2
1707 699 3 2
2400 688 3 3
2668 682 2 3
1373 666 1 2
1494 666 1 2
1564 660 1 3
2699 659 1 4
1307 648 4 1
1720 645 4 2
2176 644 1 4
1454 644 4 3
2385 639 3 4
1001 634 2 4
2099 634 4 4
1006 632 1 5
2587 630 3 5
1955 624 3 6
1827 624 4 5
2505 623 4 6
2062 621 3 6
1003 618 1 6
2286 615 4 7
2722 609 4 8
If original order is not very important you can start from:
http://sqlfiddle.com/#!9/a15a2/10
SELECT
ranktest.*,
IF(#rank IS NULL,#rank:=1, IF(#prev!=group_id,#rank:=1,#rank:=#rank+1) ) rank,
#prev:=group_id
FROM ranktest
ORDER BY group_id, total
but keep in mind that this is not very efficient query from performance perspective.
From the MySQL Manual Page entitled User-Defined Variables:
In the following statement, you
might think that MySQL will evaluate #a first and then do an
assignment second:
SELECT #a, #a:=#a+1, ...;
However, the order of evaluation for expressions involving user
variables is undefined.
This is why so few people write these answers correctly and safely. The object is not to hit the desired results once, but to do so again and again in real-world environments using best practices and guaranteed results.
If you don't read the Obligatory Doc, and implement it, your results are not guaranteed. There is no lazy way to do this and one shouldn't even bother :p
select id,total,group_id,rank
from
( select id,total,group_id,
#rn:=if( group_id!=#curr_grp, greatest(#grp_rank:=1,0),
if(total=#prev_grp_total,#grp_rank,greatest(#grp_rank:=#grp_rank+1,0) ) ) as rank,
#curr_grp:=group_id,
#prev_grp_total:=total
from trans01
cross join (select #grp_rank:=0,#curr_grp:=0,#prev_grp_total:=-1) xParams
order by group_id,total desc
)xDerived;
+------+-------+----------+------+
| id | total | group_id | rank |
+------+-------+----------+------+
| 1897 | 738 | 1 | 1 |
| 1373 | 666 | 1 | 2 |
| 1494 | 666 | 1 | 2 |
| 1564 | 660 | 1 | 3 |
| 2699 | 659 | 1 | 4 |
| 2176 | 644 | 1 | 5 |
| 1006 | 632 | 1 | 6 |
| 1003 | 618 | 1 | 7 |
| 2489 | 716 | 2 | 1 |
| 1788 | 702 | 2 | 2 |
| 2668 | 682 | 2 | 3 |
| 1001 | 634 | 2 | 4 |
| 2325 | 715 | 3 | 1 |
| 1707 | 699 | 3 | 2 |
| 2400 | 688 | 3 | 3 |
| 2385 | 639 | 3 | 4 |
| 2587 | 630 | 3 | 5 |
| 1955 | 624 | 3 | 6 |
| 2062 | 621 | 3 | 7 |
| 1307 | 648 | 4 | 1 |
| 1720 | 645 | 4 | 2 |
| 1454 | 644 | 4 | 3 |
| 2099 | 634 | 4 | 4 |
| 1827 | 624 | 4 | 5 |
| 2505 | 623 | 4 | 6 |
| 2286 | 615 | 4 | 7 |
| 2722 | 609 | 4 | 8 |
+------+-------+----------+------+
came up with this answer after googling a bit..not sure is its the best but it works for my case:
SELECT id, group_id, total,
#std:=CASE WHEN #grp <> group_id THEN concat(left(#grp:=group_id, 0), 1) ELSE if(#prev=total,#std,#std+1) END AS rn,#prev:=total
FROM
(SELECT #std:= -1) s,
(SELECT #grp:= -1,#prev:=null) c,
(SELECT *
FROM table
ORDER BY group_id, total desc
) s

Pivot table query in mysql

Is there a way to do a query in mysql with this scenario.
i have a table
group_name | cost_period | books_cost | others_cost
group_A | 1/01/2015 | 100 | 200
group_A | 1/02/2015 | 56 | 86
group_A | 1/01/2015 | 22 | 222
group_A | 1/03/2015 | 30 | 40
group_B | 1/02/2015 | 50 | 10
group_B | 1/02/2015 | 45 | 10
group_B | 1/01/2015 | 22 | 15
group_C | 1/02/2015 | 45 | 20
and i want it to have this format after the query
JAN-2015 FEB-2015 MAR_2015 total
group_A
sum of book cost 122 56 30 208
sum of others cost 422 86 40 548
group_B
sum of book cost 22 95 0 117
sum of others cost 15 20 0 45
group_C
sum of book cost 0 45 0 45
sum of others cost 0 20 0 20
is there a way this is possible by a query to avoid using a pivot tool once data is gathered? since jasper's crosstabs are not that flexible
thanks

Dynamic Cross Tab in MySQL

I have the following table with the following data
Year | Age Group | Male | Female
2000 | 0 - 25 | 50 | 100
2000 | 26 above | 40 | 75
2001 | 0 - 25 | 150 | 86
2001 | 26 above | 65 | 83
I would like to create a cross tab in the following format
| Male | Female
2000 | 90 | 175
0 - 25 | 50 | 100
26 above | 40 | 75
2001 | 215 | 169
0 - 25 | 150 | 86
26 above | 65 | 83
I will be very grateful for your assistance.
SQLFiddle example:
select * from
(
select year,ageGroup,male,female from t
union all
select year,'' ageGroup,sum(male) male,sum(female) female
from t group by year
) st
order by year,agegroup