Ok a little primer; I'm an expert PHP/JS/C developer but have never quite been able to get a comprehensive grasp on MySQL. It would be great if you could answer my question, but just as helpful if you could point me in the direction of good resources to learn about complex MySQL query do's and don'ts (mostly from an efficiency standpoint).
Objective
I need to find similarities/overlaps in a single table while still pulling the entire result set (to LEFT JOIN with the actual title/description content which is in another table).
The table is extremely simple; it contains 3 columns (page
, user
, time
).
Essentially each query will have two users. I need to pull the count of all results matching User 1
, the count of all results matching User 2
, and ALL columns (plus LEFT JOIN) for overlap (where both User 1
and User 2
have a match in the table.
Sample Query
This query works, but it's extremely slow (to the point where it takes minutes to run) and I'm guessing inefficient due to the subqueries. If any SQL experts can point out a more efficient way to do this (and why) it would be MUCH appreciated.
SELECT DISTINCT `page`,
(SELECT COUNT(*) FROM `m_likes` WHERE `user` = "1") AS userLikes,
(SELECT COUNT(*) FROM `m_likes` WHERE `user` = "2") AS friendLikes
FROM `m_likes` LEFT JOIN `app_pages` AS page ON (page.id = `page`)
WHERE `page` IN (SELECT `page` FROM `m_likes` WHERE `user` = "1")
AND `page` IN (SELECT `page` FROM `m_likes` WHERE `user` = "2")
AND (`user` = "1" OR `user` = "2")
EXPLAIN Query Results
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY m_likes index NULL page 604 NULL 35043 Using where; Using index; Using temporary
1 PRIMARY page eq_ref PRIMARY PRIMARY 767 tablename.m_likes.page 1
5 DEPENDENT SUBQUERY m_likes unique_subquery page page 604 func,const 1 Using index; Using where
4 DEPENDENT SUBQUERY m_likes unique_subquery page page 604 func,const 1 Using index; Using where
3 SUBQUERY m_likes index NULL page 604 NULL 35043 Using where; Using index
2 SUBQUERY m_likes index NULL page 604 NULL 35043 Using where; Using index
Table Schema
app_pages: id
VARCHAR(255), name
VARCHAR(255), category
VARCHAR(255)
m_likes: page
VARCHAR(255), user
VARCHAR(255), time
INT(20)
m_likes.page = app_pages.id
Also worth noting, unfortunately the User & Page IDs must be VARCHAR instead of INT, as there is no guarantee of this being run on a 64-bit system, and some of the ID values are larger than the max allowed on a 32-bit system... Hopefully that doesn't add a major performance hit.
Output Example
array (size=156)
0 =>
array (size=6)
'page' => string '100861973286778' (length=15)
'time' => string '1297383617' (length=10)
'name' => string 'Leila' (length=5)
'category' => string 'Book' (length=4)
'userLikes' => string '104' (length=3)
'friendLikes' => string '52' (length=2)
1 =>
array (size=6)
'page' => string '10150160788195604' (length=17)
'time' => string '1272653871' (length=10)
'name' => string 'Frisbee Golfing' (length=15)
'category' => string 'Interest' (length=8)
'userLikes' => string '104' (length=3)
'friendLikes' => string '52' (length=2)
The following recommendations will help you in your SQL tuning process.
You'll find 3 sections below:
SELECT
page,
userLikes,
friendLikes
FROM
((SELECT
DISTINCT `page` AS page,
(SELECT
COUNT(*)
FROM
`m_likes`
WHERE
`m_likes`.`user` = `m_likes`."1") AS userLikes,
(SELECT
COUNT(*)
FROM
`m_likes`
WHERE
`m_likes`.`user` = `m_likes`."2") AS friendLikes
FROM
`m_likes`
LEFT JOIN
`app_pages` AS page
ON (
page.id = `page`
)
WHERE
`page` IN (
SELECT
`m_likes`.`page`
FROM
`m_likes`
WHERE
`m_likes`.`user` = `m_likes`."1"
)
AND `page` IN (
SELECT
`m_likes`.`page`
FROM
`m_likes`
WHERE
`m_likes`.`user` = `m_likes`."2"
)
AND (
`user` = "2"
)
)
UNION
DISTINCT (SELECT
DISTINCT `page` AS page,
(SELECT
COUNT(*)
FROM
`m_likes`
WHERE
`m_likes`.`user` = `m_likes`."1") AS userLikes,
(SELECT
COUNT(*)
FROM
`m_likes`
WHERE
`m_likes`.`user` = `m_likes`."2") AS friendLikes
FROM
`m_likes`
LEFT JOIN
`app_pages` AS page
ON (page.id = `page`)
WHERE
`page` IN (SELECT
`m_likes`.`page`
FROM
`m_likes`
WHERE
`m_likes`.`user` = `m_likes`."1")
AND `page` IN (SELECT
`m_likes`.`page`
FROM
`m_likes`
WHERE
`m_likes`.`user` = `m_likes`."2")
AND (`user` = '1'))
) AS union1