Additional condition for equal values using MAX() function - mysql

I have a simple database for auctions. It includes a table that contains the bids.
+---------+---------+--------+------------+
| item_id | user_id | amount | time |
+---------+---------+--------+------------+
| 3 | 2 | 500 | 1540152972 |
| 3 | 4 | 500 | 1540151466 |
+---------+---------+--------+------------+
At the end of the auction I need to find which users won which items (highest amount). I've considered the following query for that
SELECT item_id, user_id, MAX(amount)
FROM auction_bids
GROUP BY item_id
Which appears to work fine, until multiple users have made a bid with the same amount.
In that case I need to retrieve the earliest one (i.e: the lowest time value).
How do I work this into my GROUP BY query?

Return a row if no other row with the same item_id has a higher price, or, if the prices are the same, the other row is later.
SELECT item_id, user_id, amount
FROM auction_bids a1
WHERE NOT EXISTS (select 1 from auction_bids a2
where a2.item_id = a1.item_id
and (a2.amount > a1.amount
or (a2.amount = a1.amount and a2.time < a1.time)))

No, this is filtering, not aggregation:
SELECT ab.*
FROM auction_bids ab
WHERE ab.amount = (SELECT MAX(ab2.amount)
FROM auction_bids ab2
WHERE ab2.item_id = ab.item_id
);

Related

Mysql Query to find the last transaction date with the purchased item from Purchase_history table

Table: purchase_history having all details of users
Fields are : id,uid, purchase_date, item_id, item_size, item_color
where id is a primary key.
There are many rows for an similar uid. e.g.
id | uid | purchase_date | item_id | item_size | item_color
1 | 200 | 2016-10-22 | 1021 | 3 | red
2 | 122 | 2016-08-02 | 21 | 1 | black
3 | 200 | 2016-05-01 | 222 | 1 | blue
4 | 101 | 2016-01-07 | 102 | 1 | red
So now I want a single query to get the last transaction date, item_id and uid group by uid. I used below query:
select uid, max(purchase_date), item_id from purchase_history group by uid;
it gives me correct uid and purchase date but the item id is not picked from the last row. It is coming from the first row. Is there any way that we can find the item id from the last row with uid and purchase_date?
Try this:
select uid, max(purchase_date) as date, item_id from purchase_history group by uid ORDER by date desc,item_id desc
Make sure that you item_id type is an integer.
You can find max of purchase date for each user in a subquery and join it with the main table like so:
select t1.uid, t1.purchase_date, t1.item_id
from purchase_history t1
inner join (
select uid, max(purchase_date) purchase_date
from purchase_history
group by uid
) t2 on t1.uid = t2.uid
and t1.purchase_date = t2.purchase_date;
NOTE: It'll give multiple rows for a uid, if there are rows with multiple max dates.
Use correlated subquery:
SELECT uid, purchase_date, item_id
FROM purchase_history p1
WHERE purchase_date = (
SELECT MAX(purchase_date)
FROM purchase_history p2
WHERE p2.uid = p1.uid
);
try this query
select * from (select * from purchase_history order by purchase_date asc) purchase_history group by uid;

select non group by columns with count in Mysql

I have a table tbl with three columns:
id | fk | dateof
1 | 1 | 2016-01-01
2 | 1 | 2016-01-02
3 | 2 | 2016-02-01
4 | 2 | 2016-03-01
5 | 3 | 2016-04-01
I want to get the results like this
Id count of Id max(dateof)
2 | 2 | 2016-01-02
4 | 2 | 2016-03-01
5 | 1 | 2016-04-01
My try
SELECT id,tbl.dateof dateof
FROM tbl
INNER JOIN
(SELECT fk, MAX(dateof) dateof ,
count(id) cnt_of_id -- How to get this count value in the result
FROM tbl
GROUP BY fk) temp
ON tbl.fk = temp.fk AND tbl.dateof = temp.dateof
This is an aggregation query, but you don't seem to want the column being aggregated. That is ok (although you cannot distinguish the rk that defines each row):
select count(*) as CountOfId, max(dateof) as maxdateof
from t
group by fk;
In other words, your subquery is pretty much all you need.
If you have a reasonable amount of data, you can use a MySQL trick:
select substring_index(group_concat(id order by dateof desc), ',', 1) as id
count(*) as CountOfId, max(dateof) as maxdateof
from t
group by fk;
Note: this is limited by the maximum intermediate size for group_concat(). This parameter can be changed and it is typically large enough for this type of query on a moderately sized table.
You obviously want one result row per fk, so group by it. Then you want the max ID, the row count and the max date for each fk:
select
max(id) as max_id,
count(*) as cnt,
max(date_of) as max_date_of
from tbl
group by fk;

MAX function in MySQL does not return proper key value

I have a table called tbl_user_sal:
| id | user_id | salary | date |
| 1 | 1 | 1000 | 2014-12-01 |
| 2 | 1 | 2000 | 2014-12-02 |
Now I want to get the id of the maximum date. I used the following query:
SELECT MAX(date) AS from_date, id, user_id, salary
FROM tbl_user_sal
WHERE user_id = 1
But it gave me this output:
| id | user_id | salary | from_date |
| 1 | 1 | 2000 | 2014-12-02 |
Which is correct as far as the max date being 2014-12-02, but the corresponding id is not correct. This happens for other records as well. I used order by to check but that was not successful either. Can anyone shed some light on this?
Note: Its not necessary that max date will have max id, according to my needs. Records can have max date but id may be older.
If you only want to retrieve that information for a single user, which you seem to, because of your WHERE clause, just use ORDER BY and LIMIT:
SELECT *
FROM tbl_user_sal
WHERE user_id = 1
ORDER BY date DESC
LIMIT 1
If you want to do that for every user, however, you will have to get a little bit fancier. Something like that should do it:
SELECT t2.id, user_id, date
--find max date for each user_id
FROM (SELECT user_id, MAX(date) AS date
FROM tbl_user_sal
GROUP BY user_id) AS t1
--join ids for each max date/user_id combo
JOIN tbl_user_sal AS t2
USING (user_id, date)
--limit to 1 id for every user_id
GROUP BY
user_id
You are missing group by clause Try this:
select max(awrd_date) as from_date,awrd_id
from tbl_user_sal
where awrd_user_id = 106
group by awrd_id
What I believe you should do here is have a subquery that pulls the max date, and your outer query looks for the row with that date.
It looks like this:
SELECT *
FROM myTable
WHERE date = (SELECT MAX(date) FROM myTable);
Additional things may need to be added if you want to search for a specific user_id, or get the largest date for each user_id, but this gives your expected results for this example here.
Here is the SQL Fiddle.

MySQL, understanding SQL query behavior

I have a simple MySQL table:
| id | sid | date |
+--------+---------+------------+
| 1 | 1 | 2013-12-01 |
+--------+---------+------------+
| 3 | 2 | 2013-12-17 |
+--------+---------+------------+
| 4 | 1 | 2013-12-17 |
+--------+---------+------------+
| 5 | 1 | 2013-12-18 |
+--------+---------+------------+
I need group this table by sid field and get records with max id for each sid with correct date. I try below code:
SELECT MAX(id), date FROM my_table GROUP BY sid
But the date field is incorrect, for example I get date 2013-12-01 with id 5 as a result.
What am I doing wrong ?
The way standard SQL is defined (at least up to ansi 1992, others will correct me), any field of your SELECT clause must be included in your group by condition. [Mysql allows you to not do so, but that is why it is confusing: the results are not as you expect]
Your query should then be:
SELECT MAX(id), date FROM my_table GROUP BY sid, date
But in this case, clearly this is not what you want. Your requirement is to get the date corresponding to the MAX(id) for each sId.
You have to isolate each part of the algorithm in different queries.
1 - get the max id for each sid:
SELECT MAX(id) AS id, sid FROM my_table GROUP BY sid
2 - get the date corresponding to the max of ids for each sid:
SELECT date FROM my_table WHERE sid = X AND id = Y
3 - join these 2 queries using an INNER JOIN or more shortly, a JOIN:
SELECT m.sid, m.id, m.date
FROM my_table m
JOIN (SELECT MAX(id) AS id, sid FROM my_table GROUP BY sid) t
ON t.sid = m.sid AND m.id = t.id
You need a join:
SELECT a.id, a.date
FROM foo a
INNER JOIN (SELECT MAX(id) as max_id FROM foo GROUP BY sid) b ON a.id = b.max_id

MySQL conditionally populate column 3 based on DISTINCT involving 2 other columns in one table

Had a good read through similar topics but I can't quite a) find one to match my scenario, or b) understand others enough to fit / tailor / tweek to my situation.
I have a table, the important fields being;
+------+------+--------+--------+
| ID | Name | Price |Status |
+------+------+--------+--------+
| 1 | Fred | 4.50 | |
| 2 | Fred | 4.50 | |
| 3 | Fred | 5.00 | |
| 4 | John | 7.20 | |
| 5 | John | 7.20 | |
| 6 | John | 7.20 | |
| 7 | Max | 2.38 | |
| 8 | Max | 2.38 | |
| 9 | Sam | 21.00 | |
+------+------+--------+--------+
ID is an auto-incrementing value as records get added throughout the day.
NAME is a Primary Key field, which can repeat 1 to 3 times in the whole table.
Each NAME will have a PRICE value, which may or may not be the same per NAME.
There is also a STATUS field that need to be populated based on the following, which is actually the part I am stuck on.
Status = 'Y' if each DISTINCT name has only one price attached to it.
Status = 'N' if each DISTINCT name has multiple prices attached to it.
Using the table above, ID's 1, 2 and 3 should be 'N', whilst 4, 5, 6, 7, 8 and 9 should be 'Y'.
I think this may well involve some form of combination of JOINs, GROUPs, and DISTINCTs but I am at a loss on how to put that into the right order for SQL.
In order to get the count of distinct Price values per name, we must use a GROUP BY on the Name field, but since you also want to display all names ungrouped but with an additional Status field, we must first create a subselect in the FROM clause which groups by the name and determines whether the name has multiple price values or not.
When we GROUP BY Name in the subselect, COUNT(DISTINCT price) will count the number of distinct price values for each particular name. Without the DISTINCT keyword, it would simply count the number of rows where price is not null.
In conjunction with that, we use a CASE expression to insert N into the Status column if there is more than one distinct Price value for the particular name, otherwise, it will insert Y.
The subselect only returns one row per Name, so to get all names ungrouped, we join that subselect to the main table on the condition that the subselect's Name = the main table's Name:
SELECT
b.ID,
b.Name,
b.Price,
a.Status
FROM
(
SELECT Name, CASE WHEN COUNT(DISTINCT Price) > 1 THEN 'N' ELSE 'Y' END AS Status
FROM tbl
GROUP BY Name
) a
INNER JOIN
tbl b ON a.Name = b.Name
Edit: In order to facilitate an update, you can incorporate this query using JOINs in the UPDATE like so:
UPDATE
tbl a
INNER JOIN
(
SELECT Name, CASE WHEN COUNT(DISTINCT Price) > 1 THEN 'N' ELSE 'Y' END AS Status
FROM tbl
GROUP BY Name
) b ON a.Name = b.Name
SET
a.Status = b.Status
Assuming you have an unfilled Status column in your table.
If you want to update the status column, you could do:
UPDATE mytable s
SET status = (
SELECT IF(COUNT(DISTINCT price)=1, 'Y', 'N') c
FROM (
SELECT *
FROM mytable
) s1
WHERE s1.name = s.name
GROUP BY name
);
Technically, it should not be necessary to have this:
FROM (
SELECT *
FROM mytable
) s1
but there is a mysql limitation that prevents you to select from the table you're updating. By wrapping it in parenthesis, we force mysql to create a temporary table and then it suddenly is possible.