Selecting rows from multiple tables. How? - mysql

Let's say that we have tree tables.
Products Fields Fields Value
---------------- ------------- --------------
pid catid fid catid fid pid value
-------|-------| -----|------- ------|-----|--------
1 1 1 1 1 1 25%
2 1 2 1 1 2 32.5%
3 2 2 1 45%
2 2 42%
3 1 17.3%
3 2 21%
The normal way is selecting Products in a one query and loop through result set(RS1).
Then we select Fields for catid per each row (RS2).
Then doing the same action with RS2 for selecting `Fields Value'.
Only problem is performance issue that will be reduced due to executing a lot of queries` when there are a lot of rows in each table.
Would you suggest me better solution to execute less queries ?
edit
I want to show each product in a box and show fields for each product with it's proper value. joining tree tables together will returns duplicated values for each FieldValue in Products and not usable in loop.

Guessing what you need, try this:
SELECT f.catid, fv.* FROM Fields f
INNER JOIN Products p
ON f.catid = p.catid
INNER JOIN FieldsValue fv
ON fv.fid = f.fid AND fv.pid = p.pid

SELECT *
FROM Products
NATURAL JOIN Fields
NATURAL JOIN FieldsValue;

use Join Syntax :
SELECT * FROM Products as P
LEFT JOIN FieldsValue as FV ON FV.PID = P.PID
LEFT JOIN Fields as F on F.fid = FV.fid

You can join the tables together using left join:
select *
from Products p
left join Fields f on f.catid = p.catid
left join `fields value` fv on fv.fid = f.fid on fv.pid = p.pid
where p.pid = 1

Related

Filtering on a LEFT JOINED column

Is there a more efficient way to filter on a joined table as in the following example? Or is this a fine approach? This query returns the desired results, but I am an amateur at MySQL.
I have indexes on products.id, product_details.product_id and product_details.value
SELECT p.id
FROM products p
LEFT
JOIN product_details d
ON d.product_id = p.id
WHERE d.value = 1
OR p.id = 4
Simplified structure as follows:
products table
product_id (PRIMARY KEY) | name
--------------------------------
1 | Shirt
2 | Shoes
3 | Dress
4 | A product with no corresponding details row
product_details table
product_id (PRIMARY KEY) | value
---------------------------------
1 | 1
2 | 23
3 | 32
This is your query:
SELECT products.id
FROM products LEFT JOIN
product_details
ON product_details.product_id = products.id
WHERE product_details.value = 1 OR products.id = 4;
This is not a bad practice. I do think the query is easier to follow using EXISTS:
SELECT p.id
FROM products p
WHERE p.id = 4 OR
EXISTS (SELECT 1
FROM product_details pd
WHERE pd.product_id = p.id AND pd.value = 1
);
In addition EXISTS makes it clear that you don't want to return duplicates if there are duplicate matching rows in product_details.
If performance is you main consideration, then EXISTS is probably your best choice, with an index on product_details(product_id, value).
Couple of notes:
As a rule of thumb, a UNION ALL statement performs better than an OR operator. Also, this helps clear up the query.
Using both an implicit JOIN and a predicate in the WHERE clause on the same table can get you into trouble - especially if you're using a LEFT OUTER JOIN (the predicate in the WHERE clause has precedence over the LEFT OUTER JOIN).
Seems like you always want to pull back any records that has a products.id = 4, and also any products that have a product_details.value = 1. This seems like two separate queries to me, and splitting it would probably make it easier to maintain in the future.
SELECT
p.id
FROM
products p
WHERE
p.id = 4
UNION ALL
SELECT
p.id
FROM
product_details pd
JOIN
products p
ON
p.id = pd.product_id
WHERE
pd.value = 1
Source: https://bertwagner.com/posts/or-vs-union-all-is-one-better-for-performance/

how to get result of join also for records that dont have a joined record with another table?

I have this query:
SELECT suppliers.id, count(*)
FROM suppliers
INNER JOIN supplier_addresses
ON suppliers.id = supplier_addresses.supplier_id
GROUP BY suppliers.id;
this gives my a table of supplierId and count of its addresses in the supplier_addresses table. But it only shows me suppliers that have at least 1 address.
I want to see in the result also count of 0 addresses...for example:
supplier.id | count(*)
1 3
2 0
3 1
4 9
in my query I dont see the second record.
Use LEFT JOIN
SELECT suppliers.id, count(supplier_addresses.supplier_id )
FROM suppliers
LEFT JOIN supplier_addresses
ON suppliers.id = supplier_addresses.supplier_id
GROUP BY suppliers.id;

Select data based on another table

I have three tables, I'll just list the important columns
db_players
id | name
players
id | teamid | careerid
db_teams
The db_teams id links to the players teamid.
I need to run a query where I select rows from db_players as long as db_teams.id isn't in a row in players as teamid where the careerid = 1.
I've never attempted this type of query with mysql before, I know I could do two queries and involve php but I'm intrigued as to whether it's possible with a pure db query.
Thanks.
EDIT - simpler now.
SELECT dp.first_name
FROM tbl_foot_career_db_players dp
INNER JOIN tbl_foot_career_players p
ON p.playerid != dp.id
WHERE p.careerid = 1
The idea is that I want to return all rows from tbl_foot_career_db_players WHERE the id from that table isn't present in a row in tbl_foot_career_players in the column playerid. And the tbl_foot_career_players.careerid must also equal 1.
List all db_players that are not in players with career = 1
SELECT d.*
FROM db_players d
LEFT JOIN players p
ON p.player_id = d.id
AND p.career = 1
WHERE p.id IS NULL
Try to JOIN them with db_players id is not in players teamid.
SELECT db_players.* FROM db_players
LEFT JOIN players ON players.id != db_players.id
LEFT JOIN db_teams ON players.teamid = db_teams.id
WHERE careerid = 1

Select Rows From Second Table Based On id From First

I have 2 tables
1_products
id, code, make, model, fk_group_id
1_stock
id, stock, repair
I want to be able to return all of the rows in both tables based on the match in the first. Say WHERE fk_group_id = 11
Here is one:
SELECT *
FROM products AS p
INNER JOIN stock AS s ON p.id = s.id
WHERE fk_group_id = 11

mySQL SELECT FROM table WHERE ... AND ... AND ... AND

I have a table "articles" with columns and data:
article_id title body
1 This is the title This is the body text
2 Another title Another body text
Another table "category" with columns and data:
category_id category
1 localnews
2 visible
3 first10
And a table "categories" with columns and data:
categories_id article_id category_id
1 1 1
2 1 2
3 1 3
4 2 1
5 2 3
I want to SELECT the row(s) WHERE categories.category_id = 1 AND =2 AND =3
I'm using:
SELECT articles.article_id, articles.title, articles.body,
categories.article_id, categories.category_id
FROM articles, categories
WHERE articles.article_id = categories.article_id
AND categories.article_id = 1
AND categories.article_id = 2
AND categories.article_id = 3
but it doesn't work. Obviously mySQL needs another syntax.
Can someone help?
Thanks
SELECT
Articles.article_id,
COUNT( Categories.article_id ) AS total
FROM CategoryArticles
LEFT JOIN Articles USING (article_id)
WHERE
CategoryArticles.category_id IN (1,2,3)
GROUP BY CategoryArticles.article_id
HAVING total = 3
I used a bit different names for table because in your example the distinction between category and categories is hard to notice.
An column of a row cannot be 1, 2 or 3 at the same time, which is what AND stipulates. Use OR in your WHERE condition. Better yet - for readability - you can use IN:
SELECT ...
WHERE `categories`.`article_id` IN(1,2,3)
In addition to the commonly used IN() and using a HAVING count, I would be interested in the performance difference by doing a multiple-join as follows...
SELECT STRAIGHT_JOIN
articles.article_id,
articles.title,
articles.body
FROM
categories c1
JOIN articles
on c1.article_id = articles.article_id
JOIN categories c2
on c1.article_id = c2.article_id
AND c2.category_id = 2
JOIN categories c3
on c1.article_id = c3.article_id
AND c3.category_id = 3
WHERE
c1.Category_ID = 1
Yes, this may look obscure, but lets think about it... by doing a join FIRST on the categories table where ONE of your specific categories -- THIS FIRST FROM instance of categories should be representative of whichever category would have the smallest granularity. Ex: Your categories of Local News, Visible and First 10. Local news would probably have the most entries, while Visible and First 10 would have even less... of those, which would have even the smallest number of records. Use THIS category as the where clause.
So, say you have 100,000 articles, and 90,000 are in local news, 45,000 in Visible, and 12,000 in First 10. By starting your query on only those in the 12,000, you are eliminating most of the data.
By then joining to the articles table, and categories AGAIN as alias C2 and C3 respectively based on the other conditions, if found, done, if not, its excluded.
Again, I'm wondering the performance impact. I would also have a compound index on the categories table on both (article_id, category_id)
The value cannot be all three values simultaneously, so you'd better use an IN clause in your WHERE to define which you want to return. Give you've already got a join condition there, you'd want to move that to an ON clause instead as well; ie:
SELECT articles.article_id, articles.title, articles.body, categories.article_id, categories.category_id
FROM articles
INNER JOIN categories ON articles.article_id = categories.article_id
WHERE categories.article_id IN ( 1, 2, 3 )
Of course, you can go to the next step and do:
SELECT articles.article_id, articles.title, articles.body, category.category
FROM articles
INNER JOIN categories ON articles.article_id = categories.article_id
INNER JOIN category ON categories.category_id = category.category_id
WHERE categories.article_id IN ( 1, 2, 3 )
If instead you wanted to show only articles that appear in all three categories, then you could take an approach like:
SELECT articles.article_id, articles.title, articles.body
FROM articles
INNER JOIN categories AS c1
ON articles.article_id = c1.article_id
AND c1.category_id = 1
INNER JOIN categories AS c2
ON articles.article_id = c2.article_id
AND c2.category_id = 2
INNER JOIN categories AS c3
ON articles.article_id = c3.article_id
AND c3.category_id = 3