mysql inner join get only the last 3 equals - php

I Hope someone could help me.
My Query.
SELECT * FROM `tbl_device`
INNER JOIN `tbl_temperature` ON tbl_device.ID = tbl_temperature.DevID
Result.
How can get something like this as shown in the photo on the link.
I need to get only the last 3 result of the tbl_device.DevID
I apologize for my bad english, it's really hard to explain. Any help is realy much appreciated.
Thanks a lot!

It should be something like
SELECT * FROM `tbl_device`
INNER JOIN `tbl_temperature` ON tbl_device.ID = tbl_temperature.DevID
ORDER BY tbl_temperature.DevID DESC
LIMIT 3 OFFSET 0
The order column can be not the same as in my example. Google MySQL LIMIT OFFSET
Or if you need to get only three last results from one of your tables then join it to another then it will be something like
SELECT * FROM tbl_device INNER JOIN
(SELECT .... FROM other_table ORDER BY your_column DESC LIMIT 3 OFFSET 0) as
T1 ON T1.id = tbl_device.ID

The simplest way is to use ANSI-standard window functions (row_number() in particular). But MySQL does not support those (yet).
In MySQL, probably the best way is to use variables:
SELECT . . .
FROM tbl_device d JOIN
(SELECT t.*,
(#rn := if(#d = t.devid, #rn + 1,
if(#d := t.devid, 1, 1)
)
) as rn
FROM (SELECT t.*
FROM tbl_temperature t
ORDER BY DevID, id DESC
) t CROSS JOIN
(SELECT #d := -1, #rn := 0) params
) t
ON tbl_device.ID = tbl_temperature.DevID
WHERE rn <= 3;
EDIT:
Here is a simpler way to express the logic. It might be performant with the right indexes:
SELECT d.*, t.*
FROM tbl_device d INNER JOIN
tbl_temperature t
ON d.ID = t.DevID
WHERE t.ID >= (SELECT t2.ID
FROM tbl_temperature t2
WHERE t2.DevId = t.DevId
ORDER BY t2.ID DESC
OFFSET 2 LIMIT 1
);
For performance, this can use an index on tbl_temperature(devid, id).

Related

Assign and display ranks to users from mysql data [duplicate]

This question already has answers here:
Rank function in MySQL
(13 answers)
Closed 4 years ago.
SELECT u.user_id, u.user_uid, s.ostats, s.attack, s.defense
FROM stats s JOIN
users u
ON s.id = u.user_id
ORDER BY s.ostats DESC;
So in above data, "ostats"(overall) is just a sum of attack+defense and by using this query I could display users in descending order of their "ostats" values..
But how do I assign and display rank of each user, like the one with most "ostats" valued user as Rank 1 and the second highest "ostats" valued user as Rank 2 and so on..?
What about using a variable to keep track of the row number?
SET #rank = 0;
SELECT
u.user_id,
u.user_uid,
s.ostats,
s.attack,
s.defense,
(#rank:=#rank + 1) AS rank
FROM stats s
JOIN users u on s.id = u.user_id
ORDER BY s.ostats DESC;
You can assign a row number using variables:
SELECT u.user_id,u.user_uid, s.ostats, s.attack, s.defense,
s.ranking
FROM (SELECT s.*, (#rn := #rn + 1) as ranking
FROM (SELECT s.* FROM stats s ORDER BY s.ostats DESC) s CROSS JOIN
(SELECT #rn := 0) params
) s JOIN
users u
ON s.id = u.user_id
ORDER BY s.ostats DESC;
In the event of ties, this will give different users different rankings. If that is an issue, you can use this modified form:
SELECT u.user_id,u.user_uid, s.ostats, s.attack, s.defense,
s.ranking
FROM (SELECT s.*,
(#rn := if(#o = ostats, #rn,
if(#o := ostats, #rn + 1, #rn + 1)
)
) as ranking
FROM (SELECT s.* FROM stats s ORDER BY s.ostats DESC) s CROSS JOIN
(SELECT #rn := 0, #o := -1) params
) s JOIN
users u
ON s.id = u.user_id
ORDER BY s.ostats DESC;
Of course, in MySQL 8.0, you can use row_number(), rank() or dense_rank() for this purpose.

Get the rank of the row given the order in sql

Let's say I have two tables like the following:
user_id
1
2
3
post_id user_id
1 2
2 3
3 2
The first table has all of the user information and the second table is a list of posts from users.
With these table, I want to make a ranking of who post the most posts. To do this, I can use the following sql
select user.user_id from user
left join post on user.user_id=post.user_id
order by count(post.post_id)
which will give me
user_id
2
3
1
, but what if I only want a single user's rank? In other words, I want to write a sql statement that will return what place the user is in given the user_id. For example, I want 1 as output if I have user_id of 2, 2 as the output if I have user_id 3, and 3 as the output if I have user_id 1.
Is this possible, or would I have to select the entire table and do a while loop in php until I hit the user and count the rows above?
Unfortunately, for a single user's rank, you pretty much have to calculate the rank of everyone and then pull out the single user. So, the ranking for all users is:
select u.user_id, count(p.post_id), (#rn := #rn + 1) as ranking
from user u left join
post p
on u.user_id = p.user_id cross join
(select #rn := 0) params
group by u.user_id
order by count(p.post_id) desc;
And for one user, use a subquery:
select *
from (select u.user_id, count(p.post_id), (#rn := #rn + 1) as ranking
from user u left join
post p
on u.user_id = p.user_id cross join
(select #rn := 0) params
order by count(p.post_id) desc
) u
where u.user_id = $USERID;
You can use user-defined variables for this:
select rnk
from (
select user.user_id, #rnk:#rnk+1 rnk
from user
left join post on user.user_id=post.user_id
cross join (select #rnk:=0) t
group by user.user_id
order by count(post.post_id) desc
) t
where user_id = ?
BTW -- I believe your query was missing a group by clause. Added above.

Mysql group_concat with distinct and where gives strange results

I have the following query which works fine (see below).
But when I add a condition, for example AND (specialtyName = '...') the main results are fine, but the GROUP_CONCAT only shows the results that match the condition.
Can anyone please help me with this?
Thanks in advance.
Fred.
SELECT
tblJobs.jobID,
tblJobs.jobName,
DATE_FORMAT(tblJobs.jobDate,'%d-%m-%Y'),
tblCompanies.companyID,
tblCompanies.companyName,
tblCompanies.companyNameConvert,
GROUP_CONCAT(DISTINCT tblSpecialties.specialtyName
ORDER BY FIELD (
specialtyName,
'specialtyName1',
'specialtyName2',
'specialtyName3'),
specialtyName ASC)
AS specialtyNames,
GROUP_CONCAT(DISTINCT tblSpecialties.specialtyNameConvert
ORDER BY FIELD (
specialtyName,
'specialtyName1',
'specialtyName2',
'specialtyName3'),
specialtyName ASC)
AS specialtyNamesConvert,
GROUP_CONCAT(DISTINCT tblRegions.regionName),
GROUP_CONCAT(DISTINCT tblRegions.regionNameConvert)
FROM tblJobs
LEFT JOIN tblCompanies ON
(tblJobs.jobCompany = tblCompanies.companyID)
LEFT JOIN tblSpecialties ON
FIND_IN_SET(tblSpecialties.specialtyID, REPLACE(tblJobs.jobSpecialty,' ',','))
LEFT JOIN tblRegions ON
FIND_IN_SET(tblRegions.regionID, REPLACE(tblJobs.jobRegion,' ',','))
WHERE
AND jobActive = '1'
AND jobDate >= '2013-01-01'
AND companyActive = '1'
GROUP BY jobID
ORDER BY jobDate DESC, jobID DESC, jobCompany DESC
If you say:
WHERE jobActive = '1' AND jobDate >= '2013-01-01' AND companyActive = '1' AND
specialties = XXX
Then you are only going to get exactly those specialties. The filtering is done before the aggregation. As a note: including such conditions in the where clause also turns the outer joins to inner joins. Your joins are probably on properly aligned foreign key relationships, so inner joins may be appropriate.
I'm guessing what you really want is to filter jobs by those having that specialty, but to keep all other information. You want to do the filtering after the aggregation. Do this with a having clause instead of a where clause:
having sum(specialties = XXX) > 0;
This will keep only the rows that have the particular specialty, and keep all the other information.
I suppose that using aliases for your tables and subqueries could resolve your problem.
You can try something like this:
SELECT
tblJobs.jobID,
tblJobs.jobName,
DATE_FORMAT(tblJobs.jobDate,'%d-%m-%Y'),
tblCompanies.companyID,
tblCompanies.companyName,
tblCompanies.companyNameConvert,
(SELECT GROUP_CONCAT(DISTINCT ts.specialtyName
ORDER BY FIELD (
specialtyName,
'specialtyName1',
'specialtyName2',
'specialtyName3'),
specialtyName ASC)
FROM tblSpecialties ts) AS specialtyNames ,
, ... ,
FROM tblJobs
LEFT JOIN tblCompanies ON
(tblJobs.jobCompany = tblCompanies.companyID)
LEFT JOIN tblSpecialties ON
FIND_IN_SET(tblSpecialties.specialtyID, REPLACE(tblJobs.jobSpecialty,' ',','))
LEFT JOIN tblRegions ON
FIND_IN_SET(tblRegions.regionID, REPLACE(tblJobs.jobRegion,' ',','))
WHERE
AND jobActive = '1'
AND jobDate >= '2013-01-01'
AND companyActive = '1'
GROUP BY jobID
ORDER BY jobDate DESC, jobID DESC, jobCompany DESC
I didn't tested this code, but It could help.

Limiting a left join to returning one result?

I currently have this left join as part of a query:
LEFT JOIN movies t3 ON t1.movie_id = t3.movie_id AND t3.popularity = 0
The trouble is that if there are several movies with the same name and same popularity (don't ask, it just is that way :-) ) then duplicate results are returned.
All that to say, I would like to limit the result of the left join to one.
I tried this:
LEFT JOIN
(SELECT t3.movie_name FROM movies t3 WHERE t3.popularity = 0 LIMIT 1)
ON t1.movie_id = t3.movie_id AND t3.popularity = 0
The second query dies with the error:
Every derived table must have its own alias
I know what I'm asking is slightly vague since I'm not providing the full query, but is what I'm asking generally possible?
The error is clear -- you just need to create an alias for the subquery following its closing ) and use it in your ON clause since every table, derived or real, must have its own identifier. Then, you'll need to include movie_id in the subquery's select list to be able to join on it. Since the subquery already includes WHERE popularity = 0, you don't need to include it in the join's ON clause.
LEFT JOIN (
SELECT
movie_id,
movie_name
FROM movies
WHERE popularity = 0
ORDER BY movie_name
LIMIT 1
) the_alias ON t1.movie_id = the_alias.movie_id
If you are using one of these columns in the outer SELECT, reference it via the_alias.movie_name for example.
Update after understanding the requirement better:
To get one per group to join against, you can use an aggregate MAX() or MIN() on the movie_id and group it in the subquery. No subquery LIMIT is then necessary -- you'll receive the first movie_id per name withMIN() or the last with MAX().
LEFT JOIN (
SELECT
movie_name,
MIN(movie_id) AS movie_id
FROM movies
WHERE popularity = 0
GROUP BY movie_name
) the_alias ON t1.movie_id = the_alias.movie_id
LEFT JOIN movies as m ON m.id = (
SELECT id FROM movies mm WHERE mm.movie_id = t1.movie_id
ORDER BY mm.id DESC
LIMIT 1
)
you could try to add GROUP BY t3.movie_id to the first query
Try this:
LEFT JOIN
(
SELECT t3.movie_name, t3.popularity
FROM movies t3 WHERE t3.popularity = 0 LIMIT 1
) XX
ON t1.movie_id = XX.movie_id AND XX.popularity = 0
On MySQL 5.7+ use ANY_VALUE & GROUP_BY:
SELECT t1.id,t1.movie_name, ANY_VALUE(t3.popularity) popularity
FROM t1
LEFT JOIN t3 ON (t3.movie_id=t1.movie_id AND t3.popularity=0)
GROUP BY t1.id
more info
LEFT JOIN only first row
https://dev.mysql.com/doc/refman/5.7/en/group-by-handling.html
Easy solution to left join the 1 most/least recent row is using select over ON phrase
SELECT A.ID, A.Name, B.Content
FROM A
LEFT JOIN B
ON A.id = (SELECT MAX(id) FROM B WHERE id = A.id)
Where A.id is the auto-incremental primary key.
LEFT JOIN (
SELECT id,movie_name FROM movies GROUP BY id
) as m ON (
m.id = x.id
)
// Mysql
SELECT SUM(db.item_sales_nsv) as total FROM app_product_hqsales_otc as db
LEFT JOIN app_item_target_otc as it ON
db.id = (SELECT MAX(id) FROM app_item_target_otc as ot WHERE id = db.id)
and db.head_quarter = it.hqcode
AND db.aaina_item_code = it.aaina_item_code AND db.month = it.month
AND db.year = it.year
WHERE db.head_quarter = 'WIN001' AND db.month = '5' AND db.year = '2022' AND db.status = '1'

MySQL UPDATE - Selective update

First up, apologies for the awful title I couldn't think of a better way to articulate my issue. (Feel free to suggest better altnernatives)
Basically I have a table with a "count" column.
I want to reset all counts to zero except for the 10 rows with the top values. I want them to be reset to 0.
How do I achieve this without writing multiple queries?
Update
I have my query as the following now
UPDATE covers AS t1
LEFT JOIN (SELECT t.cover_id
FROM covers t
ORDER BY t.cover_views DESC
LIMIT 10) AS t2 ON t2.id = t.id
SET cover_views = 0
WHERE t2.id IS NULL
I get the error #1054 - Unknown column 't2.id' in 'where clause' - any idea why?
I also tried the following with the same result
UPDATE covers t1
LEFT JOIN (SELECT t.cover_id
FROM covers t
ORDER BY t.cover_views DESC
LIMIT 10) t2 ON t2.id = t.id
SET t1.cover_views = 0
WHERE t2.id IS NULL
Use:
UPDATE TABLE t1
LEFT JOIN (SELECT t.id
FROM TABLE t
ORDER BY t.id DESC
LIMIT 10) t2 ON t2.id = t1.id
SET TABLE.count = 0
WHERE t2.id IS NULL
try:
update <table> t
left outer join
(
select id from <table> order by <counter> desc limit 10
) c on c.id = t.id
set
<counter> = 0
where
c.id is null;
You can use a subquery:
update A set count = 0 where A.id not in
(select id from A order by count desc limit 10)

Categories