mySQL multiple Joins for 5 tables with timeout errors - mysql

I have 5 tables:
Categories_groups_cat1
id|order|title(text)
Categories_vs_groups
id|categories_groups_cat1_id|categories_id
Categories
id|title(text)
Offers
id|category (text)
Coupons
id|category (text)
I want to display titles from Categories_groups_cat1 only if:
Exists at least one row in Categories_vs_groups via categories_groups_cat1_id column (Categories_vs_groups.categories_groups_cat1_id==Categories_groups_cat1.id)
AND categories_id from Categories_vs_groups exists as at least one row to table Categories (Categories.id==Categories_vs_groups.categories_id) AND where have at least one row in table Offers or Coupons via category column (offers.category==categories.title)!!
I do the following but I have timeout because the tables offers or coupons is more tha 500000.
SELECT
offers.category_gr,
categories.id, categories.title_gr,
categories_vs_groups.categories_id,
categories_vs_groups.categories_groups_cat1_id AS cat1,
categories_vs_groups.categories_groups_cat2_id AS cat2
FROM offers
LEFT JOIN categories
ON categories.title_gr=offers.category_gr
LEFT JOIN categories_vs_groups
ON categories_vs_groups.categories_id=categories.id
WHERE categories.title_gr='$row_best_offer[category_gr]'
GROUP BY categories.id
order by categories.id

If you have the right set-up, your query should not take more than 60 seconds.
Your where clause is undoing the first LEFT JOIN (and you probably don't need the second either. Because you are aggregating by categories, I would suggest starting with that. Then, the offers table is only needed for filtering, so you should consider whether it is really necessary.
So, consider this query:
SELECT c.category_gr, c.id, c.title_gr,
cvg.categories_id, cvg.categories_groups_cat1_id AS cat1,
cvg.categories_groups_cat2_id AS cat2
FROM categories c JOIN
offers o
ON c.title_gr = o.category_gr LEFT JOIN
categories_vs_groups cvg
ON cvg.categories_id = c.id
WHERE c.title_gr = '$row_best_offer[category_gr]'
GROUP BY c.id
ORDER BY c.id;
For best results, you want indexes on categories(title_gr, category_gr, id), offers(category_gr), and categories_vs_groups(categories_id).

instead of using condtion WHERE categories.title_gr='$row_best_offer[category_gr]' in where clause, put it in On clause
First left join is unnecessary.
SELECT
offers.category_gr,
categories.id, categories.title_gr,
categories_vs_groups.categories_id,
categories_vs_groups.categories_groups_cat1_id AS cat1,
categories_vs_groups.categories_groups_cat2_id AS cat2
FROM offers
JOIN categories
ON
(categories.title_gr=offers.category_gr
and categories.title_gr='$row_best_offer[category_gr]' )
LEFT JOIN categories_vs_groups
ON categories_vs_groups.categories_id=categories.id
GROUP BY categories.id
order by categories.id

Related

mysql join on product category multiple relation query

I have three tables as below, each product may belong to multiple categories.
PRODUCT TABLE AS P
1. ID
2. NAME
CATEGORY TABLE AS C
1. ID
2. NAME
RELATION TABLE AS R
1. ID
2. P_ID
3. C_ID
Now I want to get a list of all products in product table, and with their belonging category name display as well.
How do I write this query?
I can get the category ids pulled from the same query, but don't know how to get all the name pulled as well. Here's what I tried.
select p.*,y.*
from p
left join (select p_id,group_concat(c_id) as category_ids
from relation group by p_id) as y on p.id=y.p_id
Do both JOIN operations (to the relation table, and from there to the table containing the category names) and feed the result to your aggregation function (GROUP_CONCAT)
SELECT P.Name, GROUP_CONCAT(DISTINCT C.Name ORDER BY C.Name SEPARATOR '|') categories
FROM Product P
LEFT JOIN Relation R ON P.ID = R.P_ID
LEFT JOIN Category C ON R.C_ID = C.ID
GROUP BY P.ID, P.Name
This will give you one row per product with the categories separated by |.
This uses LEFT JOIN operations so it won't suppress products that have no categories.
Select P.Name, C.Name
From RELATION R Inner Join PRODUCT P
On R.P_ID=P.Id
Inner Join Category C
On C.Id=R.C_ID
This query will get you all the products, with their corresponding category.
I want to give you a small explanation about the difference between Inner Join and Left Join.
If we take as an example 2 tables :
TA(idA, description) and TB(idB, idA, description).
Select TA.description, TB.description
From TA Inner Join TB On TA.IdA = TB.IdA
will get only the rows in TA, that have a corresponding one in TB.
On the other side,
Select TA.description, TB.description
From TA Left Join TB On TA.IdA = TB.IdA
will get all the rows of TA and if the row in TA doesn't have a corresponding one in TB, TB.description for this row will be NULL.
Hope this helps!

SQL Join involving 3 tables, how to?

SQL newbie here.
So we have 3 tables:
categories(cat_id,name);
products(prod_id,name);
relationships(prod_id,cat_id);
It is a one-to-many relationship.
So, given a category name say "Books". How do I find all the products that come under books?
As an example,
categories(1,Books);
categories(2,Phones);
products(302,Sherlock Holmes);
relationships(302,1);
You need to JOIN the three tables.
SELECT p.*
FROM relationships r
INNER JOIN products p
ON p.prod_id = r.prod_id
INNER JOIN categories c
ON c.cat_d = r.cat_id
WHERE c.name = 'Books'
You have to join tables on related columns and specify WHERE clause to select all records where category name = 'Books'
SELECT p.*
FROM categories c
JOIN relationships r ON c.cat_id = r.cat_id
JOIN products p ON r.prod_id = p.prod_id
WHERE c.name = 'Books' -- or specify parameter like #Books
In SQL you often join related tables and beginners tend to join, whatever the situation. I would not recommend this. In your case you want to select products. If you only want to show products data, select from products only. You want to select products that are in the category 'Books' (or for which exists an entry in category 'Books'). Hence use an IN or EXISTS clause in order to find them:
select * from products
where prod_id in
(
select prod_id
from relationships
where cat_id = (select cat_id from categories where name = 'Books')
);
Thus you get a well structured query that tells the reader easily how the tables are related and what data you are actually interested in. Later, with different tables and data to select, this may keep you from duplicate result rows that you must get rid of by using DISTINCT or from getting wrong aggregates (sums, counts, etc.), because of mistakenly considering records multifold.
try this:
select p.Prod_id,p.name
from products p inner join relationships r on
p.prod_id = r.prod_id
where r.cat_id = (select cat_id from categories where name = 'books')
or
select p.Prod_id,p.name
from products p inner join relationships r on
p.prod_id = r.prod_id inner join categories c on c.cat_id = r.cat_id
where c.name = 'books'

using JOIN and subquery in mysql

I posted a question about 2 weeks ago about 'one to many' relation between SQL tables. Now I have a bit of a different scenario. Basically, there are two tables - coffee_users and coffee_product_registrations. The latter is connected to coffee_users table with 'uid' column. So basically coffee_users.uid = coffee_product_registrations.uid
A single user can have multiple products registered.
What I want to do is to display some product information (from coffee_product_registrations) along with some user information (from coffee_users), BUT retrieve only those rows that have more than 1 product registrations.
So to simplify, here are the steps I need to take:
Join two tables
Select users that have multiple products registered
Display all their products along with their names and stuff
My current SQL query looks like this:
SELECT c.uid, c.name, cpr.model
FROM coffee_users c
JOIN coffee_product_registrations cpr on c.uid = cpr.uid
GROUP BY c.uid
HAVING COUNT(cpr.uid) > 1
This joins the two tables on 'uid' column but displays only 1 row for each user. It selects just users that have multiple products registered.
Now I need to take these IDs and select ALL the products from coffee_product_registrations based on them.
I cannot figure out how to put this in one query.
Replace cpr.*, c.* with columns which you want to extract feom the query
Try this:
SELECT cpr.*, c.*
FROM coffee_product_registrations cpr
INNER JOIN coffee_users c ON c.uid = cpr.uid
INNER JOIN (SELECT cpr.uid
FROM coffee_product_registrations cpr
GROUP BY cpr.uid
HAVING COUNT(DISTINCT cpr.productId) > 1
) AS A ON c.uid = A.uid;

mysql query with multiple left join in 3 tables?

i have a search query which will retrieve information from 3 tables i made the query so it retrieve the information from 2 tables and i don't know if i can combine the third one or not
SELECT *
FROM articles
INNER JOIN terms
ON articles.ArticleID = terms.RelatedID AND terms.TermType = 'article'
the third query is
SELECT * FROM categories where CategoryID in (something)
something is a filed in the articles tables which have value like '3,5,8'
i want do this 2 queries into 1 query and i don't know if it can be done by 1 query or not
without looking at your schema (which would be helpful) and some sample data try this query
SELECT *
FROM categories,articles
INNER JOIN terms
ON (articles.ArticleID = terms.RelatedID AND terms.TermType = 'article')
WHERE
FIND_IN_SET(categories.CategoryID,articles.categories)
here is the definition for FIND_IN_SET()
http://dev.mysql.com/doc/refman/5.0/en/string-functions.html#function_find-in-set
If i understand you correctly. Looks like you have multiple categories for each article with the Category IDs all stored as a concatenated string.
SELECT A.*
FROM articles A
INNER JOIN terms T on A.ArticleID = T.RelatedID AND T.TermType = 'article'
LEFT JOIN categories C on C.CategoryID IN (3,5,8 OR A.CategoryIDs)
GROUP BY C.CategoryName
You want to LEFT JOIN since you may or may not have multiple categories, you can group by Categories to get disticnt category article pairs and CONCAT() to recombine article records as needed.

problem with the join query

SELECT *
FROM
productinfo as p ,
category as c
WHERE
c.id IN (p.category) AND
p.pid='T3'
WHERE p.category will return (1,2,3,4,5) from product info table which the id of the category.
Now i need category name used for T3 [product Id] ,but i am getting only the first category name.
Your base query is the following
SELECT * FROM productinfo as p WHERE p.pid = 'T3';
Now you need to pull in categories, per product. This is a many to one relationship, so you need a LEFT JOIN.
SELECT * FROM productinfo as p
LEFT JOIN category as c ON c.id = p.category
WHERE p.id = 'T3'
You need to learn the different types of joins and how they are used. Whenever I see someone use 'FROM table1, table2' 90% of the times it means they don't understand joins and they need a LEFT JOIN instead.
Edit based on your comment
Your datamodel is flawed. Since a product can contain multiple categories, this is really a many-to-many relationship. You should create a product_category table that connects product id's with category id's.