Advice on Optimizing Joins Query - php

I have 3 tables : videos, categories, video_categories.
In videos, I have id, title, and other fields.
In categories, I have id and name.
In video_categories, I have id, video_id, and category_id.
One video can have multiple categories. So the video_categories table will be something like this.
id video_id category_id
1 1 1
2 1 2
3 1 3
If I want to have a list of videos and display their categories, which would be preferred?
Via PHP, call 1 query to get all videos, then loop on that to query to get each video's categories, and another query to get the category name. This will be really slow if the table is huge, right?
Via MySQL joins (need help on this). If I left join videos to video_categories, there will be 3 results of the same video_id. I can use GROUP BY or SELECT DISTINCT to get unique result, but how can I now get the categories' names?
My expected result will be something like this:
id title categories
1 Video1 pop, rock, jazz

For option 2, use GROUP_CONCAT. It will be ok
SELECT v.id, v.title, GROUP_CONCAT(c.name)
FROM videos v
INNER JOIN video_categories vc ON vc.video_id = v.id
INNER JOIN categories c ON vc.category_id = c.id
GROUP BY v.id, v.title
For Group_Concat() function , is the default separator. That's why I don't use it here.

Guessing the names of your other columns..
SELECT v.video_id, v.title, GROUP_CONCAT(c.category_name SEPARATOR ', ')
FROM videos v
LEFT JOIN video_categories vc ON vc.video_id = v.video_id
LEFT JOIN categories c ON c.category_id = vc.category_id
GROUP BY v.video_id, v.title

Related

Mysql multiple join between two tables

I have got two tables. One is for news and second one for images. Each news can have 0-3 images (image_1, image_2, image_3 in table news - its id). Now iam trying to get all rows from images table but its giving me back only one.
Like that (but it is not working)
select news.id as nid, image_1, image_2, image_3, photos.id as pid, big, small
from news
left join photos
on image_1=photos.id, image_2=photos.id, image_3=photos.id
order by nid desc
Even #juergen has suggested better option and also guided you how to solve your problem in your way but if stil you are facing issue how to do then you can follow below query-
SELECT p.id AS pid, n1.image_1, n2.image_2, n3.image_3, big, small
FROM photos AS p
LEFT JOIN news AS n1 ON n1.image_1=p.id
LEFT JOIN news AS n2 ON n2.image_2=p.id
LEFT JOIN news AS n3 ON n1.image_3=p.id
ORDER BY n.id DESC;
You have to join the photos table 3 times with different aliases.
But you actually should rather change your table design. Add another table called news_photos
news_photos table
-----------------
news_id
photo_id
Then you can remove the image columns from the news table.
After the changes you can select news with all photos of like that
select n.*, p.name
from news
left join news_photos np on n.id = np.news_id
left join photos p on p.id = np.photo_id
where n.id = 1234

Combining multiple mySQL rows without duplicates

I have 4 tables in a MySQL database, suppliers, categories, subcats & listings.
listings is a join table to allow the many to many relationships between suppliers, categories and subcats, the structure of each is as follows
suppliers
sp_id sp_name sp_email
1 Apple info#apple.co
2 Samsung info#samsung.co
categories
cat_id cat_name
3 Electronics
4 Software
subcats
subcat_id subcat_name cat_id
5 Mobiles 3
6 Computers 3
listings
list_id sp_id subcat_id
1 1 5
2 1 6
I am trying to combine and extract the data together so there is only one entry per supplier with multiple subcategories listed eg:
RESULT
sp_id sp_name sp_email cat_name / cats subcat_name / subcats
1 Apple info#apple.co Electronics, Software Mobiles, Computers
2 Samsung info#samsung.co Electronics Mobiles
Currently I have the following query
SELECT *
FROM suppliers as s
LEFT JOIN listings as l ON s.sp_id=l.sp_id
LEFT JOIN subcats as p ON p.subcat_id=l.subcat_id
LEFT JOIN categories as c ON c.cat_id=p.cat_id
ORDER BY s.sp_id
However this outputs multiple entries per supplier, one entry for each category or subcategory associated with it. Is there an easier way to do it via SQL or with multiple queries in PHP?
I am at the limits of my current knowledge of mySQL and any suggestions or prods in the right direction would be greatly appreciated.
use GROUP_CONCAT
SELECT s.*,
GROUP_CONCAT(c.cat_name) catName,
GROUP_CONCAT(p.subcat_name) subcatName
FROM suppliers as s
LEFT JOIN listings as l ON s.sp_id=l.sp_id
LEFT JOIN subcats as p ON p.subcat_id=l.subcat_id
LEFT JOIN categories as c ON c.cat_id=p.cat_id
GROUP BY s.sp_id, s.sp_name, s.sp_email
ORDER BY s.sp_id
You can use GROUP BY in combination with GROUP_CONCAT to only show unique values from a certain column with a comma-separated list as one of the values.
You want to use the GROUP BY expression along with the GROUP_CONCAT function. This will allow you to merge together rows which share certain values while concatenating the ones they don't. For example, in your case this may wind up being something like:
SELECT s.sp_id, s.sp_name, group_concat(', ', c.cat_name)
FROM suppliers as s
LEFT JOIN listings as l ON s.sp_id=l.sp_id
LEFT JOIN subcats as p ON p.subcat_id=l.subcat_id
LEFT JOIN categories as c ON c.cat_id=p.cat_id
ORDER BY s.sp_id
GROUP BY s.sp_id, s.sp_name;

Wrapping my head around what I assume is a complicated MySQL query

I have a table called categories and a table called business_categories_coupling. In Categories, you have the usual id, name, parent. In the Coupling table, you have business_id and category_id. Each business can have multiple categories, so I store them in that table. It kinda looks like this:
business_id category_id
73 80
73 81
73 90
74 4
74 10
Right now, my query is just selecting all the categories, doing a foreach and doing a db query in each loop to find how many businesses are in that category. Obviously not the right way to go about it.
Is there a way to do a SQL query that basically selects all the categories, gets the number of times it comes up in the coupling table, and add a count to each category?
SELECT
C.*
FROM
CATEGORIES AS C
LEFT JOIN
BUSINESS_CATEGORIES_COUPLING AS B
ON
C.id = B.category_id;
Kinda like that, but with a count somewhere. I've tried various setups but nothing works like I want. Any suggestions?
EDIT 1
Solution as provided by #phani-rahul, but I added a WHERE clause:
SELECT cat.id AS id, cat.name AS name, cat.slug AS slug, COUNT(cat.id) AS business_count
FROM categories AS cat
LEFT JOIN business_categories_coupling AS coupling ON cat.id=coupling.category_id
WHERE coupling.category_id IS NOT NULL
GROUP BY cat.id
Yes, there is.
you can use Group by clause:
select a.id as category, count(a.id) as count_of_category
from categories a
left join business_categories_coupling b on a.id=b.category_id
group by a.id
your result would be something like:
category count_of_category
80 2
81 5
90 1
. .
. .
. .
You also need to GROUP by the fields of C table.
SELECT C.id, C.field1, C.field2, COUNT(*)
FROM CATEGORIES AS C
LEFT JOIN BUSINESS_CATEGORIES_COUPLING AS B
ON (C.id = B.category_id)
GROUP BY C.id, C.field1, ...
(In MySQL you can GROUP BY the single value C.id; in other SQL dialects you can express the concept of "grouping by rows of C table" by grouping by "C.*"; in some others you need to specify all non-aggregate columns of your query, in this case all columns you select from C, one by one).
What you're looking for is a GROUP BY clause.
SELECT
C.*, count(C.id)
FROM
CATEGORIES AS C
LEFT JOIN
BUSINESS_CATEGORIES_COUPLING AS B
ON
C.id = B.category_id
GROUP BY B.category_id;

Category post count

I am building a blog with Codeigniter and MySQL. The question I have is this, I have a table with posts and one with categories. I also have a cross reference table with post_categories. What I am trying to do is get all the categories with their names and the number of posts they have under their name.
Example output would be: Hello World(1) Test(0) etc.
What I am having a hard time finding is a SQL query that will join the three tables and get me the counts, and I am also having a hard time wrapping my head around how to make that query.
Here is my table schema:
blgpost
====
id
*Other schema unimportant
blgpostcategories
=================
postid
categoryid
blgcategories
==========
id
name
*Other schema unimportant
This should give you the output you want....
SELECT c.name, COUNT(p.id) FROM
blgcategories c
INNER JOIN blgpostcategories pc ON c.id = pc.categoryid
INNER JOIN blgpost p ON pc.postid = p.id
GROUP BY c.id
You don't need to join the three tables - the blgpost table doesn't have any information in it that you need.
SELECT COUNT(*), blgcategories.name
FROM blgcategories INNER JOIN blgpostcategories
ON blgcategories.id=blgpostcategories.categoryid
GROUP BY blgcategories.id;
SELECT name, COUNT(pc.id)
FROM blgcategories c
LEFT JOIN
blgpostcategories pc
ON pc.categoryid = c.id
GROUP BY
c.id
Using LEFT JOIN will show 0 for empty categories (those without posts linked to them) rather than omitting them.

SQL Query over three different tables

i got three tables
CATS
id name
------------------------------
1 category1
2 category2
3 category3
4 category4
PRODUCT
id name
------------------------------
1 product1
2 product2
ZW-CAT-PRODUCT
id_cats id_product
------------------------------
1 1
3 1
4 2
now i want to get my products and their categories
product1 => category1,category3
product2 => category4
is there a way to get this array (or object or something) with one mysql query?
i tried a bit with JOINS, but it seems thats this is not exactly what i need, or?
currently i'm using 3 querys (i think thats too much).
any suggestions?
edit
and on the other way, what if i want to get ALL products of a specific category?
can this also be done in one query?
You can use GROUP_CONCAT to get a separated list in your results.
SELECT p.*,
GROUP_CONCAT(c.name SEPARATOR ',') as cats
FROM PRODUCT p
LEFT JOIN ZW-CAT-PRODUCT l
ON l.id_product=p.id
LEFT JOIN CATS c
ON c.id=l.id_cats
GROUP BY p.id
So basically, this first does some joins to get all the data. If you were to replace the GROUP_CONCAT line with just c.name, you would see a row for each product_id/category pair. The GROUP BY tells it to group results based on product ID, and then GROUP_CONCAT(c.name..) is telling to it take all the different c.name values that occur in a group (so for each product ID, since you're grouping by product ID) and concatenate those values into one string, using , as the separator.
So to get all products for a each category in the same style, it would be like this,
SELECT c.*,
GROUP_CONCAT(p.name SEPARATOR ',') as products
FROM CATS c
LEFT JOIN ZW-CAT-PRODUCT l
ON l.id_cats=c.id
LEFT JOIN PRODUCT p
ON p.id=l.id_product
GROUP BY c.id
EDIT: To get just the product rows for a particular category (as requested in comment), it's this.
SELECT p.*
FROM PRODUCT p
LEFT JOIN ZW-CAT-PRODUCT l
ON l.id_product=p.id
LEFT JOIN CATS c
ON c.id=l.id_cats
WHERE c.name='xyz';
If you need just comma-separated list of categories for every product, look at MySQL's GROUP_CONCAT() aggregate function:
SELECT p.*, GROUP_CONCAT(c.name) AS categories
FROM PRODUCT p
LEFT JOIN ZW-CAT-PRODUCT cp ON p.id = cp.id_product
LEFT JOIN CATS c ON cp.id_cats = c.id
GROUP BY p.id
To get all products of a specific category (by category ID):
SELECT p.*
FROM PRODUCT p
INNER JOIN ZW-CAT-PRODUCT cp ON p.id = cp.id_product
WHERE cp.id_cats = 42
The same, but by category name:
SELECT p.*
FROM PRODUCT p
INNER JOIN ZW-CAT-PRODUCT cp ON p.id = cp.id_product
INNER JOIN CATS c ON cp.id_cats = c.id
WHERE c.name = 'category1'
You could fit all 3 into 1 query yes, but consider having 3 huges tables, i'd rather process them one by one instead of getting the whole bulk back in 1 time.
This takes longer, but is (in my opinion) more data friendly.

Categories