Mysql query Max not working - mysql

What i want to happen is group by parentid first, then group by position, which i have done. In that group i want the name with the highest rating to be displayed, which isn't happening. Instead the lowest id for each group is being displayed. The results should be tv1,tv3,tv5,tv7; as these are the highest rated values for each group.
id | name| parentid| position| rating |
1 | tv1 | 1 | 1 | 6 |
2 | tv2 | 1 | 2 | 5 |
3 | tv3 | 1 | 2 | 7 |
4 | tv4 | 1 | 2 | 3 |
5 | tv5 | 5 | 1 | 8 |
6 | tv6 | 5 | 1 | 2 |
7 | tv7 | 3 | 1 | 9 |
8 | tv8 | 3 | 1 | 3 |
$getquery = mysql_query("SELECT name,MAX(rating) FROM outcomes GROUP BY position,parentid") or die(mysql_error());
while($row=mysql_fetch_assoc($getquery)) {
$name = $row['name'];
$rating = $row['rating'];
echo "<p>Name: $name - $rating</p><p></p>";
}

It's not that the lowest id is being displayed -- you're not actually selecting the id column. Probably what you are seeing is the first entry in the name column for each group.
SELECT name, MAX(rating)
doesn't do what you think it does -- it doesn't instruct MySQL to pick the maximum value from the rating column, and also return the name that is associated with that row (aside: what do you think it would return if there was a tie for the maximum rating? What do you think it would return if you used AVERAGE rather than MAX?)
What it does instead is return the correctly calculated MAX(rating), and then one of the names out of that group. It doesn't guarantee which one gets returned, and it can change depending on how it decides to execute the query.
In fact, because of the undefined nature of a query such as this, it's not even legal SQL in other databases. (Try this in Postgres, and you'll get an error. Heck, try it in MySQL with the ONLY_FULL_GROUP_BY option enabled, and you'll get a similar error)
If what you want to do is find the maximum rating for each group, and then find the name associated with it, you'll have to do something like this:
SELECT name, max_rating FROM outcomes
JOIN (SELECT position, parentid, MAX(rating) AS max_rating from outcomes group by position, parentid) AS aggregated_table
USING (position, parentid)
WHERE rating = max_rating
(There are four or five other ways to do this, searching this site for mysql and aggregation will likely turn them up)

Related

Returns distinct record in a joins query - Rails 4

I'm trying to get and display an order list including the current status.
#orders = Order.joins(order_status_details: :order_status)
.order('id DESC, order_status_details.created_at DESC')
.select("orders.id, order_status_details.status_id, order_statuses.name, order_status_details.created_at")
It works good but is returning all the rows with order ids duplicated like this:
+----+-----------+----------------------+---------------------+
| id | status_id | name | created_at |
+----+-----------+----------------------+---------------------+
| 8 | 1 | Pending | 2016-01-31 16:33:30 |
| 7 | 3 | Shipped | 2016-02-01 05:01:21 |
| 7 | 2 | Pending for shipping | 2016-01-31 05:01:21 |
| 7 | 1 | Pending | 2016-01-31 04:01:21 |
+----+-----------+----------------------+---------------------+
The correct answer must return uniques ids, for the example above should be the first and second row.
I was already trying with distinct on select, .distinct, .uniq and .group but I'm getting an error.
Thanks.
First of all, I believe your model is "An Order has many OrderStatusDetail". So that is the reason why you have several different name in your result.
So you can modify the query like this:
#orders = Order.joins(order_status_details: :order_status)
.order('id DESC, order_status_details.created_at DESC')
.where('order_status_details.id IN (SELECT MAX(id) FROM order_status_details GROUP BY order_id)')
.select("orders.id, order_status_details.status_id, order_statuses.name, order_status_details.created_at")
Ideally, the where condition is used for selecting just the expected id of order_status_details, I use min_id for example, you can modify it as needed

Only return an ordered subset of the rows from a joined table

Given a structure like this in a MySQL database
#data_table
(id) | user_id | time | (...)
#relations_table
(id) | user_id | user_coach_id | (...)
we can select all data_table rows belonging to a certain user_coach_id (let's say 1) with
SELECT rel.`user_coach_id`, dat.*
FROM `relations_table` rel
LEFT JOIN `data_table` dat ON rel.`uid` = dat.`uid`
WHERE rel.`user_coach_id` = 1
ORDER BY val.`time` DESC
returning something like
| user_coach_id | id | user_id | time | data1 | data2 | ...
| 1 | 9 | 4 | 15 | foo | bar | ...
| 1 | 7 | 3 | 12 | oof | rab | ...
| 1 | 6 | 4 | 11 | ofo | abr | ...
| 1 | 4 | 4 | 5 | foo | bra | ...
(And so on. Of course time are not integers in reality but to keep it simple.)
But now I would like to query (ideally) only up to an arbitrary number of rows from data_table per distinct user_id but still have those ordered (i.e. newest first). Is that even possible?
I know I can use GROUP BY user_id to only return 1 row per user, but then the ordering doesn't work and it seems kind of unpredictable which row will be in the result. I guess it's doable with a subquery, but I haven't figured it out yet.
To limit the number of rows in each GROUP is complicated. It is probably best done with an #variable to count, plus an outer query to throw out the rows beyond the limit.
My blog on Groupwise Max gives some hints of how to do such.

Get highest value from multiple columns and associated name column

I've got a table with these columns:
id | player1_name | player1_score | player2_name | player2_score | player3_name | player3_score | player4_name | player4_score | player5_name | player5_score
Given a single row, how do I get the highest playerX_score and the corresponding playerX_name?
I've tried using GREATEST(), but I can't get the playerX_name.
As an aside, I think your table would be better designed as id | name | score | position | teamid, where position goes from 1 to 5 and teamid serves to group everyone in the same team together. It would make this sort of query much easier (greatest-score-per-team with associated rows).
However, here's one way to do what you want with your current table:
SELECT GREATEST(player1_score,player2_score,player3_score,
player4_score,player5_score) as score,
CASE GREATEST(...) -- repeat the above
WHEN player1_score then player1_name
WHEN player2_score then player2_name
WHEN player3_score then player3_name
WHEN player4_score then player4_name
WHEN player5_score then player5_name
END as name
FROM mytable
I think your table structure isn't right for what you're trying to do. You want the database to know that there's some relationship between player1_name and player1_score, but that's not encoded in the table. A change that would make this much easier would be to give each player their own record, and use what you're currently calling id (which I assume is the ID for a particular game) to indicate which players go together.
It would look like this:
game_id | player_num | player_name | score
1 | 1 | Octern | 100
1 | 2 | Boris | 400
1 | 3 | Jarlsberg | 300
1 | 4 | Pete | 40000
...
Then, to find the high scorer for a given game (in this case, game #1), you'd say:
select player_name from scores
WHERE game_id = 1
ORDER BY score desc
LIMIT 1

mysql query - conditional statements?

Here are my tables:
files
+----+--------+
| id | name |
+----+--------+
| 2 | file_1 |
| 3 | file_2 |
| 5 | file_3 |
+----+--------+
files_already_viewed
+----+---------+----------+------+
| id | file_id | category | user |
+----+---------+----------+------+
| 1 | 3 | 5 | 1 |
| 2 | 2 | 1 | 1 |
| 3 | 5 | 1 | 1 |
+----+---------+----------+------+
categories_files_join
+--------+---------+
| cat_id | file_id |
+--------+---------+
| 1 | 2 |
| 1 | 5 |
| 5 | 3 |
| 1 | 3 |
+--------+---------+
file_2 (which has an id of 3) has two categories associated with it, cat_id 5 and cat_id 1.
It has been viewed once by a user searching for files under the category 5.
But now the user is searching for files under the category 1.
I need a query that won't show file_2 under the "1" category until all the other files with a category id of 1 have been viewed first, since the user already viewed file_2. Basically putting file_2 at the end of the list.
Here is my query so far:
SELECT name FROM files
WHERE files.id NOT IN (
SELECT file_id FROM files_already_viewed
WHERE user='1')
ORDER BY most_viewed DESC
LIMIT 1
I order my search by the most popular viewed file. But i don't want to show files that have already been viewed regardless of category until all other files have been viewed with in that specific category.
Any help would be greatly appreciated. thanks!
Actually, your query will not show files already shown. What you want to do is to order those already shown files at the bottom. So, basically you'll have to sets of data: first, the data that matches the needed criteria and that the user has not been shown yet and then the data that matches the needed criteria but the user has been shown.
The way I'm handling the sets is by adding a customSort id for each set and then ordering by it. Now the short explanation is that the first group I get it by faking a MINUS operation with a left join: I get all the files that have not yet been seen. Then, the second group is a bit easier as it just needs to get all the files that have already been seen. So, the UNION of both sets in the customSort order would be the result you're looking for so now you just need to filter that result by the current query criteria (category = 1 in this case).
select file_id from (
select distinct cf1.cat_id, cf1.file_id, 1 as customSort
from CategoriesFiles1 cf1
left join FilesViewed1 fv1 on (fv1.file_id = cf1.file_id)
where (fv1.file_id is null)
union
select distinct cf2.cat_id, cf2.file_id, 2 as customSort
from CategoriesFiles2 cf2
join FilesViewed2 fv2 on (fv2.file_id = cf2.file_id)
) FinalResult
where (FinalResult.cat_id = 1)
order by customSort
Here is a link with the example. You can comment each data insert in files_already_viewed to see how, after viewing a file, the result changes. Besides, changing select file_id from to select * from will allow you to clearly see which set each row belongs to.
Let me know if this works.

MySQL - COUNT before INSERT in one query

Hey all, I am looking for a way to query my database table only once in order to add an item and also to check what last item count was so that i can use the next number.
strSQL = "SELECT * FROM productr"
After that code above, i add a few product values to a record like so:
ID | Product | Price | Description | Qty | DateSold | gcCode
--------------------------------------------------------------------------
5 | The Name 1 | 5.22 | Description 1 | 2 | 09/15/10 | na
6 | The Name 2 | 15.55 | Description 2 | 1 | 09/15/10 | 05648755
7 | The Name 3 | 1.10 | Description 3 | 1 | 09/15/10 | na
8 | The Name 4 | 0.24 | Description 4 | 21 | 09/15/10 | 658140
i need to count how many times it sees gcCode <> 'na' so that i can add a 1 so it will be unique. Currently i do not know how to do this without opening another database inside this one and doing something like this:
strSQL2 = "SELECT COUNT(gcCode) as gcCount FROM productr WHERE gcCode <> 'na'
But like i said above, i do not want to have to open another database query just to get a count.
Any help would be great! Thanks! :o)
There's no need to do everything in one query. If you're using InnoDB as a storage engine, you could wrap your COUNT query and your INSERT command in a single transaction to guarantee atomicity.
In addition, you should probably use NULL instead of na for fields with unknown or missing values.
They're two queries; one is a subset of the other which means getting what you want in a single query will be a hack I don't recommend:
SELECT p.*,
(SELECT COUNT(*)
FROM PRODUCTR
WHERE gccode != 'na') AS gcCount
FROM PRODUCTR p
This will return all the rows, as it did previously. But it will include an additional column, repeating the gcCount value for every row returned. It works, but it's redundant data...