I'm creating a system that allows a user to search a database of photo albums images for a keyword, it's working great, the only issue is that I'm ordering relevancy by the amount of times that keyword appears in an album. I'm doing this using:
SELECT collections_ids.collection_id
FROM `keywords`
INNER JOIN collections_ids ON keywords.id = collections_ids.photo_id
WHERE keywords.`keyword` = 'trees'
GROUP BY collection_id
ORDER BY COUNT(*) DESC
As said, this works great.
The only issue is, when this is included in an "WHERE IN" query, it loses it's order and is returned randomly. For clarity, here is the query:
SELECT collections.id,
collections.title
images.img_small
FROM `collections`
INNER JOIN images ON images.id = collections.cover_photo
WHERE collections.`id` IN
(SELECT collections_ids.collection_id
FROM `keywords`
INNER JOIN collections_ids ON keywords.id = collections_ids.photo_id
WHERE keywords.`keyword` = 'trees'
GROUP BY collection_id
ORDER BY COUNT(*) DESC)
I've tried researching, and people have suggested using the FIELD function, but I don't see that working in this context.
Any suggestions?
you can use sub query as join and take it count(*) as order by like below
SELECT collections.id,
collections.title
images.img_small
FROM `collections`
INNER JOIN images ON images.id = collections.cover_photo
INNER JOIN
(SELECT distinct collections_ids.collection_id As collection_id,COUNT(*) as total
FROM `keywords`
INNER JOIN collections_ids ON keywords.id = collections_ids.photo_id
WHERE keywords.`keyword` = 'trees'
GROUP BY collection_id
) as A
ON A.collection_id =collections.collection_id
order by A.total
Related
I am working with a query, which looks like this
SELECT s.c1, s.t, s.u, s.dt, t.temp, t.dt AS dt2
FROM `systemusage` AS s
INNER JOIN temperature AS t ON s.did=t.did
WHERE t.did = (SELECT id FROM devices WHERE m = 1)
LIMIT 1
Which works just fine, however if I add ORDER BY s.id, then the query gets totally stuck, can someone guide me on why? the id field is primary, so it should be indexed no?
Add an index on the column temperature.did so that the WHERE clause can be implemented efficiently.
It also may help to replace WHERE t.did = (SELECT ...) with a JOIN.
SELECT s.c1, s.t, s.u, t.temp
FROM `systemusage` AS s
INNER JOIN temperature AS t ON s.did=t.did
INNER JOIN devices AS d ON d.id = t.did
WHERE d.m = 1
ORDER BY s.id DESC, t.id DESC
LIMIT 1
I have the following query:
SELECT images.id from images WHERE images.id NOT IN
(
SELECT
temp.target_id
FROM (
SELECT
images_user_groups.images_id AS target_id,
images_user_groups.user_groups_id AS source_id
FROM
images_user_groups
) AS temp
INNER JOIN
user_groups ON user_groups.id = temp.source_id
INNER JOIN
images ON images.id = temp.target_id
WHERE
user_groups.description LIKE "%Freigabe ins Presseportal%"
GROUP BY
temp.target_id
)
It runs, but it goes very slowly -- 5 minutes and counting as I write this. (The subquery that I'm running a NOT IN on returns 36,000 rows, and I assume that the parent query is taking time to check every one of the 38,000 entries in the images table against those 36,000 rows.)
Is there a way I can speed this query up?
It should be a lot faster to write this as a LEFT JOIN, looking for NULL values in user_groups to indicate that the image was not in that group:
SELECT i.id
FROM images i
JOIN images_user_groups iu ON iu.images_id = i.id
LEFT JOIN user_groups u ON u.id = iu.user_groups_id AND u.description LIKE "%Freigabe ins Presseportal%"
GROUP BY i.id
HAVING COUNT(u.id) = 0
The problem with NOT IN and NOT EXISTS is that the WHERE clause has to be evaluated for every row in images.
You can try in below way
SELECT images.id from images WHERE images.id NOT IN
(
SELECT images_user_groups.images_id
FROM images_user_groups INNER JOIN user_groups ON user_groups.id = images_user_groups.user_groups_id
inner join images ON images.id = images_user_groups.images_id
WHERE user_groups.description LIKE "%Freigabe ins Presseportal%"
)A
We are maintaining a history of Content. We want to get the updated entry of each content, with create Time and update Time should be of the first entry of the Content. The query contains multiple selects and where clauses with so many left joins. The dataset is very huge, thereby query is taking more than 60 seconds to execute. Kindly help in improving the same. Query:
select * from (select * from (
SELECT c.*, initCMS.initcreatetime, initCMS.initupdatetime, user.name as partnerName, r.name as rightsName, r1.name as copyRightsName, a.name as agelimitName, ct.type as contenttypename, cat.name as categoryname, lang.name as languagename FROM ContentCMS c
left join ContentCategoryType ct on ct.id = c.contentType
left join User user on c.contentPartnerId = user.id
left join Category cat on cat.id = c.categoryId
left join Language lang on lang.id = c.languageCode
left join CopyRights r on c.rights = r.id
left join CopyRights r1 on c.copyrights = r1.id
left join Age a on c.ageLimit = a.id
left outer join (
SELECT contentId, createTime as initcreatetime, updateTime as initupdatetime from ContentCMS cms where cms.deleted='0'
) as initCMS on initCMS.contentId = c.contentId WHERE c.deleted='0' order by c.id DESC
) as temp group by contentId) as c where c.editedBy='0'
Any help would be highly appreciated. Thank you.
Just a partial eval and suggestion because your query seems non properly formed
This left join seems unuseful
FROM ContentCMS c
......
left join (
SELECT contentId
, createTime as initcreatetime
, updateTime as initupdatetime
from ContentCMS cms
where cms.deleted='0'
) as initCMS on initCMS.contentId = c.contentId
same table
the order by (without limit) in a subquery in join is unuseful because join ordered values or unordered value produce the same result
the group by contentId is strange beacuse there aren't aggregation function and the sue of group by without aggregation function is deprecated is sql
and in the most recente version for mysql is not allowed (by deafult) if you need distinct value or just a rows for each contentId you should use distinct or retrive the value in a not casual manner (the use of group by without aggregation function retrive casual value for not aggregated column .
for a partial eval your query should be refactored as
SELECT c.*
, c.initcreatetime
, c.initupdatetime
, user.name as partnerName
, r.name as rightsName
, r1.name as copyRightsName
, a.name as agelimitName
, ct.type as contenttypename
, cat.name as categoryname
, lang.name as languagename
FROM ContentCMS c
left join ContentCategoryType ct on ct.id = c.contentType
left join User user on c.contentPartnerId = user.id
left join Category cat on cat.id = c.categoryId
left join Language lang on lang.id = c.languageCode
left join CopyRights r on c.rights = r.id
left join CopyRights r1 on c.copyrights = r1.id
WHERE c.deleted='0'
) as temp
for the rest you should expiclitally select the column you effectively need add proper aggregation function for the others
Also the nested subquery just for improperly reduce the rows don't help performance ... you should also re-eval you data modelling and design.
i have been trying to use this particular query to find the top three most used musical keys in songs and show all the songs made using those musical keys so far the problem is that i'm using in operator with a subquery and it doesn't work!!
i have read that instead of in using join is preferable but since i haven't used any joins i am not able to use that with the query!! plaeae help!!!
SELECT `Key_Name`,`Song_Title`
FROM `musicalkey_record`,`musical_keys`,`record`
WHERE `record`.`Record_ID`=`musicalkey_record`.`Record_ID`
AND `musical_keys`.`Key_ID`=`musicalkey_record`.`Key_ID`
AND `Key_Name` IN (SELECT `Key_Name` FROM `musicalkey_record`,`musical_keys`,`record`
WHERE `record`.`Record_ID`=`musicalkey_record`.`Record_ID`
AND `musical_keys`.`Key_ID`=`musicalkey_record`.`Key_ID` GROUP BY `Key_Name` ORDER BY
COUNT(`Song_Title`) DESC LIMIT 3) ORDER BY `Key_Name`;
query with joins but without subquery:
SELECT `Key_Name`,`Song_Title` FROM `musical_keys` INNER JOIN `musicalkey_record` ON
`musical_keys`.`Key_ID`=`musicalkey_record`.`Key_ID`
INNER JOIN `record` ON `record`.`Record_ID`=`musicalkey_record`.`Record_ID` AND `
Key_Name` IN ('4F','Circle of fifths','C-Major') ORDER BY `Key_Name`;
This is a simplification of Barmar's approach, reducing the number of joins:
SELECT mk.Key_Name, Song_Title
FROM musicalkey_record mr JOIN
musical_keys mk
ON mk.Key_ID = mr.Key_ID JOIN
record r
ON r.Record_ID = mr.Record_ID JOIN
(SELECT Key_ID
FROM musicalkey_record mr
GROUP BY Key_ID
ORDER BY COUNT(*) DESC
LIMIT 3
) top3
ON mr.Key_ID = top3.Key_ID
ORDER BY mk.Key_Name;
I may be wrong, but it seems all those joins are not necessary. You want to count song records per musical key. So join musical_keys with musicalkey_record and count. To get the song names you would have to join with record, too, and use wm_concat to get the song names in one string.
SELECT *
FROM
(
SELECT mk.Key_Name, GROUP_CONCAT(r.Song_Title) as Song_Titles
FROM musical_keys mk
LEFT JOIN musicalkey_record mkr ON mkr.Key_ID = mk.Key_ID
LEFT JOIN record r ON r.Record_ID = mkr.Record_ID
GROUP BY mk.Key_Name
ORDER BY COUNT(*) DESC
LIMIT 3
) dummy
ORDER BY Key_Name;
EDIT: If you want to show all equally ranking records, i.e. at least three, but more if record four or more have the same count as record three, then you would have to get the top three, look up the third place and then select again to get all records with at least that count.
SELECT mk.Key_Name, GROUP_CONCAT(r.Song_Title) as Song_Titles
FROM musical_keys mk
LEFT JOIN musicalkey_record mkr ON mkr.Key_ID = mk.Key_ID
LEFT JOIN record r ON r.Record_ID = mkr.Record_ID
GROUP BY mk.Key_Name
HAVING COUNT(*) >=
(
SELECT MIN(cnt)
FROM
(
SELECT COUNT(*) as cnt
FROM musical_keys mk
LEFT JOIN musicalkey_record mkr ON mkr.Key_ID = mk.Key_ID
GROUP BY mk.Key_Name
ORDER BY COUNT(*) DESC
LIMIT 3
) dummy
)
ORDER BY Key_Name;
I suppose that the Key_Name is unique in musical_keys? Then you can even remove musical_keys from the inner select altogether and only select from musicalkey_record grouping by mkr.Key_ID instead of mk.Key_Name. Thus the query is even shorter.
SELECT mk.Key_Name, Song_Title
FROM musicalkey_record AS mr
JOIN musical_keys AS mk ON mk.Key_ID = mr.Key_ID
JOIN record AS r ON r.Record_ID = mr.Record_ID
JOIN (SELECT Key_Name
FROM FROM musicalkey_record AS mr
JOIN musical_keys AS mk ON mk.Key_ID = mr.Key_ID
JOIN record AS r ON r.Record_ID = mr.Record_ID
GROUP BY Key_Name
ORDER BY COUNT(Song_Title) DESC
LIMIT 3) AS top3 ON mk.Key_Name = top3.Key_Name
ORDER BY mk.Key_Name
I have this php mysql statement
SELECT a.*, p.filename, m.`first name`, m.`last name`, m.`mobile number`, m.`status`, m.`email address`
FROM map a
join members m on a.members_id = m.id
join pictures p on m.pictures_id = p.id
WHERE a.active = 1
GROUP BY a.members_id
order by a.`date added` DESC
limit 1;
However it's not working. The map table has records, and many of them can have the same members_id value. I want to group them by the members_id, then order them by date added, so the most recent is on top of each group, then only get the top row (i.e. get most recent of each group).
Does anyone know whats wrong here?
Thanks
Try:
select * from
(SELECT a.*,
p.filename,
m.`first name`, m.`last name`, m.`mobile number`, m.`status`, m.`email address`
FROM map a
join members m on a.members_id = m.id
join pictures p on m.pictures_id = p.id
WHERE a.active = 1
order by a.members_id, a.`date added` DESC) sq
GROUP BY members_id;
Note that the fact that MySQL returns the first row when grouping is not documented and may change in future releases - so although this query should work with current versions of MySQL, it is not guaranteed to do so in future.
If you want to get one result per map, you have to select it in two steps - so with a subquery. The inner query gets the newest map per member and the outer query gets all the data. Be careful with the indices, otherwise it will be very slow.
I think it will be something like:
SELECT a.*, p.filename, m.`first name`, m.`last name`, m.`mobile number`, m.`status`, m.`email address`
FROM map a
inner join members m on a.members_id = m.id
inner join pictures p on m.pictures_id = p.id
inner join (
select max(a.`date added`) as maxdate from map ia where ia.members_id = m.id)
) as sub_a on sub_a.member_id = a.member_id and sub_a.maxdate = a.`date added`
WHERE a.active = 1
That depends on a single maximal date added, otherwise you will need some more tricks.
One approach is to use a where clause to filter out the records you are not interested in:
SELECT a.*, p.filename, m.`first name`, m.`last name`, m.`mobile number`, m.`status`, m.`email address`
FROM map a
join members m on a.members_id = m.id
join pictures p on m.pictures_id = p.id
WHERE a.active = 1 and
a.`date added` = (select max(map.`date added`)
from map
where map.members_id = a.members_id and
map.active = 1
)
GROUP BY a.members_id
order by a.`date added` DESC;
You cannot do this in one query. Replace the from map part with a subselect
select max(map_id) as map_id, members_id, max(date_added) as date_added from map where active = 1 group by members_id
This will give you all the members with the last dates. I have assumed your map_id existing, and being auto_increment. Use this instead of the original map table, and you will not need the group by, order and limit parts at all.