Firstly do Group By then retrieve all rows as per order? - mysql

I have table where I have id and time.
ID | Time
1 | 8.35
1 | 8.40
3 | 8.43
4 | 8.45
1 | 8.50
2 | 8.52
3 | 8.54
4 | 8.55
1 | 8.57
2 | 9.01
3 | 9.05
5 | 9.06
Required Result
ID | Time
5 | 9.06
3 | 9.05
3 | 8.54
3 | 8.43
2 | 9.01
2 | 8.52
1 | 8.57
1 | 8.50
1 | 8.40
1 | 8.35
4 | 8.55
4 | 8.45
Currently I am doing it by Select * from table group by ID order by Time DESC and get
Result One:
ID | Time
5 | 9.06
3 | 9.05
2 | 9.01
1 | 8.57
4 | 8.55
then writing second query and storing data in list.
foreach value in Result one:
Select * from Table where ID = value
Instead of writing a loop, I will like to have only one query.
Basic problem is I want to group IDs and top group should be the item that occured recently. As in example 1 occurs many time but I will consider only the latest time while grouping.
Can I write only one query to get result?

SELECT ID, Time FROM Table ORDER BY ID, Time
Grouping combines matching rows, so you do not want to group, ordering puts them in order, and that's what you want, you want all the IDs in order then all the times in order within those ids, so you want to order by ID then order by time.
UPDATE due to question edit
This can be done with a join to a sub select
SELECT t.ID. t.Time FROM Table t
JOIN (SELECT ID, Max(Time) as Time FROM Table GROUP BY ID) ss
ON t.ID = ss.ID
ORDER BY ss.Time DESC, t.ID DESC, t.Time DESC
The sub select (ss) does the first query you have there, and joins it to the main table, letting you order by the highest(max) time for each ID, then by the ID and the Time for the row itself. Note that all the ordering is done on the final query, ordering in the sub select is useless, since the join will reorder it anyways.

Related

Select all rows with multiple occurrences - on same day

I have a single MySQL table with the name 'checkins' and 4 columns.
id | userIDFK | checkin_datetime | shopId
------------------------------------------------
1 | 1 | 2018-01-18 09:44:00 | 3
2 | 2 | 2018-01-18 10:32:00 | 3
3 | 3 | 2018-01-18 11:19:00 | 3
4 | 1 | 2018-01-18 17:57:00 | 3
5 | 1 | 2018-01-18 16:31:00 | 1
6 | 1 | 2018-01-19 08:31:00 | 3
Basically I want to find rows where users have checked-in more than once (>=2) on the same day and the same shop. So for instance if a user checks-in as in rows with ids 1 and 4 (same user, same day, same shop), the query should return a hit with the the entire rows (id, userIDFK, checkin_datetime, shopId). Hope this makes sense.
I already tried using
SELECT id, userIDFK, checkin_datetime, shopId
FROM (
SELECT * FROM 'checkins' WHERE COUNT(userIDFK)>=2 AND COUNT(shopId)>=2
)
The same day part I have no clew how to do it, and I know this query is way off, but this is the best I could.
You can try grouping by userId checkin_date and shopID
SELECT userIDFK, checkin_datetime, shopId,COUNT(SHOPiD)
FROM checkins
GROUP BY userIDFK, DATE(checkin_datetime), shopId
HAVING COUNT(SHOPID)>1
EDIT
You can include a subquery to get all lines:
select b.id,b.userIDFK, b.checkin_datetime, b.shopId
from checkins b
where (SELECT COUNT(SHOPiD)
FROM checkins a
where a.userIDFK=b.userIDFK and date(a.checkin_datetime)=date(b.checkin_datetime) and a.shopId=b.a.shopId
GROUP BY userIDFK, DATE(checkin_datetime), shopId)>1
GROUPBY can be used to get the multiple occurrences.
SELECT id, userIDFK, checkin_datetime, shopId
FROM checkins
GROUP BY userIDFK, DATE(checkin_datetime), shopId
HAVING count(id) > 1;
Hope it helps!
EDIT:
Using inner join you can achieve it. Here is the query:
SELECT c1.* FROM checkins c1 INNER JOIN checkins c2
ON c1.userIDFK = c2.userIDFK
AND date(c1.checkin_datetime) = date(c2.checkin_datetime)
AND c1.shopId = c2.shopId
AND c1.id != c2.id
Cheers!!

Getting the most recent row and linking it with another table?

Im trying to get the most recent row of a table
user_quiz:
+--------+-----------+-------------+-------------------+------------+
|quiz_id |userid | module_id |number_of_questions| user_score |
+--------+-----------+-------------+-------------------+-------- ---+
| 1 | 1 | 1 | 5 | 5 |
| 2 | 2 | 2 | 10 | 9 |
| 3 | 1 | 1 | 10 | 9 |
+--------+-----------+-------------+-------------------+------------+
I have used the query:
SELECT * FROM user_quiz WHERE userid = 1 ORDER BY quiz_id DESC LIMIT 1
which correctly retrieves the last row.
However I want to link the module_id with another table:
module:
+---------+------------+
|module_id|module_name |
+---------+------------+
| 1 | Forces |
| 2 | Electricity|
+---------+------------+
And retrieve the module name.
The result of the query will be used to print out the users most recent quiz:
Most recent quiz: Forces - Number of questions: 10 - User Score: 9
Is this possible using just one query?
You just need a JOIN:
SELECT uq.*, m.module_name
FROM user_quiz uq JOIN
modules m
ON uq.module_id = m.module_id
WHERE uq.userid = 1
ORDER BY uq.quiz_id DESC
LIMIT 1;
A more simple query to achieve the same would be
SELECT
user_quiz.quiz_id,
user_quiz.number_of_questions,
user_quiz.user_score,
modules .module_name
FROM user_quiz JOIN modules
ON user_quiz.module_id = modules.module_id
WHERE user_quiz.userid = 1
ORDER BY user_quiz.quiz_id DESC
LIMIT 1
If you want to get the same results for all the users, you could use a bit more sophisticated query
SELECT
user_quiz_virtual_table.userid,
user_quiz_virtual_table.quiz_id,
user_quiz_virtual_table.number_of_questions,
user_quiz_virtual_table.user_score,
modules.module_name
FROM (
SELECT
user_quiz.userid
user_quiz.quiz_id,
user_quiz.module_id
user_quiz.number_of_questions,
user_quiz.user_score
FROM user_quiz
ORDER BY user_quiz.quiz_id DESC
GROUP BY userid
) AS user_quiz_virtual_table
JOIN modules ON user_quiz_virtual_table.module_id = modules.module_id

Select promoted items grouped by another attribute

From table like below:
id | node_id | promoted | group_type | created_at |status
------------------------------------------------------------------
8 | 4321 | 1 | 3 | 2018-01-08 13:29:55| 1
4 | 4321 | 0 | 3 | 2018-01-06 11:22:53| 1
3 | 4321 | 0 | 1 | 2018-01-05 23:19:02| 1
2 | 4321 | 1 | 1 | 2018-01-05 21:20:15| 1
1 | 4321 | 1 | 3 | 2018-01-05 11:09:51| 1
I have to get one id and group_type values per each group_type.
If there is promoted item in the group, query should return it's id and group_type.
If there are more than one promoted items in the group, most recent promoted record should be returned.
If there is no promoted item in the group, query should return most recent record.
Using query below I managed to get almost what I need
SELECT a.id, a.group_type, a.promoted, a.created_at
FROM (
SELECT group_type, MAX(promoted) AS max_promoted
FROM nodes
WHERE node_id=4321 AND status=1
GROUP BY group_type
) AS g
INNER JOIN nodes AS a
ON a.group_type = g.group_type AND a.promoted = g.max_promoted
WHERE node_id= 4321 AND status=1 ORDER BY created_at
Unfortunately when there is more than one promoted item in the group I get both.
Any idea how to get only one promoted item per group?
EDIT:
If there is more than one group, query should return multiple rows but one per every group.
You can limit the result of the query by adding LIMIT 0,1 at the end of the query.
As you have ordered your result it will works.
For more information about LIMIT see : https://dev.mysql.com/doc/refman/5.7/en/limit-optimization.html
Edited: You should order items in descending to get the latest one on top and limit items as per required i.e. 1 or 2 and so on. Also union will help in getting latest result either promoted in case not promoted. The last limit will result only single (required) row. Here's your query:
(SELECT a.id, a.group_type, a.promoted, a.created_at
FROM (
SELECT group_type, MAX(promoted) AS max_promoted
FROM nodes
WHERE node_id=4321 and status=1
GROUP BY group_type
) AS g
INNER JOIN nodes AS a
ON a.group_type = g.group_type AND a.promoted = g.max_promoted
WHERE node_id= 4321 and status=1 ORDER BY created_at desc
limit 1)
union
(select a.id, a.group_type, a.promoted, a.created_at from nodes a order by created_at desc limit 1)
limit 1
Hope it helps!

Select where max Mysql

help please make sql select to database. There are such data.
My table is:
id news_id season seria date_update
---|------|---------|-----|--------------------
1 | 4 | 1 | 7 | 2017-04-14 16:38:10
2 | 4 | 1 | 7 | 2017-04-14 17:38:10
5 | 4 | 1 | 7 | 2017-04-14 16:38:10
3 | 4 | 1 | 7 | 2017-04-14 16:38:10
4 | 4 | 1 | 7 | 2017-04-14 16:38:10
6 | 4 | 1 | 7 | 2017-04-14 16:38:10
7 | 4 | 1 | 7 | 2017-04-14 16:38:10
8 | 1 | 1 | 25 | 2017-04-23 18:42:00
Need to get all cells grouped by max season and seria and date and sorted by date_update DESC.
In result i need next rows
id news_id season seria date_update
---|------|---------|-----|--------------------
8 | 1 | 1 | 25 | 2017-04-23 18:42:00
2 | 4 | 1 | 7 | 2017-04-14 17:38:10
Because this rows have highest season and seria and date_update per One news_id. I.e i need to select data wich have highest season and seria and date_update grouped by news_id and also sorted by date_update DESC
I tried so, but the data is not always correct, and it does not always for some reason cover all the cells that fit the condition.
SELECT serial.*
FROM serial as serial
INNER JOIN (SELECT id, MAX(season) AS maxseason, MAX(seria) AS maxseria FROM serial GROUP BY news_id) as one_serial
ON serial.id = one_serial.id
WHERE serial.season = one_serial.maxseason AND serial.seria = one_serial.maxseria
ORDER BY serial.date_update
Please, help. Thank.
The specification is unclear.
But we do know that the GROUP BY news_id clause is going collapse all of the rows with a common value of news_id into a single row. (Other databases would throw an error with this syntax; we can get MySQL to throw a similar error if we include ONLY_FULL_GROUP_BY in the sql_mode.)
My suggestion would be to remove the GROUP BY news_id clause from the end of the query.
But that's just a guess. It's not at all clear what you are trying to achieve.
EDIT
SELECT t.*
FROM (
SELECT r.news_id
, r.season
, r.seria
, MAX(r.date_update) AS max_date_update
FROM (
SELECT p.news_id
, p.season
, MAX(p.seria) AS max_seria
FROM (
SELECT n.news_id
, MAX(n.season) AS max_season
FROM serial n
GROUP BY n.news_id
) o
JOIN serial p
ON p.news_id = o.news_id
AND p.season = o.max_season
) q
JOIN serial r
ON r.news_id = q.news_id
AND r.season = q.season
AND r.seria = q.max_seria
) s
JOIN serial t
ON t.news_id = s.news_id
AND t.season = s.season
AND t.seria = s.seria
AND t.date_update = s.max_date_update
GROUP BY t.news_id
ORDER BY t.news_id
Or, an alternate approach making use of MySQL user-defined variables...
SELECT s.id
, s.season
, s.seria
, s.date_update
FROM (
SELECT IF(q.news_id = #p_news_id,0,1) AS is_max
, q.id
, #p_news_id := q.news_id AS news_id
, q.season
, q.seria
, q.date_update
FROM ( SELECT #p_news_id := NULL ) r
CROSS
JOIN serial q
ORDER
BY q.news_id DESC
, q.season DESC
, q.seria DESC
, q.date_update DESC
) s
WHERE s.is_max
ORDER BY s.news_id
The subquery selects the maximum season and the maximum seria per news_id. How many records exist for the news_id that match both the maximum season and the maximum seria we don't know. It can be, one or two or thousand or zero.
So with the join you get an unknown number of records per news_id. Then you group by news_id. This gets you one result row per news_id. How then can you select serial.*? * means all columns from a row, but which row,when there can be many for a news_id? MySQL usually picks values arbitrarily in this case (usually all from the same row, but even that is not guaranteed). So you end up with random rows which you order by date_update.
This doesn't make much sense. So the question is: what do you really want to achieve? Maybe my explanation suffices and you are able now to fix your query yourself.

Select a Sum the last 5 rows

I'm building a little quiz game in PHP/MySQL. After asking questions I want a screen to display how many of the last round were answered correctly.
Im storing whether they were answered correctly or not in a table that looks like this:
rowID | questionid | playerid | answercorrect |
1 | 1 | 1 | 1 |
2 | 2 | 1 | 1 |
3 | 3 | 1 | 1 |
4 | 4 | 1 | 1 |
5 | 5 | 1 | 0 |
6 | 6 | 1 | 1 |
7 | 7 | 1 | 1 |
I want to see how many of the last x (usually 5) questions were answered correctly.
I thought this would be simple. I'm trying this:
SELECT sum( answercorrect ) FROM `answersgiven` ORDER BY id DESC LIMIT 5
I thought this would sum the answercorrect column for the last 5 rows, giving me an answer of 4, but it's giving me 7, which is the result for ALL of the rows.
I feel like I'm missing something obvious. It seems like a simple thing to want to do.
Any ideas?
Try this:
SELECT sum(answercorrect)
FROM (SELECT answercorrect FROM `answersgiven` ORDER BY rowID DESC LIMIT 5) t1
Example Fiddle
In your query, the LIMIT clause affects the overall result: So first all are summed up (which results in one row - the sum over all rows) and then the first 5, if available, are taken (which again is just the one row).
The easiest way of achieving your target is to first select just the first 5 rows (in the subselect) and then sum up afterwards.
try this query
SELECT sum(col)
FROM (SELECT col FROM `table` ORDER BY id DESC LIMIT 5) t1
You're almost there. Just sum up those top 5 answers now:
SELECT SUM('top5')
FROM
(SELECT answercorrect AS 'top5'
FROM `answersgiven`
ORDER BY id DESC LIMIT 5) 'x'