sql select max for each grouped column and rest of row - mysql

I have a table in mysql
name year data
a 1 1
b 1 2
c 2 3
a 2 4
c 3 5
For each year I need the max(data), year, and name associated with that max.
ive tried
select max(data), name , year from table group by year; however I do not have access to name.
Thank you in advance.

I think you can try something like below
select name, data, year from table A
join (select max(data) data, year from table a
group by year) B on A.data = B.data and A.year = B.year

Related

Join on SQL using the most recent data instead of the equal one

I need to join 2 dataframes using month and name. However, in one of these dataframes I don't have all the monthly result, so I want to repeat the most recent one.
For example,
Dataframe A
name score month
Alex 20 2020/01
Alex 30 2020/03
Dataframe B
name month tenure
Alex 2020/01 1
Alex 2020/02 2
Alex 2020/03 3
Join A+B using name and month - expected result
name month score tenure
Alex 2020/01 20 1
Alex 2020/02 20 2 --> repeat the score from the most recent date
Alex 2020/03 30 3
Does someone know how can I do that?
You can use a correlated subquery:
select b.*,
(select a.score
from a
where a.name = b.name and a.month <= b.month
order by a.month desc
limit 1
) as score
from b;
Or, you can use window functions and a join:
select b.*, a.score
from b left join
(select a.*,
lead(month) over (partition by name order by month) as next_month
from a
) a
on b.name = a.name and
b.month >= a.month and
(b.month < a.next_month or a.next_month is null);
This method is convenient if you want to fetch multiple columns from a.

how to create a table like this by mysql

I have a table like this:
ID Payment year
A 10 1
A 15 2
A 12 3
B 11 2
B 15 4
C 25 1
C 17 3
I'm looking for a query that returns row for each ID for the its last year. The year column is ordered increasing for each ID.
I need a result like this:
ID Payment year
A 12 3
B 15 4
C 17 3
I have this query so far:
select ID, Payment, Year from payment_table
where year = (select max(year) from ?????????);
I don't know what shall I write instead of ????????
It would be appreciated If anybody gives me some idea.
Use subquery :
select t.*
from table t
where year = (select max(t1.year) from table t1 where t1.id = t.id);

Sum of values from 2 columns from 2 tables AND sorting by a column

I have 2 tables:
Table "credits":
id | amount | type
1 8 1
2 7 2
3 2 1
4 1 1
5 5 3
6 4 2
and
Table "debits":
id | amount
1 3
1 2
3 2
4 1
5 3
5 1
I need to get the sum of all "id's" balances (credit-debit) and grouping it by "type". So far I have this:
SELECT id, SUM(amount) as balance,
FROM
(
SELECT id, amount FROM credits
UNION ALL
SELECT id, -amount FROM debits
)
unified_table
GROUP BY id
But it just gives me the "id's" balances:
id | balance
1 3
2 7
3 0
4 0
5 1
6 4
Ideally, I need something like this:
type | balance
1 3
2 11
3 1
I tried to add the "type" column in the first "select" of the union, and then group by "type". But not working I think because table "debits" dont have column "type". How can I accomplish this? Thank you for your help
I think this would do it:
SELECT c.type, sum(c.amount - IFNULL(d.amount,0))
FROM credits c LEFT OUTER JOIN (SELECT id, sum(amount) FROM debits GROUP BY id) d
ON c.id=d.id
GROUP BY c.type
The idea is to group the debits table first, and then join it with the credits table, which will result in a table that you can group by type
Try this:
SELECT Type, Sum(Amount)
FROM (
SELECT C.Amount - ISNULL(D.Amount, 0) AS Amount, C.Type
FROM Credits C
LEFT JOIN (SELECT Id, Sum(Amount)
FROM Debits
GROUP BY ID) D ON C.Id = D.Id
) A
GROUP BY A.Type
Here is my solution:
SELECT
credits.`type`,
credits.`amount` - IFNULL(t_debit.`d_amount`, 0) AS balance
FROM
credits,
(SELECT id, SUM(amount) AS d_amount FROM debits GROUP BY id)t_debit
WHERE
credits.`id` = t_debit.`id`
GROUP BY
credits.`type`;
First I select sum of amounts from debits table group by id and after I did another select query on the credits table where credit id match to debit id. I don't use UNION operator because the id's column in debits table is an foreign key.

Get product total sales per moth, with 0 in the gaps

I have been stuck in a recent problem with a SQL Query. What I'm trying to archieve is to get each product in the store and show how many of them has been sold each month. However, sometimes there are some months where these products were not sold, which means they won't be displayed.
For instance, this is the result I'm getting right now
Article Month Sold
CN140027 6 312
CN140027 7 293
CN140027 12 122
CN140186 1 10
CN140186 4 2
While I want to get something more like this
Article Month Sold
CN140027 6 312
CN140027 7 293
CN140027 8 0
CN140027 9 0
CN140027 10 0
CN140027 11 0
CN140027 12 122
CN140186 1 10
CN140186 2 0
CN140186 3 0
CN140186 4 2
And here is the query I'm using at the moment
SELECT k.artikelnr, Months.datefield as `Months`, IFNULL(SUM(k.menge),0) as `Quantity`
FROM store_shop_korb as k LEFT OUTER JOIN office_calendar AS Months
ON Months.datefield = month(k.date_insert)
WHERE k.date_insert BETWEEN "2014-12-01" AND "2015-12-31"
group by k.artikelnr, Months.datefield
What am I missing? Or what am I doing wrong? Any help is really appreciated.
Thanks in advance.
EDIT:
Additional information:
office_calendar is the calendar table. It only contains the months as registry, from 1 to 12.
Additionally, I'm taking the article/product ID from a table called 'store_shop_korb', which contains all the lines of a made order (so it contains the article ID, its price, the quantity for each order..)
This works for me:
SELECT k.artikelnr, c.datefield AS `Month`, COALESCE(s.Quantity, 0) AS Sold
FROM (
SELECT artikelnr
FROM store_shop_korb
GROUP BY artikelnr
) k
JOIN office_calendar c
LEFT JOIN (
SELECT artikelnr, MONTH(date_insert) AS monthfield, SUM(menge) AS Quantity
FROM store_shop_korb
GROUP BY artikelnr, MONTH(date_insert)
) s ON k.artikelnr = s.artikelnr AND c.datefield = s.monthfield
ORDER BY k.artikelnr, c.datefield
If you have a table of articles, you can use it in the place of subquery k. I'm basically normalizing on the fly.
Explanation:
There's basically 3 sets of data that get joined. The first is a distinct set of articles (k), the second is a distinct set of months (c). These two are joined without restriction, meaning you get the cartesian product (every article x every month). This result is then left-joined to the sales per month (s) so that we don't lose 0 entries.
Add another where condition , i think it will solve your problem
SELECT k.artikelnr, Months.datefield as `Months`, IFNULL(SUM(k.menge),0) as `Quantity`
FROM store_shop_korb as k LEFT OUTER JOIN office_calendar AS Months
ON Months.datefield = month(k.date_insert)
WHERE IFNULL(SUM(k.menge),0)>0 AND k.date_insert BETWEEN "2014-12-01" AND "2015-12-31"
group by k.artikelnr, Months.datefield
I have tried this in MSAccess and it seems to work OK
SELECT PRODUCT, CALENDAR.MONTH, A
FROM CALENDAR LEFT JOIN (
SELECT PRODUCT, MONTH(SALEDTE) AS M, SUM(SALEAMOUNT) AS A
FROM SALES
WHERE SALEDTE BETWEEN #1/1/2015# AND #12/31/2015#
GROUP BY PRODUCT, MONTH(SALEDTE) ) AS X
ON X.M = CALENDAR.MONTH
If you already have a calender table then use this.
SELECT B.Article,
A.Month,
COALESCE(c.Sold, 0)
FROM (SELECT DISTINCT Months.datefield --Considering this as months feild
FROM office_calendar AS Months) A
CROSS JOIN (SELECT DISTINCT article
FROM Yourtable) B
LEFT OUTER JOIN Yourtable C
ON a.month = c.Month
AND b.Article = c.Article
Else you need a months table. Try this.
SELECT *
FROM (SELECT 1 AS month UNION
SELECT 2 UNION
SELECT 3 UNION
SELECT 4 UNION
SELECT 5 UNION
SELECT 6 UNION
SELECT 7 UNION
SELECT 8 UNION
SELECT 9 UNION
SELECT 10 UNION
SELECT 11 UNION
SELECT 12) A
CROSS JOIN (SELECT DISTINCT article
FROM Yourtable) B
LEFT OUTER JOIN Yourtable C
ON a.month = c.Month
AND b.Article = c.Article

create a conditional select

I have a table that keeps record of targets assigned to different employees for different products for each month and it has a status field which keeps record of whether assigned target had been approved or not.
status - 1>>Pending, 2>>Approved
Eg:
pdt_id month emp_id status
1 04 1 2
2 04 2 2
3 04 3 1
1 05 1 2
2 05 2 2
3 05 3 2
Now I want to generate a report which shows the only the month for which there are no pending approvals. i.e from the above data the report should only show '05' because its the only month in which all the request have been approved
if i provide condition select month where status='2' it will fetch both 04 and 05 but i want to fetch only 05 ...
Plea
SELECT month
FROM myTable
WHERE month NOT IN (
SELECT month
FROM myTable
WHERE status = 1
)
LEFT JOIN the table onto itself to find out matches and eliminate them.
SELECT
t1.`month`
FROM
your_table AS t1
LEFT JOIN your_table AS t2
ON t1.`month` = t2.`month`
AND t2.`status` = 1
WHERE t2.month IS NULL
GROUP BY t1.month
There might be more elegant ways of doing this, but it gets the job done.
Months with Approved statuses only:
SELECT DISTINCT month
FROM myTable a
WHERE NOT EXISTS
( SELECT *
FROM myTable b
WHERE a.month = b.month
AND b.status <> 2
)
Months without any Pending:
SELECT DISTINCT month
FROM myTable a
WHERE NOT EXISTS
( SELECT *
FROM myTable b
WHERE a.month = b.month
AND b.status = 1
)
There are usually 3 ways to do this kind of problem, (using NOT EXISTS, using NOT IN and using LEFT JOIN with NULL check). You already have answers for the other 2 ways.
In this special case, there's another (4th) way. If you never plan to add more statuses than the 1 and 2, this will also work:
SELECT month
FROM myTable
GROUP BY month
HAVING MIN(status) = 2
Just a final comment/question. Do you only store month in the table, and not year? Because if you also have a year field, the query will not show correct results, once you have data from more than one year in the table.
You can just select months that don't have state Pending:
select month from table_name as tb1 where 0 = (select count(*) from table_name as tb2 where tb2.state = '1' AND tb1.month = tb2.month)
I dont understand why dont you do?
... WHERE month = "05" AND status = 2;
and if you have another concept which is "approved" why not add it as a column and include it in the query as well?
Sometimes fixing a thing means redesigning your tables.