Having a hard time with mysql's GROUP BY or DISTINCT - mysql

I tried searching and searching for an answer to my problem but haven't successfully found one so I'm hoping one of you more experienced gurus can help me out with this MySQL issue I'm having.
On one of my websites I allow people to basically POST on their profile. Then on the side of my website, I list the last 8 profile posts that are authored by the recipient (in other words they post on their own profile) and do not show on the side posts done by other people. I call these "Updates"
Recently I noticed a woman had posted twice on her profile and it was showing two posts on the side of the website from her. I only want to show one post by each person, and only the last 8 "Updates"...
What I did which seemed to work at first, is use a GROUP BY in my query as you see here:
SELECT `ratemynudepics`.* FROM `ratemynudepics` WHERE `user_id` = `author` AND `body` <> '' GROUP BY `author` ORDER BY `id` DESC LIMIT 8
Her multiple posts on the side went down to just one post and I thought it was fixed until I tried posting a test "Update" of my own on my own profile. It didn't show up on the side at all.
I tried using a DISTINCT instead of GROUP BY as seen here:
SELECT DISTINCT `user_id`, `author`, `id`, `body`, `date` FROM ratemynudepics WHERE `user_id` = `author` AND `body` <> '' ORDER BY `id` DESC LIMIT 8
That change made it so my post did in fact appear on the side, but the woman's two updates were back underneath mine.
I've tried all sorts of variations and used both DISTINCT and GROUP BY at the same time and no matter what I try nothing will properly show mine up top, (which should be the case since mine is the last record in the database) with only one listing of the woman's. I checked many times to make sure she didn't post with a different user_id and sure enough testing the query in phpmyadmin shows two listings for her, both having the same user_id, and author. I'm not sure why DISTINCT is allowing for multiple rows with the same user_id using the above query.
I tried the following query:
SELECT DISTINCT `user_id`, `id`, `author`, `date`, `body` FROM ratemynudepics WHERE `user_id` = `author` AND `body` <> '' GROUP BY `author` ORDER by `id` DESC LIMIT 8
and it only shows her once but doesn't show me in any of those 8 results even though again my row is the last one inserted into the database.
Can someone please help me to understand what I'm doing wrong here so that I can properly display only a maximum of 1 row per user but not abandon my latest database result which should be the 1st result? Much appreciated!! Peace
Edit - Here is some sample result data to match the queries
Notice the 2nd result set shows my 'test' update but shows a user's post twice, which she did post the same text twice, about a month apart.
SELECT DISTINCT `id`, `user_id`, `author`, `body`, `date` FROM ratemynudepics WHERE `user_id` = `author` AND `body` <> '' GROUP BY `author` ORDER BY `id` DESC LIMIT 8
AND
SELECT `ratemynudepics`.* FROM ratemynudepics WHERE `user_id` = `author` AND `body` <> '' GROUP BY `author` ORDER BY `id` DESC LIMIT 8
Both above queries produce the following
id user_id author body date
122 4391 4391 Email me at [blocked] 1497299836
83 4270 4270 I'm back..lol..ho 1474258804
79 4303 4303 Send me a message if y 1473959358
76 4362 4362 This place is a morgue. 1472580597
68 4358 4358 Smile, have a nice day 1470897755
57 4344 4344 Can someone rate my bo 1467946896
55 4338 4388 hey lets chat 1466792249
50 4319 4319 hi whats up 1465604578
SELECT DISTINCT `id`, `user_id`, `author`, `body`, `date` FROM `ratemynudepics` WHERE `user_id` = `author` AND `body` <> '' ORDER BY `id` DESC LIMIT 8
produces the following results
id user_id author body date
153 1 1 test 1510212341
135 4391 4391 Email me at [blocked] 1508374921
122 4391 4391 Email me at [blocked] 1497299836
83 4270 4270 I'm back..lol..ho 1474258804
79 4303 4303 Send me a message if y 1473959358
76 4362 4362 This place is a morgue. 1472580597
68 4358 4358 Smile, have a nice day 1470897755
57 4344 4344 Can someone rate my bo 1467946896
The bottom result shows my test row, but shows the woman's multiple posts. The first result shows only one of hers but leaves mine out... Any ideas? Thanks!!

Thanks to another post on this website I found the answer to my problem if I use the following query:
SELECT `id`, `user_id`, `author`, `body`, `date`
FROM ratemynudepics
WHERE id IN ( SELECT MAX(id)
FROM ratemynudepics
WHERE `user_id` = `author` AND `body` <> ''
GROUP BY user_id )
ORDER BY `id` DESC LIMIT 8
Produces
id user_id author body date
153 1 1 test 1510212341
135 4391 4391 Email me at [blocked] 1508374921
83 4270 4270 I'm back..lol..ho 1474258804
79 4303 4303 Send me a message if y 1473959358
76 4362 4362 This place is a morgue. 1472580597
68 4358 4358 Smile, have a nice day 1470897755
57 4344 4344 Can someone rate my bo 1467946896
55 4338 4338 hey lets chat 1466792249
Thanks for everyone's help nonetheless. Happy Halloween! :D
Here is the post that helped me with the answer How can I SELECT rows with MAX(Column value), DISTINCT by another column in SQL?

Related

SQL query on messages table returns not all results. Strange behavior

Can someone help. I have strange behavior on SQL query to "messages" table.
It supposed to return MAX(id)'s for each user contacted target user.
Table is simple:
id(int, ai) | from(int) | dest(int) | text(txt) | time(int) | msg_status(int)
USERS table have only 5 test users.
MSG table have about 40 messages.
When I query most of user ids(1, 2, 3, 4) - I receive normal result.
When I query one specific user No.5- I receive ONE less result.
The query is:
SELECT MAX(`id`) FROM `msg` WHERE `from` = '5' OR `dest` = '5'
GROUP BY (IF(`from` > `dest`, `from`, `dest`)), (IF(`from` < `dest`, `dest`, `from`));
For most users it gives normal result. For example for user 1 I have:
MAX(id) 37, 30, 33, 36
And it is OK as user No.1 have conversation messages with all other 4 users.
But for user No.5 I have:
MAX(id) 36
Thus this is not correct. As user No.5 have last messages as described here:
id from dest text
35 5 2 hellp
36 5 1 hi there
So there is one less result, as it have to be something like:
MAX(id) 35, 36
But it is not.
Can someone suggest what is wrong?
UPD.
Simplifying the query:
SELECT * FROM `msg` WHERE `id` IN (SELECT MAX(`id`) FROM `msg`
WHERE `from` = '5' OR `dest` = '5' GROUP BY `from`, `dest`);
I receive result:
id from desr text
32 1 5 test
35 5 2 hello
36 5 1 test2
So oroginal query have to produce 35 and 36 result, thus giving 36 only...
There's a logic error in the GROUP BY expressions:
GROUP BY
(IF(`from` > `dest`, `from`, `dest`)),
(IF(`from` < `dest`, `dest`, `from`));
If from > dest, then this is equivalent to GROUP BY from, from; if from < dest, then this is GROUP BY dest, dest. For the two example rows with from = 5, they're grouped by from, and thus in the same group, and thus MAX(id) is 36, with no 35 in the results.
In contrast, ID 1 will be the minimum when compared to any other user ID, so when querying for ID 1, the query will group by the other IDs, guaranteeing they remain separate groups. That is why the query works for ID 1.
To avoid this error, it's better to use the Greatest and Least functions:
GROUP BY Greatest(`from`, `dest`), Least(`from`, `dest`)
The 2 GROUP BY statements should return the same value (5) for both Id 35 and 36 so MAX(id) will return 36 - which is what you are getting.
Your SQL matches your result so it all seems to be working. If the result is not what you want then you’ll need to change your SQL - and if you want help with that then you’ll need to explain what the logic is that would return IDs 35 and 36

MySQL select HOTTEST (most upvoted in shortest time)

For a long time I have been trying to figure out how to make Hottest Posts
What I want to achieve : ORDER BY MOST UPVOTED IN LESS TIME
For example I got 4 posts:
ID UPVOTES(Total) UPVOTES(Weekly) DATE
1 50 50 01.09.2017
2 421 6 25.07.2017
3 71 50 13.08.2017
4 111 37 15.08.2017
And It would need to order like 1 -> 3 -> 4 -> 2
My Goal is to get UPVOTES(Weekly) - > I Don't know how to calculate
it. I just made it here, to better explain what I want to achieve.
I have got 2 database tables fun_posts and fun_post_upvotes
I was trying to achieve it like this, but it didn't work, it just ordered by id or ordered by upvotes
$stmt = $this->conn->prepare=("SELECT * , (SELECT COUNT(*) FROM
fun_post_upvotes WHERE image_id=fun_posts.id GROUP BY image_id) FROM fun_posts
ORDER BY fun_posts.id DESC, fun_posts.upvotes DESC");
This is working a part of it.
SELECT fun_posts_upvotes.image_ID, COUNT(image_ID) as 'Upvotes' FROM fun_posts_upvotes GROUP BY fun_posts_upvotes.image_ID ORDER BY Upvotes DESC;
Just try it and add the Date part. ;-) you can ask again if you tried some :P
If I understand the problem correctly you should quite easily be able to apply the following to your problem:
MYSQL Order By Sum of Columns
Order By Sum of Two Fields
CREATE TABLE `tb1` (
`id` int(11) NOT NULL,
`Name` varchar(50) NOT NULL,
`up` int(11) NOT NULL,
`Down` int(11) NOT NULL,
`Date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
SELECT `Name`, (`tb1`.`up`/ DATEDIFF(NOW(),`tb1`.`Date` )) as `heat`
FROM `tb1`
ORDER BY (`tb1`.`up`/ DATEDIFF(`tb1`.`Date`, NOW())) ASC
This should illustrate my point
I think adding to your query:
ORDER BY UPVOTES(Weekly) DESC
would work for your issue.

Mysql Select Statement isn't working

So maybe it's due to lack of sleep, but I am having a major brain malfunction and can't remember what is going wrong here. Here is my statement:
SELECT DISTINCT `county`, COUNT(*)
FROM `ips`
WHERE `county` != 'NULL' AND `county` != '' AND
EXISTS (SELECT * FROM `pages`
WHERE (`timestamp` BETWEEN FROM_UNIXTIME(?) AND FROM_UNIXTIME(?)))
GROUP BY `county`
I'm expecting the results to be something like:
County | Number
Some county | 42
Other county | 27
My pages table has a timestamp of each time a page is viewed by a user, so if they viewed a page between the date, the county from the IP table is selected and the number of that total county is being populated as the num. I'm using PDO and i'm passing in two times that I've used strtotime() on.
I'm currently stuck. All help is apprieciated. Hopefully it's not some stupid little mistake that I've overlooked.
You cant compare null with != you need to use is not null.
SELECT `county`, COUNT(*)
FROM `ips`
WHERE `county` IS NOT NULL AND `county` != '' AND
EXISTS (SELECT 1 FROM `pages`
WHERE (`timestamp` BETWEEN FROM_UNIXTIME(?) AND FROM_UNIXTIME(?)))
GROUP BY `county`

MySQL query with multiple subqueries is too slow. How do I speed it up?

I have a MySQL query which does exactly what I want, but it takes anywhere between 110 & 130 seconds to process. The problem is that it works in tandem with a software that times out 20 seconds after making the query.
Is there anything I can do to speed up the query? I'm considering moving the db over to another server, but are there any more elegant options before I go that route?
-- 1 Give me a list of IDs & eBayItemIDs
-- 2 where it is flagged as bottom tier
-- 3 Where it has been checked less than 168 times
-- 4 Where it has not been checked in the last hour
-- 5 Or where it was never checked but appears on the master list.
-- 1 Give me a list of IDs & eBayItemIDs
SELECT `id`, eBayItemID
FROM `eBayDD_Main`
-- 2 where it is flagged as bottom tier
WHERE `isBottomTier`='0'
-- 3 Where it has been checked less than 168 times
AND (`id` IN
(SELECT `mainid`
FROM `eBayDD_History`
GROUP BY `mainid`
HAVING COUNT(`mainID`) < 168)
-- 4 Where it has not been checked in the last hour
AND id IN
(SELECT `mainID`
FROM `eBayDD_History`
GROUP BY `mainID`
HAVING ((TIME_TO_SEC(TIMEDIFF(NOW(), MAX(`dateCollected`)))/60)/60) > 1))
-- 5 Or where it was never checked but appears on the master list.
OR (`id` IN
(SELECT `id`
FROM `eBayDD_Main`)
AND `id` NOT IN
(SELECT `mainID`
FROM `eBayDD_History`))
If I understand the logic correctly, you should be able to replace this logic with this:
select m.`id`, m.eBayItemID
from `eBayDD_Main` m left outer join
(select `mainid`, count(`mainID`) as cnt,
TIME_TO_SEC(TIMEDIFF(NOW(), MAX(`dateCollected`)))/60)/60) as dc
from `eBayDD_History`
group by `mainid`
) hm
on m.mainid = hm.mainid
where m.`isBottomTier` = '0' and hm.cnt < 168 and hm.dc > 1 or
hm.mainid is null;

MySQL: How to construct a given query

I am not a MySQL guru at all, and I would really appreciate if someone takes some time to help me. I have three tables as shown below:
TEAM(teamID, teamName, userID)
YOUTH_TEAM(youthTeamID, youthTeamName, teamID)
YOUTH_PLAYER(youthPlayerID, youthPlayerFirstName, youthPlayerLastName, youthPlayerAge, youthPlayerDays, youthPlayerRating, youthPlayerPosition, youthTeamID)
And this is the query that I have now:
SELECT team.teamName, youth_team.youthTeamName, youth_player.*
FROM youth_player
INNER JOIN youth_team ON youth_player.youthTeamID = youth_team.youthTeamID
INNER JOIN team ON youth_team.teamID = team.teamID
WHERE youth_player.youthPlayerAge < 18
AND youth_player.youthPlayerDays < 21
AND youth_player.youthPlayerRating >= 5.5
What I would like to add to this query is a more thorough checks like the following:
if player has 16 years, and his position is scorer, then the player should have at least 7 rating in order to be returned
if player has 15 years, and his position is playmaker, then the player should have at least 5.5 rating in order to be returned
etc., etc.
How can I implement these requirements in my query (if possible), and is that query going to be a bad-way solution? Is it maybe going to be better if I do the selection with PHP code (if we suppose I use PHP) instead of doing it in the query?
Here is a possible solution with an additional "criteria/filter" table:
-- SAMPLE TEAMS: Yankees, Knicks:
INSERT INTO `team` VALUES (1,'Yankees',2),(2,'Knicks',1);
-- SAMPLE YOUTH TEAMS: Yankees Juniors, Knicks Juniors
INSERT INTO `youth_team` VALUES (1,'Knicks Juniors',1),(2,'Yankees Juniors',2);
-- SAMPLE PLAYERS
INSERT INTO `youth_player` VALUES
(1,'Carmelo','Anthony',16,20,7.5,'scorer',1),
(2,'Amar\'e','Stoudemire',17,45,5.5,'playmaker',1),
(3,'Iman','Shumpert',15,15,6.1,'playmaker',1),
(4,'Alex','Rodriguez',18,60,3.5,'playmaker',2),
(5,'Hiroki','Kuroda',16,17,8.7,'scorer',2),
(6,'Ichiro','Suzuki',19,73,8.3,'playmaker',2);
-- CRITERIA TABLE
CREATE TABLE `criterias` (
`id` int(11) NOT NULL,
`age` int(11) DEFAULT NULL,
`position` varchar(45) DEFAULT NULL,
`min_rating` double DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-- SAMPLE CRITERIAS
-- AGE=16, POSITION=SCORER, MIN_RATING=7
-- AGE=15, POSITION=PLAYMAKER, MIN_RATING=5.5
INSERT INTO `criterias` VALUES (1,16,'scorer',7), (2,15,'playmaker',5.5);
Now your query could look like:
SELECT team.teamName, youth_team.youthTeamName, youth_player.*
FROM youth_player
CROSS JOIN criterias
INNER JOIN youth_team ON youth_player.youthTeamID = youth_team.youthTeamID
INNER JOIN team ON youth_team.teamID = team.teamID
WHERE
(
youth_player.youthPlayerAge < 18
AND youth_player.youthPlayerDays < 21
AND youth_player.youthPlayerRating >= 5.5
)
AND
(
youth_player.youthPlayerAge = criterias.age
AND youth_player.youthPlayerPosition = criterias.position
AND youth_player.youthPlayerRating >= criterias.min_rating
)
This yields (shortened results):
teamName youthTeamName youthPlayerName Age Days Rating Position
=============================================================================
Yankees "Knicks Juniors" Carmelo Anthony 16 20 7.5 scorer
Yankees "Knicks Juniors" Iman Shumpert 15 15 6.1 playmaker
Knicks "Yankees Juniors" Hiroki Kuroda 16 17 8.7 scorer
Doing it in the query is quite fine...... as long as it doesn't get too messed up. You can perform a lot of stuff in your query, but it may get hard to maintain. So if it gets too long and you want somebody else to take a look at it, you should split it up or find a solution in your php-script.
As for your requirements add this too your WHERE-part:
AND
(
(YOUTH_PLAYER.youthPlayerAge >= 16 AND YOUTH_PLAYER.youthPlayerPosition = 'scorer' AND YOUTH_PLAYER.youthPlayerRating >= 7)
OR (YOUTH_PLAYER.youthPlayerAge >= 15 AND YOUTH_PLAYER.youthPlayerPosition = 'playmaker' AND YOUTH_PLAYER.youthPlayerRating >= 5.5)
)