Why are these two mysql queries different? - mysql

select distinct p.product_id from cscart_products p
left join product_bikes pb on p.product_id = pb.product_id
left join cscart_product_options po on po.product_id = p.product_id
left join cscart_product_option_variants pov on pov.option_id = po.option_id
left join variant_bikes vb on vb.variant_id = pov.variant_id
where pb.bike_id = 111 or vb.bike_id = 111
And:
select distinct p.product_id from cscart_products p
left join product_bikes pb on p.product_id = pb.product_id and pb.bike_id = 111
left join cscart_product_options po on po.product_id = p.product_id
left join cscart_product_option_variants pov on pov.option_id = po.option_id
left join variant_bikes vb on vb.variant_id = pov.variant_id and vb.bike_id = 111
Return different result sets, why?

The first query has an OR in the WHERE clause:
WHERE pb.bike_id = 111 OR vb.bike_id = 111
The second query effectively has AND instead, via the conditions:
LEFT JOIN product_bikes pb ON p.product_id = pb.product_id AND pb.bike_id = 111
...
LEFT JOIN variant_bikes vb ON vb.variant_id = pov.variant_id AND vb.bike_id = 111
Bonus question: is there a way with joins to make it behave the same and benefit from having smaller joins for performance?
There is a way to write the query, but it isn't necessarily any faster because the method (that I'm thinking of) uses UNION:
select distinct p.product_id from cscart_products p
left join product_bikes pb on p.product_id = pb.product_id and pb.bike_id = 111
left join cscart_product_options po on po.product_id = p.product_id
left join cscart_product_option_variants pov on pov.option_id = po.option_id
left join variant_bikes vb on vb.variant_id = pov.variant_id -- and vb.bike_id = 111
UNION
select distinct p.product_id from cscart_products p
left join product_bikes pb on p.product_id = pb.product_id -- and pb.bike_id = 111
left join cscart_product_options po on po.product_id = p.product_id
left join cscart_product_option_variants pov on pov.option_id = po.option_id
left join variant_bikes vb on vb.variant_id = pov.variant_id and vb.bike_id = 111
There probably is a better way of doing it, such that you have a UNION sub-query, along the lines of:
SELECT DISTINCT p.product_id
FROM cscart_products AS p
LEFT JOIN cscart_product_options AS po ON po.product_id = p.product_id
LEFT JOIN cscart_product_option_variants AS pov ON pov.option_id = po.option_id
LEFT JOIN (SELECT vb.product_id FROM variant_bikes AS vb WHERE vb.bike_id = 111
UNION
SELECT pb.product_id FROM product_bikes AS pb WHERE pb.bike_id = 111
) AS pv ON pv.product_id = p.product_id
Since you aren't (in the example) selecting data from the cscart_product_options or cscart_product_options_variants tables, you could eliminate those from the query. You should also look at whether the LEFT JOIN with the sub-query is appropriate; I think it more likely that you want an inner join. There may well be more work that can be done to improve the performance.

In addition to what Jonathan said. In the first query, the WHERE forces it that you don't get ANY results UNLESS (pb.bike_id = 111 or vb.bike_id = 111) is true. In the second query, you will get all DISTINCT [product_id]s even though only one row will be able to join via the LEFT JOINs.
If you are getting a lot of results from the second query vs. the first, that's way. The easier way to see this is by putting more in your SELECT so:
SELECT p.product_id, pb.bike_id ...
You'll notice if you do that, that the first query will have 111 in every product it displays, but the second query will have a lot of NULL values for pb.bike_id.
Make sense?

Related

SQL LIKE seems not work like i think it should

I have an SELECT statement that has a huge number of left join and I want to filter some out.
When I check how many records i have in total and subtract the records with my LIKE statements, I should get the amount that is not affected by my restrictions.
But when I negate my restriction to get the ones I didn't affect, I get an different number than calculated.
SQL without restrictions (Record count: 13.251.981)
SELECT p.product_number
FROM product p
LEFT JOIN product_category pc on p.id = pc.product_id
LEFT JOIN product_category_tree pct on p.id = pct.product_id
LEFT JOIN product_configurator_setting pcs on p.id = pcs.product_id
LEFT JOIN product_cross_selling pcs2 on p.id = pcs2.product_id
LEFT JOIN product_cross_selling_assigned_products pcsap on p.id = pcsap.product_id
LEFT JOIN product_cross_selling_translation pcst on pcs2.id = pcst.product_cross_selling_id
LEFT JOIN product_custom_field_set pcfs on p.id = pcfs.product_id
LEFT JOIN product_media pm on p.id = pm.product_id
LEFT JOIN product_option po on p.id = po.product_id
LEFT JOIN product_price pp on p.id = pp.product_id
LEFT JOIN product_property pp2 on p.id = pp2.product_id
LEFT JOIN product_review pr on p.id = pr.product_id
LEFT JOIN product_search_keyword psk on p.id = psk.product_id
LEFT JOIN product_tag pt on p.id = pt.product_id
LEFT JOIN product_translation pt2 on p.id = pt2.product_id
LEFT JOIN product_visibility pv on p.id = pv.product_id
With restriction (Record count: 9.285.545)
WHERE p.product_number NOT LIKE 'SW%'
AND p.product_number NOT LIKE '%.%'
AND pt2.name NOT LIKE '%Gutschein'
AND pt2.name NOT LIKE '%Test%'
With negated restriction (Record count: 100.851)
WHERE p.product_number LIKE 'SW%'
OR p.product_number LIKE '%.%'
OR pt2.name LIKE '%Gutschein'
OR pt2.name LIKE '%Test%';
From my calculations i should get 3.966.436 records that don't get affected. (13.251.981 - 9.285.545 = 3.966.436)
But instead I get 100.851
How is that possible?
The solution for me was actually this WHERE:
WHERE p.product_number < 'SW'

Database Query not pulling the full (or correct) information

I have been working on a multi-table query (something I haven't had much experience in) and at first I thought it was working perfectly fine until I noticed that half of the results had null values. I have put the query and table structures below so any help would be appreciated!
SELECT
i.name, i.material, i.price, a.str_mod, a.def_mod,
a.dex_mod, a.spd_mod, i.level_req
FROM `character` as c
LEFT JOIN item_owned as o ON c.uid = o.oid
LEFT JOIN items as i ON o.iid = i.id
LEFT JOIN armour as a ON i.id = a.aid
WHERE uid = :id AND o.equipped = 1 AND i.type = 'Armour'
Above is the query I have been running and below is the table structures
Found the solution thanks to Malfunct on discord... The query had a column typo so should have been
SELECT
i.name, i.material, i.price, a.str_mod, a.def_mod, a.dex_mod, a.spd_mod, i.level_req
FROM `character` as c
JOIN item_owned as o ON c.uid = o.oid
JOIN items as i ON o.iid = i.id
JOIN armour as a ON i.id = a.aid WHERE uid = 1
AND o.equipped = 1
AND i.type = 'Armour'

MySQL query performance limit?

We have a home grown document management system and our system is running very slow, particularly on the search. It worked fine at first, but it has gotten progressively slower over time. Its now taking anywhere from 30 to 150 seconds to return results depending upon criteria. This is our search query. We’ve been staring at this thing left and right and can’t see any place to tune this more. All of the joined fields are indexed on their respective tables.
SELECT DISTINCT f.*, ts.*, fo.*, ft.*, p.*, u.*, c.*, co.*, ct.*, fs.*, fd.*, r.*, rt.*, si.*, s.* FROM (
SELECT DISTINCT f.* FROM files f
JOIN folders fo ON(fo.id = f.belongs_to_folder_id)
JOIN projects p ON(p.id = f.belongs_to_project_id)
LEFT OUTER JOIN file_statuses fs ON(fs.id = f.file_status_id)
LEFT OUTER JOIN submittal_items_files sif ON(sif.file_id = f.id)
LEFT OUTER JOIN submittal_items si ON(si.id = sif.submittal_item_id)
LEFT OUTER JOIN submittals s ON(s.id = si.belongs_to_submittal_id)
LEFT OUTER JOIN record_types rt ON(rt.id = f.record_type_id)
LEFT OUTER JOIN companies co ON(co.id = f.company_id)
LEFT JOIN folders_actions_groups ag ON (
f.belongs_to_folder_id = ag.folder_id AND
ag.action_id = 10010
)
LEFT JOIN files_actions_groups fg ON (fg.file_id = f.id)
JOIN users_groups ug ON ((ug.group_id = ag.group_id OR ug.group_id = fg.group_id) AND ug.user_id = 411)
WHERE (
(f.file_generated_name LIKE CONCAT('%', 'the', '%')) OR
(f.record_id LIKE CONCAT('%', 'the', '%')) OR
(f.record_title LIKE CONCAT('%', 'the', '%')) OR
(f.additional_info LIKE CONCAT('%', 'the', '%')) OR
(si.item_number LIKE CONCAT('%', 'the', '%')) OR
(s.element_number LIKE CONCAT('%', 'the', '%'))
) AND f.path LIKE CONCAT('Some Text', '%') AND
f.file_status_id = 3 AND
f.file_revision = 1 AND
f.discipline_id = 1 AND
f.record_type_id = 2 AND
f.triage_status_id = 2 AND
f.deleted = 0
ORDER BY f.created DESC, f.id DESC
LIMIT 100
) AS f
LEFT OUTER JOIN users u ON(f.created_by_user_id = u.id)
LEFT OUTER JOIN contacts c ON(c.user_id = u.id)
LEFT OUTER JOIN companies co ON(co.id = f.company_id)
LEFT OUTER JOIN company_types ct ON(ct.id = co.company_type_id)
JOIN triage_statuses ts ON(f.triage_status_id = ts.id)
JOIN folders fo ON(fo.id = f.belongs_to_folder_id)
JOIN folder_types ft ON(ft.id = fo.folder_type_id)
JOIN projects p ON(p.id = f.belongs_to_project_id)
LEFT OUTER JOIN file_statuses fs ON(fs.id = f.file_status_id)
LEFT OUTER JOIN file_disciplines fd ON(fd.id = f.discipline_id)
LEFT OUTER JOIN revisions r ON(r.id = f.file_revision)
LEFT OUTER JOIN record_types rt ON(rt.id = f.record_type_id)
LEFT OUTER JOIN submittal_items_files sif ON(sif.file_id = f.id)
LEFT OUTER JOIN submittal_items si ON(si.id = sif.submittal_item_id)
LEFT OUTER JOIN submittals s ON(s.id = si.belongs_to_submittal_id)
LEFT OUTER JOIN files_actions_groups ffg ON(ffg.file_id = f.id)
LEFT OUTER JOIN groups g ON(g.id = ffg.group_id)
ORDER BY f.created DESC, f.id DESC
This might be an obvious answer, but have you indexed your database? If you're new to indexing, here's a pretty good rule: just put a unique index on all the columns named "id", such as folders.id or projects.id, then put a standard index on all the columns that reference a foreign id, such as folder.belongs_to_folder_id or folder.record_type_id
Another thing I would change is to try and select only the columns you will actually use rather than your huge list of f.*, ts.*, fo.*, ft.*, p.*, u.*, c.*, co.*, ct.*, fs.*, etc...
You also have TONS of joins, which are very expensive in terms of processing time. Do you really need all those joined tables?

Rows missing from mysql pivot query results

I have a mysql query as stated below, it returns exactly the results I want for one row, but doesn't return any other rows where I expect there to be 8 in my test data (there are 8 unique test ids). I was inspired by this answer but obviously messed up my implementation, does anyone see where I'm going wrong?
SELECT
c.first_name,
c.last_name,
n.test_name,
e.doc_name,
e.email,
e.lab_id,
a.test_id,
a.date_req,
a.date_approved,
a.accepts_terms,
a.res_value,
a.reason,
a.test_type,
a.date_collected,
a.date_received,
k.kind_name,
sum(case when metabolite_name = "Creatinine" then t.res_val end) as Creatinine,
sum(case when metabolite_name = "Glucose" then t.res_val end) as Glucose,
sum(case when metabolite_name = "pH" then t.res_val end) as pH
FROM test_requisitions AS a
INNER JOIN personal_info AS c ON (a.user_id = c.user_id)
INNER JOIN test_types AS d ON (a.test_type = d.test_type)
INNER JOIN kinds AS k ON (k.id = d.kind_id)
INNER JOIN test_names AS n ON (d.name_id = n.id)
INNER JOIN docs AS e ON (a.doc_id = e.id)
INNER JOIN test_metabolites AS t ON (t.test_id = a.test_id)
RIGHT JOIN metabolites AS m ON (m.id = t.metabolite_id)
GROUP BY a.test_id
ORDER BY (a.date_approved IS NOT NULL),(a.res_value IS NOT NULL), a.date_req, c.last_name ASC;
Most of your joins are inner joins. The last is a right outer join. As written, the query keeps all the metabolites, but not necessarily all the tests.
I would suggest that you change them all to left outer joins, because you want to keep all the rows in the first table:
FROM test_requisitions AS a
LEFT JOIN personal_info AS c ON (a.user_id = c.user_id)
LEFT JOIN test_types AS d ON (a.test_type = d.test_type)
LEFT JOIN kinds AS k ON (k.id = d.kind_id)
LEFT JOIN test_names AS n ON (d.name_id = n.id)
LEFT JOIN docs AS e ON (a.doc_id = e.id)
LEFT JOIN test_metabolites AS t ON (t.test_id = a.test_id)
LEFT JOIN metabolites AS m ON (m.id = t.metabolite_id)
I would also suggest that your aliases be related to the table, so tr for test_requisition, pi for personal_info, and so on.

MySQL SubQuery with query variables

I'm trying to do the following MySql query and when i go to run it, it tells me that "q.object_id" in the sub-query is unknown (if i change it to o.id, it says the same).
SELECT q.*, (SELECT c.title
FROM api_course c
LEFT OUTER JOIN api_object_parents op
ON c.object_id = op.parent
AND op.object_id = q.object_id) as parent_title
FROM api_quiz q
LEFT OUTER JOIN api_object o ON q.object_id = o.id
WHERE o.type = 'Quiz'
Basically i need to get the same id that is being used in the main query and use that in a sub-query.
Thanks!
is this what you are looking for?
SELECT a.*, d.title
FROM api_quiz a
LEFT JOIN api_object b
on a.object_id = b.id
LEFT JOIN api_object_parents c
ON c.parent = a.object_id
LEFT JOIN api_course d
ON c.object_id = c.parent