Get the first n lines for group after summing up - mysql

I am practising my skills in MySQL using the Sakila DB.
I would I have created a view called rentals_customer_store_film_category with is the union of customers, payments, rentals, film, and category tables.
I would now like to get the top 5 films by income. Meaning that I would lie to sum up all incomes of each film by store and then return the first 5.
I tried the below code but it does not work.
I cannot figure out what is wrong with it
Any help?
SELECT store_id, film_id, income
FROM
(SELECT film_id, store_id, sum(amount) as income,
#store_rank := IF(#current_store = store_id, #store_rank + 1, 1) AS store_rank,
#current_store := store_id
FROM rentals_customer_store_film_category
group by store_id, film_id
ORDER BY store_id, income DESC, film_id
) ranked
WHERE store_rank <= 5
RESULTS BELOW. As you can see, it does not stop at the fifth row per store. It shows all films by store while I would like only the top 5 for store:id 1 and top 5 for store id 2.
store_id film_id income
1 971 134.82
1 879 132.85
1 938 127.82
1 973 123.83
1 865 119.84
1 941 117.82
1 267 116.83
1 327 110.85
1 580 106.86
1 715 105.85
1 897 104.85
...
...
...
...
2 878 127.83
2 791 127.82
2 854 123.83
2 946 117.86
2 396 113.81
2 369 111.84
2 764 110.85
2 260 110.84
2 838 110.82
2 527 109.83
2 893 106.84
2 71 102.87
2 8 102.82
...
...
...
...

The order in this case is important to compare the previous store_id with the current,try this:
SELECT store_id, film_id, income
FROM
(SELECT film_id, store_id, sum(amount) as income,
#First compare previus with current
#store_rank := IF(#prev_store = store_id, #store_rank + 1, 1) AS store_rank,
#asign previus store
#prev_store := store_id
FROM films
group by store_id, film_id
ORDER BY store_id, income DESC, film_id
) ranked
WHERE store_rank <= 5

Related

SQL sum of maxes of each occurrence

I have the following table:
category id date views
1 3 5-1-17 40
1 3 5-2-17 70
1 3 5-3-17 110
1 3 5-4-17 200
1 5 5-1-17 50
1 5 5-2-17 75
2 6 4-1-17 90
2 6 4-2-17 95
2 9 4-3-17 115
2 9 6-5-17 125
2 9 6-6-17 135
How do I sum the max views for each id by category?
The resulting pull should look like this:
category views
1 275
2 230
For category 1, the max views for id 3 is 200 and the max views for id 5 is 75. The sum for category 1 is thus 275.
For category 2, the max views for id 6 is 95 and the max views for id 9 is 135. The sum for category 2 is thus 230.
You can use two levels of aggregation:
select category, sum(max_views)
from (select category, id, max(views) as max_views
from t
group by category, id
) t
group by category;
You can also use row_number() here:
select category, sum(max_views)
from (select t.*,
row_number() over (partition by category, id, order by views desc) as seqnum
from t
) t
where seqnum = 1
group by category;
It would be interesting which is faster. I would vote on the double aggregation, but that might not always be true.

MySQL how to count duplicate items on invoice?

Consider the following table:
id, invoice_number, item_number, qty, price
1 5000 200 1 4.50
2 5000 201 2 5.50
3 5000 201 1 5.75
2 5000 202 1 5.50
3 5000 202 1 5.75
4 5000 203 1 6.00
How can I get the following result:
invoice, item, count
5000, 200, 1
5000, 201, 2
5000, 202, 2
5000, 203, 1
I know I can group by the invoice number, but then I cannot simply count the invoices, or I will lose their name. Additionally I would love to know if they have a different price, even if it was just a flag, however I am thinking to get that level of detail I might as well put this in a script and loop through the items, its just that my data set is very large, and id love to do it with pure SQL :)
Thanks in advance!
To get your exact desired output:
select invoice_number, item_number, count(*)
from tbl
group by invoice_number, item_number
To get your desired output along with the number of unique prices within the group, and what those prices are:
select invoice_number,
item_number,
count(*) as num_rows,
count(distinct price) as num_prices,
group_concat(distinct price) as prices
from tbl
group by invoice_number, item_number

MySQL sum amounts for transactions related to loans, in reverse order

I'm trying to add an amount living in a transactions table, but the adding must be done taking the transactions in reversed order, grouped by a loan they belong to. Let me put some examples:
Transactions
tid loanid amount entrydate
------------------------------------
1 1 1,500 2013-06-01
2 2 1,500 2013-06-01
3 1 1,000 2013-06-02
4 3 2,300 2013-06-04
5 5 2,000 2013-06-04
6 1 1,100 2013-06-07
7 2 1,000 2013-06-09
| | | |
Loans
loanid
------
1
2
3
4
5
|
As you can see, there's no transactions for loanid 4, just to make clear the point that there's no obligation for the transactions to exist for every loan.
Now, what I'm trying to achieve is to sum up the amounts for the transactions of each loan. This first approach achieves this:
SELECT tr.tid,
l.loanid,
tr.entrydate,
tr.amount,
#prevLoan:=l.loanid prevloan,
#amnt:=if(#prevLoan:=l.loanid, #amnt+tr.amount, tr.amount) totAmnt
FROM (SELECT * FROM Transactions) tr
JOIN (SELECT #prevLoan:=0, #amnt:=0) t
JOIN Loans l
ON l.loanid = tr.loanid
GROUP BY l.loanid, tr.tid
Which achieves something like this:
tid loanid entrydate amount prevloan totAmnt
-----------------------------------------------------------
1 1 2013-06-01 1,500 1 1,500
3 1 2013-06-02 1,000 1 2,500
6 1 2013-06-07 1,100 1 3,600 <-- final result for loanid=1
2 2 2013-06-01 1,500 2 1,500
7 2 2013-06-09 1,000 2 2,500 <-- final result for loanid=2
4 3 2013-06-04 2,300 3 2,300 <-- final result for loanid=3
5 5 2013-06-04 2,000 5 2,000 <-- final result for loanid=5
| | | | | |
As you can see, for each loan, the amount of the transactions belonging to it are getting sumed up in the totAmnt column, so the last transaction of each loan has the total sum of the transactions for the same loan.
Now.... what I actually need is something to get the sum to be done in reversed order. I mean, for the same transactions of each loan, the sum still gets the same result, but I need the sum to be done from the last transaction of each loan up to the first one.
I've tried the following, but to no avail (it's the same query as the last one, but with an Order By DESC on the FROM transactions table):
SELECT tr.tid,
l.loanid,
tr.entrydate,
tr.amount,
#prevLoan:=l.loanid prevloan,
#amnt:=if(#prevLoan:=l.loanid, #amnt+tr.amount, tr.amount) totAmnt
FROM (SELECT * FROM Transactions ORDER BY tr.entrydate DESC) tr
JOIN (SELECT #prevLoan:=0, #amnt:=0) t
JOIN Loans l
ON l.loanid = tr.loanid
GROUP BY l.loanid, tr.tid
I'm using tr.entrydate because is a more familiar way to say the order criteria, and besides that's what policy says is the valid order criteria, tid may say something but entrydate is the ordering column of the Transactions table...
Using the previous query, I just get the same results I get with the first query, so I guess something must be missing there. What I need is to get results as the following:
tid loanid entrydate amount prevloan totAmnt
-----------------------------------------------------------
6 1 2013-06-07 1,100 1 1,100
3 1 2013-06-02 1,000 1 2,100
1 1 2013-06-01 1,500 1 3,600 <-- final result for loanid=1
7 2 2013-06-09 1,000 2 1,000
2 2 2013-06-01 1,500 2 2,500 <-- final result for loanid=2
4 3 2013-06-04 2,300 3 2,300 <-- final result for loanid=3
5 5 2013-06-04 2,000 5 2,000 <-- final result for loanid=5
| | | | | |
As you can see the sum for each loanid gets the same final result, but the sum is done for the transactions in reversed order...
Hope all this mess is clear... How can I achieve such a result?
You appear to be VERY close... I think you have two small adjustments. First, dont use the outer GROUP BY as you are not doing any aggregations (sum, min, max, avg, etc). Second, when querying your transaction table, just order that by the loan ID FIRST, THEN the date descending... This way all the loan IDs are in proper order grouped together, but within each loan, THEY are sorted descending order as you are looking for. Also, adjust your #prevLoan AFTER you have accumulated so the current record can be compared to the next. You are starting the #variable to zero so it won't match the first loan ID on the first run anyhow. Finally, you don't even need the join to the loan table since the transaction table has the loan ID to use as basis of comparison test. Since the inner-most query is ordered by loan and then entry date descending, you should not need it again at the outer query.
SELECT
tr.tid,
tr.loanid,
tr.entrydate,
tr.amount,
#amnt := if( #prevLoan = tr.loanid, #amnt+tr.amount, tr.amount) totAmnt,
#prevLoan := tr.loanid prevloan
FROM
( SELECT *
FROM Transactions
ORDER BY loanid, entrydate DESC) tr
JOIN (SELECT #prevLoan := 0,
#amnt := 0) t
Alternate Solution?? Per my comment, it looks like you want the high totals and shrinking down... Is this closer?
SELECT
tr.tid,
tr.loanid,
tr.entrydate,
tr.amount,
trTotals.TotalLoans - if( #prevLoan = tr.loanid, #amnt, 0 ) as NewBal,
#amnt := if( #prevLoan = tr.loanid, #amnt+tr.amount, tr.amount) runningTotals,
#prevLoan := tr.loanid prevloan
FROM
( SELECT *
FROM Transactions
ORDER BY loanid, entrydate DESC) tr
JOIN ( SELECT loanid, sum( amount ) as TotalLoans
FROM Transactions
group by loanid ) trTotals
on tr.loanid = trTotals.loanid
JOIN (SELECT #prevLoan := 0,
#amnt := 0) t
Produces... (Total Paid) (for reversing calc)
tid loanid entrydate amount NewBal Running Totals prevLoan
6 1 2013-06-07 1100 3600 1100 1
3 1 2013-06-02 1000 2500 2100 1
1 1 2013-06-01 1500 1500 3600 1
7 2 2013-06-09 1000 2500 1000 2
2 2 2013-06-01 1500 1500 2500 2
4 3 2013-06-04 2300 2300 2300 3
5 5 2013-06-04 2000 2000 2000 5

How to make a fake column with an autoincrement number in a "group by" query

I have data in a table like this:
fgid qty ntid
1 100 10
2 90 10
6 200 11
1 80 11
1 120 12
6 100 12
6 30 13
And i make query :
SELECT fgid, SUM(qty) AS total_qty, COUNT(ntid) AS nt_count FROM sofg
GROUP BY fgid
AND the result is :
fgid total_qty nt_count
1 300 3
2 90 1
6 330 3
Then i want to make the result like this :
no fgid total_qty nt_count
1 1 300 3
2 2 90 1
3 6 330 3
How to do that with a query? where 'no' is (like) autoincrement number.
Try this query.
SELECT
#rownum := #rownum + 1 rownum,
t.*
FROM (SELECT #rownum:=0) r,
(
SELECT fgid, SUM(qty) AS total_qty, COUNT(ntid) AS nt_count FROM sofg GROUP BY fgid
) t;
Basically the same as Dhinakaran's answer, but there's no need to put the whole main query into a subquery. There's no difference to his answer appart from maybe being more pleasing to the eye, but please accept Dhinakaran's answer, as he was faster.
SELECT
#rownum:=#rownum + 1 as rownumber,
fgid,
SUM(qty) AS total_qty,
COUNT(ntid) AS nt_count
FROM sofg
, (select #rownum:=0) v
GROUP BY fgid

SQL query "Find the article that has generated most income?"

I want to make a SQL query that shows me the article that generated most income. (in a shop)
Table = orderrader
rownumber articlenumber ordernumber price amount
1 11 1 8624 3
2 6 1 4794 2
3 17 1 8755 3
4 1 1 7803 1
5 16 1 8987 3
6 10 1 4575 3
7 4 1 8747 1
8 15 1 5439 3
9 11 2 8624 3
10 1 2 7803 1
Following sql statement will return only one articlenumber with max revenue.
Select articlenumber, sum(price*amount) as totalincome
from orderrader
group by articlenumber
order by sum(price*amount) desc LIMIT 1
SELECT articlenumber
FROM orderrader
WHERE (price * amount) = (SELECT MAX(price * amount) FROM orderrader)
This should do the trick, i checked it on my own database. It will give ONLY the one with the highest price*amount
SELECT articlenumber, SUM(price*amount) AS income
FROM table
GROUP BY articlenumber
ORDER BY income DESC
select articlenumber, sum(price*amount) as s from orderrader group by articlenumber order by s desc;