I am trying to get data from reviews table then, join the same table again but find different data (likes) based on the id of the first table (reviews) and find the corresponding description from yet another joined table (descriptions).
I know this might be hard to visualize but maybe someone will know why the likes from the second reviews table return the same data for different rows:
SELECT r.title, co.likes, d.description
FROM reviews r
INNER JOIN reviews co
INNER JOIN reviews_descriptions d
ON co.id = d.review_id
WHERE co.parent = 52
AND r.id = 52;
The result is two rows in which title and likes have the same data while the description field grabs different data (the correct way). Likes should have different data for each row.
Help please.
An alternative for writing this and I believe this is what you are looking for:
SELECT r.title, co.likes, d.description
FROM reviews r, reviews co, review_descriptions d
WHERE co.parent = r.id AND d.review_id = co.id AND r.id = 52;
Since you are matching co.parent to 52 and r.id to 52, then you should have a join on co.parent = r.id, however, you should rarely have to do a join on the same table. I think your tables are poorly formatted or you don't need to do the join in the first place and should be able to use r.likes instead of co.likes.
Related
I have three tables, tblPresents, tblPresentsOrdered and tblPresentsDelivered.
What I want to do is sum up all the orders and deliveries for a given present ID, so I can tally up the total ordered and delivered and check for discrepancies.
So far I have the following:
$sql ='SELECT prsName, SUM(ordQuantity) AS qtyOrdered,
SUM(delQuantity) AS qtyDelivered
FROM tblPresentOrders
LEFT JOIN tblPresentDeliveries
ON tblPresentDeliveries.delPresent = tblPresentOrders.ordPresent
RIGHT JOIN tblPresents ON tblPresents.prsID = tblPresentOrders.ordPresent
GROUP BY prsName';
The first column (Ordered) is summing up correctly, but the deliveries is counting the delivery twice (there are two separate orders for that line).
What am I doing wrong?
Because you can have multiple orders per delivery (and presumably multiple presents per order) you need to perform aggregation in derived tables before JOINing to avoid duplication in counted/summed values. Note that using a mixture of LEFT JOIN and RIGHT JOIN in the same query can be a bit hard to read so I've rewritten the query using only LEFT JOINs.
SELECT p.prsName, o.qtyOrdered, d.qtyDelivered
FROM tblPresents p
LEFT JOIN (SELECT ordPresent, SUM(ordQuantity) AS qtyOrdered
FROM tblPresentOrders
GROUP BY ordPresent) o ON o.ordPresent = p.prsID
LEFT JOIN (SELECT delPresent, SUM(delQuantity) AS qtyDelivered
FROM tblPresentDeliveries
GROUP BY delPresent) d ON d.delPresent = p.prsID
I have more than one shop in ps_shop table and its some of shop have in ps_storeinfo table but those shop of ps_shop table are not in ps_storeinfo table that are needed to insert into ps_storeinfo with user id and shop name. Here shopname of ps_shop table = storename in ps_storeinfo table. Here I have written sql query for this and I am getting all data but problem is more than one user is coming by my SQL if it have more than one user. I need one user for one shop.
In ps_ employee_shop table user is assigned for shop base on shop id. And In ps_ employee table is for user. This is for prestashop 1.6.
My SQL is given below :
$table_prefix = _DB_PREFIX_;
'SELECT ps.*, pe.email, pe.firstname, pe.lastname, pes.id_employee,psi.storename
FROM '.$table_prefix.'shop ps
LEFT JOIN '.$table_prefix.'storeinfo psi ON ps.name = psi.storename
INNER JOIN '.$table_prefix.'employee_shop pes ON ps.id_shop = pes.id_shop
INNER JOIN '.$table_prefix.'employee pe ON pes.id_employee = pe.id_employee where ps.id_shop <>1 and pe.id_employee <>1
GROUP BY pes.id_employee
';
Output image is:
Well, if more than one entity exists in the table on the right side of the JOIN with the matching ON key any relational DB engine will return one row for each matching entity on the right side, duplicating the left side of the JOIN clause.
In the scenario described you need to decide which user you want to return. The first one sorted alphabetically? The one created most recently?
Based on the answer you will need to use a JOIN with a correlated subquery. Here's an example for getting the employee with the highest value of id_employee:
SELECT
ps.*, pe.email, pe.firstname, pe.lastname, pes.id_employee,psi.storename
FROM
'.$table_prefix.'shop ps
LEFT JOIN '.$table_prefix.'storeinfo psi ON ps.name = psi.storename
INNER JOIN (
SELECT
pe.email, pe.firstname, pe.lastname, pes.id_employee
FROM
'.$table_prefix.'employee pe
'.$table_prefix.'employee_shop pes ON pes.id_employee = pe.id_employee
WHERE
pe.id_employee <> 1
AND ps.id_shop = pes.id_shop
ORDER BY
pe.id_employee DESC
LIMIT 1
)
WHERE
ps.id_shop <>1
As you can see what this is really doing is limiting the resulting list of employees to just one record to avoid duplication. In this case the list is ordered by id_employee, but the correct ordering depends on the business logic that needs to be implemented.
I have a table which contains records of a 'widget' many of the columns contain just the Id of a record in a different table. When editing the widget record users are allowed to do a save even if it is incomplete. They can open it later and continue.
The problem I have, is when it is incomplete my query returns nothing because the where clause contain fields which have default 0 in them and there is no match in the other tables. Here is a sample of script which illustrates this problem.
select Client,Make,Model,Shape
from
widget,clients,makes,models,shapes
where
widget.ClientId = '3' and
widget.MakeId = makes.Id and
widget.ModelId = models.Id and
widget.ShapeId = shapes.Id
I am building this query dynamically using PHP so am trying to keep it as simple as possible. All sugestions welcome, thanks.
The problem is that you are using an implicit inner join (implicit meaning that you do the join in the where clause). Inner joins return matching records only, therefore if some of the data are incomplete, no records will be returned.
Use an outer join instead, that return all records from one of the tables in the join and the matching records from the other table (MySQL does not support full outer join, but this is not relevant here anyway).
Based on your description widget table is your main table, so use left join to join all other tables on widget to get the widget even if it is incomplete:
select c.Client, m.make, md.model, s.shape
from widget w
left join clients c on c.id = w.ClientId
left join makes m on m.id = w.MakeId
left join models md on md.id = w.ModelId
left join shapes s on s.id = w.ShapeId
select c.Client, m.make, md.model, s.shape
from widget w
join clients c on c.id = w.ClientId
join makes m on m.id = w.MakeId
join models md on md.id = w.ModelId
join shapes s on s.id = w.ShapeId
Use Joins instead of multiple tables in FROM Clause.
Instead of direct join use LEFT JOIN so that no matter if there is records from other tables, still first table entries will be returned:
SELECT Client, Make, Model, Shape
FROM widget
LEFT JOIN clients ON widget.ClientId = widget.Id
LEFT JOIN makes ON widget.MakeId = makes.Id
LEFT JOIN models ON widget.ModelId = models.Id
LEFT JOIN shapes ON widget.ShapeId = shapes.Id
WHERE widget.ClientId = 3
Table columns should be in lower case
Table names should be in singular form, e.g. model, shape
Foreign keys should be in other table. Instead of widget having ModelId, model should have widget id
I may be wrong if relations are different
I have a database with three tables:
Products
MaterialsProducts
Materials
The relationship between the tables are like this:
Materials 1-* MaterialsProducts *-1 Products
Quite often I need to retrieve 200+ products and their related material data (from the Materials table).
Currently it is done like this:
SQL: select all relevant products
PHP: iterate through the selected products, calling the database to select material data for each product(generating a database call for each product!)
Is there a way to select all relevant products + their material data at the same time? And still have each product only take up one row in the result.
So the solution shouldn't be "SELECT * FROM products p, materialsproducts mp, materials m WHERE p.id = mp.productid AND m.id = mp.materialid WHERE x". (That SELECT would make each product take up more than one row in the result.)
you can use left join to get all data you need
documentation here
Your query have a mistake
"SELECT * FROM `products` p LEFT JOIN `materialsproducts` mp on p.`id` = mp.`productid` LEFT JOIN `materials` m ON m.`id` = mp.`materialid` WHERE $whateveryouneed"
remember to not limit your WHERE to only 1 ID otherwise you will need more queries.
UPDATED
as asked
products has a 1-* relationship with both materialsproducts and typesproducts. materialsproducts has a *-1 relationship with materials. typesproducts has a *-1 relationship with types.
So you can make a left join query as above in this way
SELECT * FROM `products` p
LEFT JOIN `materialsproducts` mp
ON p.`id` = mp.`productid`
LEFT JOIN `materials` m
ON mp.`mp_field_id_here` = m.`m_field_id_here` //here you need to change with actual field to compare
LEFT JOIN `typesproducts` tp
ON p.`id` = tp.`tp_field_id_here` //here you need to change with actual field to compare
LEFT JOIN `types` t
ON tp.`tp_field_id_here` = t.`t_field_id_here` //here you need to change with actual field to compare
then you can add a where statment to limit result to something that suits you
WHERE WHATEVER_YOU_NEED
UPDATED AGAIN
to limit result to only some filed just change * to something specific as
"SELECT p.`Product_Name`, m.`Material_1`, m.`Material_2, m.`Material_3`, t.`Type_1`, t.`Type_2` FROM .....
NOTE I assume you retrieve material 1, material 2, material 3 from material table wich is why i used as prefix m. Either way i used as prefix t for type 1 and type 2 because i thought they are fileds of type table, otherwise you can change them according to your needs.
Try
SELECT p.*, mp.*,m.*
FROM products p
JOIN materials m ON p.id=m.prd_id
JOIN materialsproducts mp ON m.mp_id=mp.id;
It is not simple to have a query like this compress the materials data into a single row for each product - so I don't think that plan will work well for you.
What I would recommend instead is to use a normal JOIN query (much like the one you put in your question and rejected), then use PHP code to deal with the cases where a single product has multiple rows in the result set because it is linked to multiple materials.
You could use GROUP_CONCAT to combine materials related to the same product into a single list while grouping by products:
SELECT
p.ProductID,
p.ProductName,
GROUP_CONCAT(m.MaterialName) AS Materials
FROM Products p
INNER JOIN MaterialsProducts mp ON p.ProductID = mp.ProductID
INNER JOIN Materials m ON m.MaterialID = mp.MaterialID
GROUP BY
p.ProductID,
p.ProductName
;
I've got a large mysql query with 5 joins which may not seem efficient but I'm struggling to find a different solution which would work.
The views table is the main table here, because both clicks and conversions table rely on it via the token column(which is indexed and set as a foreign key in all tables).
The query:
SELECT
var.id,
var.disabled,
var.name,
var.updated,
var.cid,
var.outdated,
IF(var.type <> 0,'DL','LP') AS `type`,
COUNT(DISTINCT v.id) AS `views`,
COUNT(DISTINCT c.id) AS `clicks`,
COUNT(DISTINCT co.id) AS `conversions`,
SUM(tc.cost) AS `cost`,
SUM(cp.value) AS `revenue`
FROM variants AS var
LEFT JOIN views AS v ON v.vid = var.id
LEFT JOIN traffic_cost AS tc ON tc.id = v.source
LEFT JOIN clicks AS c ON c.token = v.token
LEFT JOIN conversions AS co ON co.token = v.token
LEFT JOIN c_profiles AS cp ON cp.id = co.profile
WHERE var.cid = 28
GROUP BY var.id
The results I'm getting are:
The problem is the revenue and cost results are too hight, because for views,clicks and impressions only the distinct rows are counted, but for revenue and cost for some reason(I would really appreciate an explanation here) all rows in all tables are taken into the result set.
I know this is a large query, but both clicks and conversions tables rely on the views table which is used for filtering the results e.g. views.country = 'uk'. I've tried doing 3 queries and merging them, but that didn't work(it gave me wrong results).
One more thing that I find weird is that if I remove the joins with clicks, conversions, c_profiles the costs column shows correct results.
Any help would be appreciated.
In the end I had to use 3 different queries and do a merge on them. Seemed like an overhead, but worked for me.