MySQL one row per user depending on maxDate or maxId - php

I have one table in MYSQL called enrollments with these fields: id, user, estimated_date.
The values in this case are:
id, user, estimated_date
1, 1, 2015-10-10
2, 1, 2015-10-10
3, 2, 2015-10-20
4, 2, 2015-10-10
I want to select one row per user: the one that has the max(estimated_date). But in case the estimated_date is equal has to select the one that has the max(id). In other words... a group by with ordering by estimated_date and id (in this order).
The output should be:
2, 1, 2015-10-10
3, 2, 2015-10-20
I have this code now:
SELECT * from enrollments m
INNER JOIN
(SELECT user,
max(estimated_date) AS maxdate
FROM enrollments
GROUP BY user
) x ON m.user = x.user
AND m.estimated_date = x.maxdate
Could you help me please? I've been searching a lot but I didn't find anything that fits this case... Thanks!

You don't need an INNER JOIN, just a ORDER by estimated_date DESC, id DESC is enough. Here is your query:
Select * FROM (
SELECT
DISTINCT `id`, `user`, `estimated_date`
FROM
enrollments
ORDER by estimated_date DESC, id DESC
) as X
GROUP BY user
full fiddle here: http://sqlfiddle.com/#!9/31799/3

Related

Build MySql Query with multiple where conditions

I have to build a some complicated mysql query. I have a table with some user informations, with 3 columns: id, id_user, id_campo, valore. So, for example:
1, 1, 1, "Roberto" (where id_campo=1 is for the user name);
2, 2, 1, "Luca";
3, 1, 2, "Windows"; (where id_campo=2 is for used OS);
4, 2, 2, "Linux";
and so on.
Now, I have to select users where: name="Roberto" AND os="Linux", but the same user :
SELECT id_user WHERE (id_campo=1 AND valore="Roberto") OR (id_campo=2 AND valore="Linux").
In this sample case, the query return id_user=1 and id_user=2 but I would obtain no result. How I can modify the query so I can include the condition "the same user" ?
Thanks!
You need to build two "subqueries" and then join them by user id.
SELECT a.id_user
from (
SELECT id_user FROM table
WHERE (id_campo=1 AND valore="Roberto")
) a,
(
SELECT id_user FROM table
WHERE (id_campo=2 AND valore="Linux")
) b
WHERE a.is_user = b.id_user
You have a wrong db design but anyway you can usie an inner join on the same table based on id_user
select a.id_user, a.id_campo, a.valore
from my_table as a
inner join my_table as b on .id_user = b-id_user
where ( a.id_campo = 1 and a.valore ='Roberto')
AND ( b.id_campo = 2 and b.valore ='Linux')

Find parameter and compare the date from other table, sort by parameter

I have a table of employees (ID) and table when it changes their status (date, status, ID(FK)). I need to display list of employees sorted according to the status of a specified date
employees
ID, Name
1, Jack
2, Ralf
3, Jenny
changes
IDchange, Date, Status, ID
1, 2015-01-01, 2, 1 //Jack started in 2015-1-1 with status 2
2, 2015-03-01, 1, 2 //Ralf started in 2015-3-1 with status 1
3, 2015-04-01, 1, 3 //Jenny started in 2015-4-1 with status 1
4, 2015-08-01, 2, 2 //Ralf change status to 2 in 2015-8-1
5, 2015-10-01, 3, 2 //Ralf change status to 3 in 2015-10-1
6, 2016-04-01, 4, 1 //Jack change status to 4 in 2016-04-1
I tried theese for any date $My_Date, but I don't know how to repair it for all changes, it check only last date change
<?php
$sql2="SELECT *, Status
FROM employees s
JOIN (
SELECT MAX(Date) max_date, ID, Status
FROM changes sml
WHERE date <= '".$My_Date."'
GROUP BY ID
) sml ON (sml.ID = s.ID)
ORDER BY Status, Name ASC";
$result2 = MySQL_Query($sql2);
... ?>
You can use a correlated subquery in order to get the status per ID of each employee:
SELECT ID, Name, (SELECT Status
FROM changes AS c
WHERE e.ID = c.ID AND date <= ?
ORDER BY date DESC LIMIT 1) AS Status
FROM employees AS e
ORDER BY Status, Name ASC
The subquery will return the status for the current employee that is closer to the parameterised date value.
We can do it using a simple join query. As we are passing the date as parameter, there is no need to calculate max(date), example below:
select e.name, s.date, s.status
from employee e join status s on e.id = s.id
where s.date = ?
order by s.status desc

MySQL query: selecting latest results

I have a MySQL table with data, and would like to select the most recent entry for a specific test that was conducted.
table: research
columns: id (int), projectid (int), test (varchar),
when (datetime), result (longtext)
Example of rows:
1, 6, "Test1", 01-01-2013 12:15, "AAAAA"
2, 6, "Test1", 01-01-2013 13:15, "BBBBB"
3, 6, "Test2", 01-01-2013 16:00, "CCCCC"
4, 6, "Test2", 01-01-2013 16:15, "DDDDD"
5, 6, "Test2", 01-01-2013 16:30, "EEEEE"
If I want the latest results for "Test 1", I use..
SELECT *
FROM research
WHERE projectid=6 AND test='Test1'
ORDER BY when DESC limit 1
But how can I get the latest result for each test in a single project, in one query? I tried:
SELECT research.*
FROM research INNER JOIN (SELECT MAX(id) AS id
FROM research
WHERE projectid=".$ProjectId."
GROUP BY test) ids
ON research.id = ids.id
WHERE research.projectid=".$ProjectId
But it does not give me the latest result from a test, it gives me the oldest..
The idea behind the subquery is that it separately gets the latest date WHEN for every projectid and test. The result of which is then joined back on the original table.
SELECT a.*
FROM research a
INNER JOIN
(
SELECT projectid, test, MAX(`when`) maxDate
FROM research
GROUP BY projectid, test
) b ON a.projectid = b.projectid AND
a.test = b.test AND
a.`when` = b.maxDate
SQLFiddle Demo
You can use a WHERE with IN that filters on the max(when) date:
select *
from research r
where r.projectid = 6
and r.`when` in (select max(`when`)
from research
group by projectid, test);
See SQL Fiddle with Demo
Try this:
SELECT test,when,result
FROM research r1
WHERE when = (SELECT max(when)
FROM research r2
WHERE r2.test=r1.test)
Please try the query below.
SELECT `e`.* FROM (
SELECT `r`.*
FROM research `r`
ORDER By `r`.`when`
DESC
)`e`
WHERE `e`.`projectid`= $ProjectId GROUP BY `e`.`test`
I Hope this helps.
Thanks.
So, to get for every test of every project, the row with the largest when column, you can use this:
SELECT r.*
FROM research AS r
JOIN
( SELECT projectid, test, MAX(`when`) AS maxw
FROM research
GROUP BY projectid, test
) AS m
ON m.projectid = r.projectid
AND m.test = r.test
AND m.maxw = r.`when` ;
For every test of a single project (say project 6):
SELECT r.*
FROM research AS r
JOIN
( SELECT test, MAX(`when`) AS maxw
FROM research
WHERE projectid = 6
GROUP BY test
) AS m
ON m.test = r.test
AND m.maxw = r.`when`
WHERE r.projectid = 6 ;
An index on (projectid, test, when) will help efficiency of both the above queries.
Partition by test and order by date within the partition. Use analytic functions. No need for complicated queries above/below...

Need help in optimising query

I have two tables - incoming tours(id,name) and incoming_tours_cities(id_parrent, id_city)
id in first table is unique, and for each unique row from first table there is the list of id_city - s in second table(i.e. id_parrent in second table is equal to id from first table)
For example
incoming_tours
|--id--|------name-----|
|---1--|---first_tour--|
|---2--|--second_tour--|
|---3--|--thirth_tour--|
|---4--|--hourth_tour--|
incoming_tours_cities
|-id_parrent-|-id_city-|
|------1-----|---4-----|
|------1-----|---5-----|
|------1-----|---27----|
|------1-----|---74----|
|------2-----|---1-----|
|------2-----|---5-----|
........................
That means that first_tour has list of cities - ("4","5","27","74")
AND second_tour has list of cities - ("1","5")
Let's assume i have two values - 4 and 74:
Now, i need to get all rows from first table, where my both values are in the list of cities. i.e it must return only the first_tour (because 4 and 74 are in it's list of cities)
So, i wrote the following query
SELECT t.name
FROM `incoming_tours` t
JOIN `incoming_tours_cities` tc0 ON tc0.id_parrent = t.id
AND tc0.id_city = '4'
JOIN `incoming_tours_cities` tc1 ON tc1.id_parrent = t.id
AND tc1.id_city = '74'
And that works fine.
But i generate the query dynamically, and when the count of joins is big (about 15) the query slowing down.
i.e. when i try to run
SELECT t.name
FROM `incoming_tours` t
JOIN `incoming_tours_cities` tc0 ON tc0.id_parrent = t.id
AND tc0.id_city = '4'
JOIN `incoming_tours_cities` tc1 ON tc1.id_parrent = t.id
AND tc1.id_city = '74'
.........................................................
JOIN `incoming_tours_cities` tc15 ON tc15.id_parrent = t.id
AND tc15.id_city = 'some_value'
the query run's in 45s(despite on i set indexes in the tables)
What can i do, to optimaze it?
Thanks much
SELECT t.name
FROM incoming_tours t INNER JOIN
( SELECT id_parrent
FROM incoming_tours_cities
WHERE id IN (4, 74)
GROUP BY id_parrent
HAVING count(id_city) = 2) resultset
ON resultset.id_parrent = t.id
But you need to change number of total cities count.
SELECT name
FROM (
SELECT DISTINCT(incoming_tours.name) AS name,
COUNT(incoming_tours_cities.id_city) AS c
FROM incoming_tours
JOIN incoming_tours_cities
ON incoming_tours.id=incoming_tours_cities.id_parrent
WHERE incoming_tours_cities.id_city IN(4,74)
HAVING c=2
) t1;
You will have to change c=2 to whatever the count of id_city you are searching is, but since you generate the query dynamically, that shouldn't be a problem.
I'm pretty sure this works, but a lot less sure that it is optimal.
SELECT * FROM incoming_tours
WHERE
id IN (SELECT id_parrent FROM incoming_tours_cities WHERE id_city=4)
AND id IN (SELECT id_parrent FROM incoming_tours_cities WHERE id_city=74)
...
AND id IN (SELECT id_parrent FROM incoming_tours_cities WHERE id_city=some_value)
Just an hint.
If you use the IN operator in a WHERE clause, you can hope that the short-circuit of operator AND may remove unnecessary JOINs during the execution for the tours that do not respect the constraint.
Seems like an odd way to do that query, here
SELECT t.name FROM `incoming_tours` as t WHERE t.id IN (SELECT id_parrent FROM `incoming_tours_cities` as tc WHERE tc.id_city IN ('4','74'));
I think that does it, but not tested...
EDIT: Added table alias to sub-query
I've written this query using CTE's and it includes the test data in the query. You'll need to modify it so that it queries the real tables instead. Not sure how it performs on a large dataset...
Declare #numCities int = 2
;with incoming_tours(id, name) AS
(
select 1, 'first_tour' union all
select 2, 'second_tour' union all
select 3, 'third_tour' union all
select 4, 'fourth_tour'
)
, incoming_tours_cities(id_parent, id_city) AS
(
select 1, 4 union all
select 1, 5 union all
select 1, 27 union all
select 1, 74 union all
select 2, 1 union all
select 2, 5
)
, cityIds(id_city) AS
(
select 4
union all select 5
/* Add all city ids you need to check in this table */
)
, common_cities(id_city, tour_id, tour_name) AS
(
select c.id_city, it.id, it.name
from cityIds C, Incoming_tours_cities tc, incoming_tours it
where C.id_city = tc.id_city
and tc.id_parent = it.id
)
, tours_with_all_cities(id_city) As
(
select tour_id from common_cities
group by tour_id
having COUNT(id_city) = #numCities
)
select it.name from incoming_tours it, tours_with_all_cities tic
where it.id = tic.id_city

MySQL query for selecting a maximum element

I have a table with 4 columns: place_id, username, counter, last_checkin
I'm writing a check-in based system and I'm trying to get a query that will give me the "mayor" of each place. The mayor is the one with most check-ins, and if there is more than 1 than the minimum last_checkin wins.
For example, if I have:
place_id, username, counter, last_checkin
123, tom, 3 , 13/4/10
123, jill, 3, 14/4/10
365, bob, 2, 15/4/10
365, alice, 1, 13/4/10
I want the result to be:
123, tom
365, bob
I'm using it in PHP code
Here is the test data:
CREATE TABLE `my_table` ( `place_id` int(11), `username` varchar(50), `counter` int(11), `last_checkin` date);
INSERT INTO `my_table` VALUES (123,'tom',3,'2010-04-13'),(123,'jill',3,'2010-04-14'),(365,'bob',2,'2010-04-15'),(365,'alice',1,'2010-04-13');
How about..
SELECT
place_id,
(SELECT username
FROM my_table MT2
WHERE MT2.place_id = MT1.place_id
ORDER BY counter DESC, last_checkin ASC
LIMIT 1) AS mayor
FROM my_table MT1
GROUP BY place_id;
Edited as Unreason suggests to have ascending order for last_checkin.
Brian's correlated query is something I would write. However I found this different take and it might perform differently depending on the data
SELECT
mt1.place_id,
mt1.username
FROM
my_table mt1 LEFT JOIN my_table mt2
ON mt1.place_id = mt2.place_id AND
(mt1.counter < mt2.counter OR
(mt1.counter = mt2.counter AND mt1.last_checkin > mt2.last_checkin)
)
WHERE
mt2.place_id IS NULL
Which uses left join to get to the top records according to certain conditions.
$data = query("SELECT max(counter) counter,username FROM table GROUP By place_id ORDER By last_checkin DESC");

Categories