MySQL: Group By Clause - php

I need some help with a group by mysql query clause.
Medals Table (this holds all the medals):
+-----------------------------------------------------------------------+
| medal_id | medal_level | medal_name | medal_type | medal_icon |
|-----------------------------------------------------------------------|
| 1 | 1 | 1 post medal | 1 | icon_file.png |
| 2 | 2 | 1 thread medal | 2 | icon_file.png |
| 3 | 1 | 2 post medal | 1 | icon_file.png |
| 4 | 2 | 2 threads medal | 2 | icon_file.png |
+-----------------------------------------------------------------------+
Users Medals Table (this holds the medals which users have won):
+--------------------------------+
|medal_id |user_id | earnedtime |
|--------------------------------|
| 1 | 1 | 1313548360 |
| 2 | 1 | 1313548365 |
| 3 | 1 | 1313548382 |
| 4 | 1 | 1313548410 |
+--------------------------------+
MySQL Query:
SELECT m.*, u.*
FROM users_medals u
LEFT JOIN medals m ON (u.medal_id=m.medal_id)
WHERE u.user_id IN(1)
GROUP BY m.medal_type
ORDER BY u.earnedtime
What this is intended to do is display medals users have earned (this is a plugin for a bulletin board system). It selects and displays the medals where the users medal id is equal to the medal id in the table that holds all the medals.
This works fine, however, it's not displaying the latest medal. It's only displaying the following medal id's: 1, 2. It should be displaying 3 and 4.
Additional Info: I only want to display one medal from each medal type. So for example, if the user has earned two "post medals", only the latest one earned will be displayed, along with any other medals earned.
Any help would be greatly appreciated.

SELECT m.*, u.*
FROM users_medals u
LEFT JOIN medals m ON u.medal_id = m.medal_id
WHERE u.user_id IN (1)
AND u.earnedtime = (
SELECT MAX(users_medals.earnedtime) FROM users_medals
LEFT JOIN medals ON users_medals.medal_id = medals.medal_id
WHERE users_medals.user_id = u.user_id
AND medals.medal_type = m.medal_type
)
I think it should be possible to do this as a subjoin too but the restriction of the medal type being in another table made my head ache. It would probably be easier to write if there was a view of the two tables joined together.

You have "GROUP BY medal_type". Since your medals table only has values of 1,2 for this column, you won't get 3 & 4 in your results.

It's not showing 3 and 4 because you are grouping it by medal type. So 3 and 4 and the same type as 1 and 2. If you take group by off it will show every medal.
If you want this group by but only want 3 and 4. Then then you simply need to
order by m.medal_type descending

Since you're grouping by the medal type, it'll only select one of each type. If you want a list of all the medals a user got, why are you grouping by the medal type?
Perhaps you're looking for the highest level of the medal by type? If that's the case, you might want to specify that.

Dalen did ask the right thing - why would you like to use GROUP BY in the first place? That's not needed in this query. GROUP BY serves a different purpose; maybe you can understand it more by checking an example like this one.

Related

I need to calculate percentage of counted items

I have two tables:
1st: reasons
id | title
---------------------------------
1 | Customer didn't like it
2 | Needs improving
3 | Wrong format
2nd: projects
id | title | rejected
------------------------------------
1 | Priject 1 | Null
2 | Priject 2 | 1
3 | Priject 3 | 1
4 | Priject 4 | Null
5 | Priject 5 | 2
I need to display Reasons.Title and number of project rejected for that reason. I've managed to join those tables together, with this code
SELECT reasons.title as title, count(*) as num
FROM reasons
LEFT JOIN reasons on projects.rejected = reasons.id
WHERE projects.rejectedIS NOT NULL
GROUP BY projects.rejected
Now I need to add percentage, so my final table looks like this
title | num | percentage
--------------------------------------------------
Customer didn't like it | 2 | 66,6
Needs improving | 1 | 33,3
The format of percentage is of course not important.
I would like to get this done with MySql, so I do not need to use two queries and extra PHP, but if there is another solution, other from MySql, I'm open to suggestions
You can do this by getting the total in the FROM clause:
SELECT r.title as title, count(*) as num,
COUNT(*) / pp.cnt as ratio
FROM reasons r JOIN
projects p
ON p.rejected = r.id CROSS JOIN
(SELECT COUNT(*) as cnt FROM projects p WHERE rejects IS NOT NULL) pp
GROUP BY r.title, pp.cnt;
Notes:
This fixes the table names, so the query has a projects table.
This removes the WHERE because it is not needed.
This changes the LEFT JOIN to an inner join.

Run While Loop Through All Distinct Values of A Column

This is an example MYSQL result
+----+---+
| A | B |
+----+---+
| 1 | 1 |
| 1 | 2 |
| 2 | 3 |
| 2 | 4 |
| 3 | 5 |
+----+---+
I would like to run through every distinct in Column A and do something utilizing the values in Column B.
Let's say A has userids and B has foods. I would like to grab all the foods that user 1 likes and then shoot an email to 1, then grab all the foods that user 2 likes and email to her, and so forth. Would appreciate any suggestions.
If you want comma separated values, you can use GROUP_CONCAT
SELECT A, GROUP_CONCAT(DISTINCT B) foodList
FROM tableName
GROUP BY A
SQLFiddle Demo
Other Link
GROUP BY clause

Get 4 latest rows from each category, from same table

Before I explain my problem, I will quickly go over how the table structure is:
Type: MySQL
Posts/topic Table:
int int** UNIX Time Int Int
--------------------------------------------------
| id | category | postdate | topic_id | is_topic |
--------------------------------------------------
| 1 | a | 12345678 | 1 | 1 |
--------------------------------------------------
| 2 | a | 12345678 | 1 | 0 |
--------------------------------------------------
| 3 | b | 12345678 | 3 | 1 |
--------------------------------------------------
| 4 | b | 12345678 | 3 | 0 |
--------------------------------------------------
| 5 | c | 12345678 | 5 | 1 |
--------------------------------------------------
| 6 | c | 12345678 | 5 | 0 |
--------------------------------------------------
**I'm using letters to make is easier to read the table
I am trying to retrieve the 4 newest rows for each category, and I am able to get the 1 newest from each category with GROUP BY, but I have no idea how to get multiple for each category.
I have tried something like this:
SELECT *
FROM posts p
WHERE NOT EXISTS
(
SELECT *
FROM posts
WHERE category = p.category
LIMIT 4
)
I also tried working with some of the other answers people supplied for some other answers here on SO, but I did not seem to be able to make them fit my purpose.
I am completely lost here, as mysql is not a strong side of mine when it comes to these more complex queries.
Any help or pointers in the right directions would really be appreciated!
Thanks!
UPDATE: Please note that the number of categories is not be static, and will change.
Something like this would probably do :
select * from
(select
#rank:=CASE WHEN #ranked <> category THEN 1 ELSE #rank+1 END as rank,
id,
category,
postdate,
topic_id,
is_topic
#ranked:=category
from
(select #rank := -1) a,
(select #ranked :=- -1) b,
(select * from posts order by category, postdate desc) c
) ranked_posts
where ranked_posts.rank <= 4
Basically what is happening here is that I am trying to create a "ranking" function present in other engines (MS SQL comes in mind).
The query goes through all the posts, ordered by category, postdate and adds a "ranking" number to every row resetting the rank when the category changes. Like:
rank | category
1 a
2 a
...
100 a
1 b
2 b
1 c
2 c
3 c
You do that inside a "sub query" to mimic a table then just select the rows that have rank <= 4, (the ones you need). If you need more or less you can adjust that number.
One thing to keep in mind is that the ordering is important or the ranks will get all screwed up. The categories have to be "grouped", hence the ORDER BY category and then the groups ordered by your criteria postdate desc.

MYSQL Get results assigned to multiple "categories" maybe using JOIN?

I am new to the world of mysql and I'm having some trouble getting the data I need from a database.
The 2 tables I have are...
Results
ID | TITLE | LOTS OF OTHER DATA |
1 | res1 | |
2 | res2 | |
3 | res3 | |
4 | res4 | |
5 | res5 | |
Categories
ID | RESULT_ID | CATEGORY NAME |
1 | 1 | purchase |
2 | 1 | single_family |
3 | 1 | conventional |
4 | 2 | usda |
5 | 3 | somecategory |
I'm trying to create a query that will select results that belong to all of the categories provided in the query. For example a query for purchase & single_family & conventional in this example would return the first result in the results table.
Does that make sense? Is there a query that will do this or is this more of a problem with my database structure?
Thanks a lot!
Try something like this:
SELECT * FROM Results r
INNER JOIN Categories c on r.ID = c.RESULT_ID
WHERE c.name in ('purchase', 'single_family', 'conventional')
GROUP BY r.ID
HAVING COUNT(c.ID) = 3
The basic select with join will get you three rows only for result 1.
Edit: To make sure your code won't break if you change your database you should always select the fields you want explicitly: SELECT r.ID, .. FROM ..
So you're basically doing a simple join with all the category table for all categories where the category name is one of the names in the list. Try to run the 3 first lines manually to see the result you get.
Next you group by the result id. This means that you are aggregating all the rows sharing the same result id into one row. The last line means that we are filtering the aggregated columns that are aggregated by 3 rows. That means that you will only return results that have 3 matching categories.
So the only problem with this approach is if you have duplicate result_id, categoryname in your Categories table.

query to imlement block list

I have two tables a and b as follows to implement a simple block list where users can block other users.....
Table A
+------------+--------------+------+
| Name | phone |userid|
+------------+--------------+------+
| Mr Sasi | 01225 708225 | 1 |
| Miss Brown | 01225 899360 | 2 |
| Mr Black | 01380 724040 | 3 |
+------------+--------------+------+
Table B
+------------+--------------+
| blockedbyid| blockedid |
+------------+--------------+
| 1 | 2 |
| 2 | 3 |
| 1 | 3 |
+------------+--------------+
"blockedbyid" is id of user who has blocked the user in "blockedid".
I need to join the two tables and fetch all records from table A such that the result has all users who are not blocked by a particular user [ie blockedbyid='XXX'].. Can you guys give the SQL query so that i can fetch the records as a recordset??? I dont want to fetch two different rowsets and compare it in php....
Something like this should work
Parameter :USERID
SELECT * FROM TABLEA WHERE userid NOT IN (SELECT blockedid FROM TABLEB WHERE blockedbyid = :USERID)
Using join
SELECT u.* FROM TABLEB b, TABLEA u WHERE b.blockedbyid = 'XXX' AND b.blockedid = NULL
It may work like that, give it a try.
Roadie57 solutions seems better though.

Categories