Mysql latest record for distinct column - mysql

I have a table: invoice
inv_id cus_id due_amt paid total_due
1 71 300 0 300
2 71 200 0 500
3 71 NULL 125 375
4 72 50 0 50
5 72 150 0 200
I want the result
cus_id total_due
71 375
72 200
That is I want the total_due of unique customer or otherwise can say I need the latest invoice details of unique customer.
What I tried:
SELECT cus_id, total_due FROM invoice GROUP BY cus_id ORDER BY inv_id DESC
But this not give the required result.
Please someone can help me..

Try this Query :
SELECT `cus_id` as CustId, (SELECT `total_due` FROM invoice WHERE cus_id = CustId ORDER BY `inv_id` DESC LIMIT 1) as total_due FROM invoice GROUP BY cus_id

create a subquery to get the recent total_due of the customer
SELECT cus_id, (select total_due from invoice where inv_id=max(a.inv_id)) as total_due FROM invoice a GROUP BY cus_id ORDER BY inv_id DESC
Demo here

Try this sample query
SELECT i1.cus_id,i1.total_due FROM invoice as i1
LEFT JOIN invoice AS i2 ON i1.cus_id=i2.cus_id AND i1.inv_id<i2.inv_id
WHERE i2.inv_id IS NULL

Just give a row number based on the group of cus_id and in the descending order of inv_id. Then select the rows having row number 1.
Query
select t1.cus_id, t1.total_due from (
select cus_id, total_due, (
case cus_id when #a
then #b := #b + 1
else #b := 1 and #a := cus_id end
) as rn
from your_table_name t,
(select #b := 0, #a := '') r
order by cus_id, inv_id desc
) t1
where t1.rn = 1
order by t1.cus_id;
Find a demo here

The query:
SELECT cus_id, total_due FROM invoice GROUP BY cus_id ORDER BY inv_id DESC
is invalid SQL because of the total_due column in the SELECT clause.
A query with GROUP BY is allowed to contain in the SELECT clause:
expressions that are also present in the GROUP BY clause;
expressions that use aggregate functions (aka "GROUP BY" functions);
columns that are functionally dependent on columns that are present in the GROUP BY clause.
The expression total_due is neither of the above.
Before version 5.7.5, MySQL used to accept such invalid queries. However, the server was free to return indeterminate values for the invalid expressions. Since version 5.7.5, MySQL rejects such queries (other RDBMSes reject them from long time ago...).
Why is such a query invalid?
Because a GROUP BY query does not return rows from the table. It creates the rows it returns. For each row it puts in the result set it uses a group of rows from the table. All rows in the group have the same values for the expressions present in the GROUP BY clause but they may have distinct values in the other expressions that appear in the SELECT clause.
What's the correct solution for this particular question?
I answered this question many times before on StackOverflow. Take a look at this answer, this answer, this answer or this answer and apply to your query what you learn from there.

Related

Sql query max, group by

I am trying to get all students group by class_id, student_id, teacher_id
SO what I mean is this one :
Select id,class_id, student_id,teacher_id, max(active)
FROM student_classes
GROUP BY class_id, student_id, teacher_id
But this is what I get
Actually what I want as a result is:
114 137 1 47 1
108 138 2 49 0
113 197 3 47 1
So basically the problem is at the third row. Instead of having id = 113 I get ID=111.
What should I do in this case? Can you please help me with the query
As mentioned in the comments, MySQL allows something against the SQL standard, letting you include a non-aggregated column (in this case id) in the select list of a query that includes a group by. As far as I know, it will arbitrarily pick one row in each grouping and display the id value from that row.
If you have a specific rule about which id value you want to see, you need to express that in your query.
By the way, your desired output appears to have multiple typos (e.g. 197, which doesn't appear in your data at all).
From your comment (which you should edit into your original question), and your desired output, I think the rule you want for the id column is:
If there are any rows with active=1 in the group, choose the maximum id value from those rows
If all rows in the group have active=0, choose the minimum id value. (You didn't say this specifically; I'm assuming it based on the presence of 108 on the second row of your desired output.)
I think that this query will produce those results. (And also eliminate the non-standard MySQL behavior.)
SELECT
COALESCE(
MAX(CASE WHEN active=1 THEN id ELSE NULL END),
MIN(id)
) AS some_id
class_id, student_id, teacher_id, max(active)
FROM student_classes
GROUP BY class_id, student_id, teacher_id
MySQL versions 5.5, 5.6 works as you coded. But actually it's not correct. With version 5.7 and higher it will throw error. The error will be like "SELECT list is not in GROUP BY clause and contains nonaggregated column 'student_classes.id'..."
Therefore it seems your DB version is old and maybe this code should work as you wanted
select
---------
min(x.id) as id,
---------
x.class_id,
x.student_id,
x.active
from student_classes x
inner join (select
class_id,
student_id,
teacher_id,
---------
max(active) max_active
---------
from student_classes x
group by class_id, student_id, teacher_id
) y
on x.class_id = y.class_id and
x.student_id = y.student_id and
x.teacher_id = y.teacher_id and
x.active = y.max_active
group by x.class_id, x.student_id, x.active
order by id, class_id, student_id
;
You don't want an aggregation actually, but rather pick particular rows. The rule for picking a row is: Per class_id, student_id, teacher_id get the one with the maximum active and in case of a tie the lowest id. This is a ranking of rows.
As of MySQL 8 you can use a window function like ROW_NUMBER to rank rows:
select *
from
(
select
sc.*,
row_number() over (partition by class_id, student_id, teacher_id
order by active desc, id) as rn
from student_classes sc
) with_wanted_id
where rn = 1;
In older versions you could use NOT EXISTS to exclude rows for which a better row exists:
select *
from student_classes sc1
where not exists
(
select null
from student_classes sc2
where sc2.class_id = sc1.class_id
and sc2.student_id = sc1.student_id
and sc2.teacher_id = sc1.teacher_id
and
(
sc2.active > sc1.active
or
(sc2.active = sc1.active and sc2.id < sc1.id)
)
);

Using SUM for first n rows in each group in SQL

I have one table with two columns (client_id and spent). Each client bought something more than 20 times. I want to sum spendings for each client.
I know that I can use sum and group by to do that, but I want to use sum not for all values in the groups but for only first 10 values. How can I do that?
I have tried with top 10 and limit 10, but It does not work.
I have 10 different clients and more than 200 rows. Clients are not scattered, they are in order, so I have 20 rows on client1 than 20 rows of on client2... Each client appears more than 20 times. I want to use sum and group by, but I do not want to sum all values (more than 20 for each row) , i want to sum first 10 values. Its ordered by time for each client
select client_id, sum(spendings)
from my_table
group by client_id
If you are using Mysql 8.0, You can do this by using window function.
select client_id, sum(spendings)
from (select client_id, spendings, row_number() over (partition by client_id order by <timecol>) rn
from my_table) t
where rn < 11
group by client_id
For MySQL, try this following code-
SELECT client_id,SUM(spendings)
FROM
(
select client_id,
spendings,
#row_num :=IF(client_id = #client_id,#row_num+1,1)AS RowNumber,
#client_id := client_id
FROM my_table
ORDER BY client_id
-- You may need to add here appropriate ordering
-- To get your desired 10 rows for group by in the result
)A
WHERE A.RowNumber < 11
GROUP BY client_id

How to sum when they have same color with sql?

I have this table
i want to ignore productNo and sum all product count accordingly.
select sum(count), max(productNo)
from Table
where date between 117 and 118
group by product
this one gives wrong result...
I want to have sum of counts for each Product-ProductNo combination
try like below
select product,productno,sum(count) as result
from table_name
where productno='X1'
group by product,productno
seems you need the firts rows order by result
select product,productno,sum(count) as result
from table
group by product,productno
order by result
limit 1
Since you haven't tagged any DBMS so, i would use row_number():
select t.*
from (select product, productno, sum(count) as cnt,
row_number() over (partition by product order by sum(count) desc) as seq
from table t
group by product, productno
) t
where seq = 1;
You can also use LIMIT clause (but not for each product) :
select product, productno, sum(count) as cnt
from table t
group by product, productno
order by cnt desc
limit 1;
Some other DBMS requires TOP clause instead of LIMIT clause so, you can change accordingly but the idea would be same.
select sum(count), max(productNo)
from Table
where date between 117 and 118
group by product, productNo
with this it works :)

SQL query to average values only if more than one value, not equal to 0, exits?

I only want to calculate an AVG of values if two or more non-zero values exist for a specific item_id. If two or more non-zero values do not exist, I do not want to calculate the AVG.
The current SQL is below. Perhaps there should be an if clause first?... any ideas?
SELECT AVG( days_since ) AS days_out
FROM (
SELECT days_since
FROM user_123
WHERE item_id = 645
AND days_since <> 0
ORDER BY id DESC
LIMIT 5
) AS recent
Seems like an If clause might work;
IF SELECT COUNT (*), item_id, days_since >= 2
where days_since <>0, then perform AVG...
What about:
SELECT AVG(days_since) AS days_out
FROM User_123
WHERE Item_ID = 645
AND Days_Since <> 0
HAVING COUNT(*) > 1
I'm not sure what the ORDER BY and LIMIT clauses are for, so you'll have to adapt the answer to reimpose the conditions implied by them.
How to keep it working while reapplying Order By and Limit. These are used so that only the 5 most recent entries are used in the Average calculation.
SELECT AVG(days_since) AS days_out
FROM (SELECT days_since
FROM User_123
WHERE Item_ID = 645
AND Days_Since <> 0
ORDER BY ID DESC
LIMIT 5
) AS u
HAVING COUNT(*) > 1;

Most common number in MYSQL SELECT statement

I am trying to get a MYSql statement to spit out the most common number in a field. I believe I am supposed to use COUNT(QUANTITY) but I am confused by which to GROUP BY and ORDER BY, I can't seem to get the correct MODE (Most common number).
*EDIT*
Here is a sample table:
QUANTITY | ORDER_NUMBER
1 51541
4 12351
5 11361
5 12356
6 12565
8 51424
10 51445
25 51485
The MYSql statement should spit out the number 5 because it appears most often
SELECT QUANTITY,COUNT(*)
FROM ...
GROUP BY 1
ORDER BY 2 DESC
LIMIT 1;
SELECT ORDER_NUMBER AS ORDER, COUNT(QUANTITY) as numorders
FROM table
GROUP BY ORDER_NUMBER
ORDER BY numorders
to get the top 10 order_numbers do
select order_number, count(order_number) as quantity
from your_table
group by order_number
order by quantity desc
limit 10
SELECT QUANTITY, COUNT(QUANTITY) AS TOTAL_Q
FROM MYTABLE
GROUP BY QUANTITY
ORDER BY TOTAL_Q DESC
this will give you number of quanity from most to least number....