Reputation: 5
We have a videos table with several million lines, we would like to select only the videos of a few categories and a few tags.
Is there a way to do it in a more optimized way than this query?
Thank you for your help and feedback.
SELECT videos.id, videos.title FROM videos
WHERE videos.active = 1 AND videos.deleted IS NULL AND videos.segment = 1
AND (
videos.id IN(
SELECT videos.id FROM videos
INNER JOIN categories_videos on categories_videos.video_id = videos.id
INNER JOIN categories on categories.id = categories_videos.category_id AND categories.id = 59
WHERE videos.active = 1 AND videos.deleted IS NULL AND videos.segment = 1
)
OR videos.id IN(
SELECT videos.id FROM videos
INNER JOIN tags_videos on tags_videos.video_id = videos.id
INNER JOIN tags on tags.id = tags_videos.tag_id AND tags.id = 231014
WHERE videos.active = 1 AND videos.deleted IS NULL AND videos.segment = 1
)
)
Upvotes: 0
Views: 166
Reputation: 142453
How many Categories can a video be associated with? How many Tags? If both are more than 1, there is very little excuse to keep them separate.
Using a many-to-many table to match Tags with Videos is costly. (I assume the tags include actor(s), director(s), genre(s), location(s), etc. That could easily lead to 100M rows in that table.
You may want to match 2 tags at once. Performance takes another hit.
If there is only one Category, put it in the video table -- no Join needed.
I have found that things like "Tag" should have the string in the many-to-many table, thereby merging tags_videos
and tags
tables:
CREATE TABLE `tags` (
video_id INT UNSIGNED NOT NULL,
tag VARCHAR(100) NOT NULL,
PRIMARY KEY(video_id, tag), -- useful for getting tags for a video
INDEX(tag, video_id) -- useful for getting videos for a tag
) ENGINE=InnoDB;
-- No auto_increment; it would just be clutter and slow things down
When searching for either of 2 tags:
FROM videos AS v
JOIN tags AS t
WHERE t.tag IN ('comedy', 'John Wayne')
Upvotes: 0
Reputation: 522346
I would just express this as a union query:
SELECT v.id, v.title
FROM videos v
INNER JOIN categories_videos cv ON cv.video_id = v.id
INNER JOIN categories c ON c.id = cv.category_id AND c.id = 59
WHERE v.active = 1 AND v.deleted IS NULL AND v.segment = 1
UNION ALL
SELECT v.id, v.title
FROM videos v
INNER JOIN tags_videos tv ON tv.video_id = v.id
INNER JOIN tags t ON t.id = tv.tag_id AND t.id = 231014
WHERE v.active = 1 AND v.deleted IS NULL AND v.segment = 1;
Upvotes: 1