Just can't seem to get mysql query correct - mysql

I am trying to get results that i know are there but just can't seem to get the query right. I am using the following:
SELECT
*
FROM
carpets AS c
INNER JOIN carpet_relations AS r ON c.id = r.carpet_id
WHERE
c.active = '1'
AND **((r.relation_type = '5')
AND (r.related_id = '1' ))**
*AND* ((r.relation_type = '4')
AND (r.related_id = '1'))
AND (c.width_feet BETWEEN '0' AND '17')
AND (c.width_inches BETWEEN '0' AND '11')
AND (c.height_feet BETWEEN '0' AND '29')
AND (c.height_inches BETWEEN '0' AND '11')
ORDER BY
c.item_no
as you can see i am trying get results that have two matching fields in a second table if i change the and that is in italics to an or i get results but it is results for both i need the results that are in the set prior to the and which is bold
so it would be something like:
list that has relation type 5 and 4
keep in mind that related id could be different for both there are three colums carpet_id,related_id,relation_type
thanks for any help
c_categories
Column Type Null Default Comments
id int(11) No
title varchar(250) No
active int(11) No
weight int(11) No
template_id int(11) No
c_sizes
Column Type Null Default Comments
id int(11) No
title varchar(250) No
active int(11) No
weight int(11) No
template_id int(11) No
carpet_relations
Column Type Null Default Comments
carpet_id int(11) No <------ signifies which carpet the relationship is with
related_id int(11) No <------ the id of the c_size or c_categories to use
relation_type int(11) No <------ signifies which table either c_sizes or c_categories

Change this:
((r.relation_type = '5') AND (r.related_id = '1' )) AND ((r.relation_type = '4') AND (r.related_id = '1'))
to this:
(r.relation_type = '5' OR r.relation_type = '4') AND (r.related_id = '1')

For flexibility's sake:
r.relation_type IN (4, 5)

Now I think I understand your problem - you're after the intersection of results of type 5 and type 4.
This could be done in PHP as you mention by doing two queries and intersecting them, or you can do this in MySQL by joining the carpet_relations table on twice like so:
SELECT
*
FROM
carpets AS c
INNER JOIN carpet_relations AS r1 ON c.id = r1.carpet_id
INNER JOIN carpet_relations AS r2 ON c.id = r2.carpet_id
WHERE
c.active = '1'
AND (r1.relation_type = '5')
AND (r1.related_id = '1' )
AND (r2.relation_type = '4')
AND (r2.related_id = '1')
AND (c.width_feet BETWEEN '0' AND '17')
AND (c.width_inches BETWEEN '0' AND '11')
AND (c.height_feet BETWEEN '0' AND '29')
AND (c.height_inches BETWEEN '0' AND '11')
ORDER BY
c.item_no

AND ((r.relation_type = '5')
AND ((r.relation_type = '4')
you are expecting relation type to be 5 and 4 at the same time.
Would you like to have some "or" in your salad?
edit:
SELECT
*
FROM
carpets AS c
INNER JOIN carpet_relations AS r ON c.id = r.carpet_id
WHERE
c.active = '1'
AND
((r.relation_type = '5') AND (r.related_id = '1' ))
OR
((r.relation_type = '4') AND (r.related_id = '1'))
AND (c.width_feet BETWEEN '0' AND '17')
AND (c.width_inches BETWEEN '0' AND '11')
AND (c.height_feet BETWEEN '0' AND '29')
AND (c.height_inches BETWEEN '0' AND '11')
ORDER BY
c.item_no
solves your problem.

Related

Why two same queries statements have different result

Here is my queries statement. They are similar but the result when execute is different. Both results are valid according to my ORDER BY clause. But the order in in label_total column is not same
--First SQL Statement
SELECT
labels.production_line_id,
prices.`code`,
labels.quantity,
`labels`.`total` AS `label_total`
FROM
`labels`
INNER JOIN `order_details` ON `labels`.`order_detail_id` = `order_details`.`id`
AND `order_details`.`deleted_at` IS NULL
INNER JOIN `prices` ON `prices`.`id` = `order_details`.`price_id`
AND `prices`.`deleted_at` IS NULL
WHERE
`order_details`.`order_id` IN ( '2452' )
AND `labels`.`type` = '0'
AND `labels`.`status` != '7'
AND `labels`.`status` != '8'
ORDER BY
`labels`.`production_line_id` ASC,
`prices`.`code` ASC,
`labels`.`quantity` DESC
-- Second
SELECT
labels.production_line_id,
prices.`code`,
labels.quantity,
`labels`.`total` AS `label_total`
FROM
`labels`
INNER JOIN `order_details` ON `labels`.`order_detail_id` = `order_details`.`id`
AND `order_details`.`deleted_at` IS NULL
INNER JOIN `prices` ON `prices`.`id` = `order_details`.`price_id`
AND `prices`.`deleted_at` IS NULL
WHERE
`order_details`.`order_id` IN ( '2452' )
AND `labels`.`type` = '0'
AND `labels`.`status` != '7'
AND `labels`.`status` != '8'
ORDER BY
labels.production_line_id ASC,
prices.`code` ASC,
labels.quantity DESC
The result

MySQL Table Index Cardinality

I have a MySQL database table that's quite large with 2.5million rows and growing. To speed up queries I've added an index to one of columns. When I set the index manually, through PHPMyAdmin for example, it's cardinality is around 1500 which seems about right and my queries run without issue.
The problem then comes after a few queries, (especially on an INSERT but not limited to), have been run, the cardinality of that index drops to 17 or 18, and queries run extremely slow. Sometimes it seems to work it's way back to about 1500 or I have to do it through PHPMyAdmin again.
Is there any way to stop this cardinality drop from happening?
CREATE TABLE IF NOT EXISTS `probe_results` (
`probe_result_id` int(11) NOT NULL AUTO_INCREMENT,
`date` date NOT NULL,
`month` int(11) NOT NULL,
`year` int(11) NOT NULL,
`time` time NOT NULL,
`type` varchar(11) NOT NULL,
`probe_id` varchar(50) NOT NULL,
`status` varchar(11) NOT NULL,
`temp_1` decimal(11,0) NOT NULL,
`temp_2` decimal(11,0) NOT NULL,
`crc` varchar(11) NOT NULL,
`raw_data` text NOT NULL,
`txt_file` text NOT NULL,
PRIMARY KEY (`probe_result_id`),
KEY `probe_id` (`probe_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=2527300 ;
The 'probe_result_id' column is the primary key, the probe_id is the column with the index in question.
Example query:
SELECT IF(b.reactive_total IS NULL, 0, b.reactive_total) AS reactive_total, a.* FROM (SELECT COUNT(CASE WHEN asset_testing_results.asset_testing_year = '2016' AND asset_testing_results.asset_testing_month = '7' AND asset_testing_results.asset_stopped = '0' AND asset_testing_results.asset_testing_completed = '0' THEN 1 END) AS due_total, (COUNT(CASE WHEN asset_testing_results.asset_testing_year = '2016' AND asset_testing_results.asset_stopped = '0' AND asset_testing_results.asset_testing_completed = '1' AND asset_testing_results.asset_testing_satisfactory = '1' AND asset_testing_results.asset_testing_actioned = '0' THEN 1 END)+(IF(probes_passed_total IS NULL, 0, probes_passed_total))) AS passed_total, (COUNT(CASE WHEN asset_testing_results.asset_testing_year = '2016' AND asset_testing_results.asset_stopped = '0' AND asset_testing_results.asset_testing_completed = '1' AND asset_testing_results.asset_testing_satisfactory = '0' AND asset_testing_results.asset_testing_actioned = '0' THEN 1 END)+(IF(probes_failed_total IS NULL, 0, probes_failed_total))) AS failed_total, COUNT(CASE WHEN asset_testing_results.asset_testing_year = '2016' AND asset_testing_results.asset_stopped = '0' AND asset_testing_results.asset_testing_completed = '1' AND asset_testing_results.asset_testing_actioned = '1' THEN 1 END) AS actioned_total, COUNT(CASE WHEN asset_testing_results.asset_testing_year = '2016' AND asset_testing_results.asset_testing_month < '7' AND asset_testing_results.asset_testing_completed = '0' AND asset_testing_results.asset_testing_satisfactory = '0' AND asset_testing_results.asset_stopped = '0' THEN 1 END) AS missed_total, site.site_key, site.site_name FROM site LEFT JOIN location ON location.site_key = site.site_key LEFT JOIN sub_location ON sub_location.location_key = location.location_key LEFT JOIN asset ON asset.sub_location_key = sub_location.sub_location_key AND asset.stopped = '0' LEFT JOIN asset_testing ON asset_testing.asset_type_key = asset.asset_type_key AND asset_testing.probe_assessed = '0' LEFT JOIN asset_testing_results ON asset_testing_results.asset_testing_key = asset_testing.asset_testing_key AND asset_testing_results.asset_key = asset.asset_key LEFT JOIN (SELECT site.site_key, COUNT(CASE WHEN p.probe_id IS NOT NULL AND p.asset_testing_key IS NOT NULL THEN 1 END) AS probes_passed_total, COUNT(CASE WHEN p.probe_id IS NOT NULL AND p.asset_testing_key IS NULL AND p.temp_1 IS NOT NULL THEN 1 END) AS probes_failed_total FROM assetsvs_probes LEFT JOIN (SELECT q.probe_id, q.month, q.year, IF(r.temp_1 IS NULL, q.temp_1, r.temp_1) as temp_1, r.asset_testing_key FROM (SELECT DISTINCT probe_results.probe_id, probe_results.month, probe_results.year, probe_results.temp_1 FROM probe_results LEFT JOIN assetsvs_probes ON assetsvs_probes.probe_id = probe_results.probe_id LEFT JOIN asset ON asset.asset_key = assetsvs_probes.asset_key LEFT JOIN sub_location ON sub_location.sub_location_key = asset.sub_location_key LEFT JOIN location ON location.location_key = sub_location.location_key LEFT JOIN site ON site.site_key = location.site_key WHERE site.client_key = '25')q LEFT JOIN (SELECT probe_results.month, probe_results.year, probe_results.probe_id, temp_1, asset_testing.asset_testing_key FROM probe_results LEFT JOIN assetsvs_probes ON assetsvs_probes.probe_id = probe_results.probe_id LEFT JOIN asset_testing ON asset_testing.asset_testing_key = assetsvs_probes.asset_testing_key LEFT JOIN asset ON asset.asset_key = assetsvs_probes.asset_key LEFT JOIN sub_location ON sub_location.sub_location_key = asset.sub_location_key LEFT JOIN location ON location.location_key = sub_location.location_key LEFT JOIN site ON site.site_key = location.site_key WHERE temp_1 != 'invalid' AND ((temp_1 >= test_min AND test_max = '') OR (temp_1 <= test_max AND test_min = '') OR (temp_1 >= test_min AND temp_1 <= test_max)) AND year = '2016' AND site.client_key = '25' GROUP BY probe_results.month, probe_results.year, probe_results.probe_id)r ON r.probe_id = q.probe_id AND r.month = q.month AND r.year = q.year WHERE q.year = '2016' GROUP BY probe_id, month, year) p ON p.probe_id = assetsvs_probes.probe_id LEFT JOIN asset_testing ON asset_testing.asset_testing_key = assetsvs_probes.asset_testing_key LEFT JOIN asset ON asset.asset_key = assetsvs_probes.asset_key LEFT JOIN sub_location ON sub_location.sub_location_key = asset.sub_location_key LEFT JOIN location ON location.location_key = sub_location.location_key LEFT JOIN site ON site.site_key = location.site_key GROUP BY site.site_key) probe_results ON probe_results.site_key = site.site_key WHERE site.client_key = '25' GROUP BY site.site_key)a LEFT JOIN (SELECT COUNT(CASE WHEN jobs.status = '3' THEN 1 END) AS reactive_total, site.site_key FROM jobs LEFT JOIN jobs_meta ON jobs_meta.job_id = jobs.job_id AND jobs_meta.meta_key = 'start_date' LEFT JOIN site ON site.site_key = jobs.site_key WHERE site.client_key = '25' AND jobs_meta.meta_value LIKE '%/2016 %' GROUP BY site.site_key)b ON b.site_key = a.site_key
Thanks
Cardinality (along with other statistics) is calculated and updated by MySQL automatically, so you do not have direct means to prevent it from dropping.
However, you can take a few steps to make this less likely to happen or correct the behaviour.
First of all, MySQL updates index statistics for all supported table engines if you run analyze table command.
For innodb table engine MySQL provides a set of configuration settings that can influence the behaviour of the sampling. The settings and their effect are described in the MySQL documentation:
non-persistent statistics
persistent statistics
The main setting is innodb_stats_transient_sample_pages:
• Small values like 1 or 2 can result in inaccurate estimates of
cardinality.
• Increasing the innodb_stats_transient_sample_pages value might
require more disk reads. Values much larger than 8 (say, 100), can
cause a significant slowdown in the time it takes to open a table or
execute SHOW TABLE STATUS.
• The optimizer might choose very different query plans based on
different estimates of index selectivity
.
For myisam MySQL dos not provide such a variety of settings. myisam_stats_method setting is described in the general index statistics documentation

Left join with multiple status

Lets say i have two stores. Store A(22) and Store B(21). This is the query to fetch the things that matched with the Store ID:
SELECT c . * , s.s_name, s.logo, s.s_slug, cm.c_code, cm.c_shorturl, cm.c_shorturl_id
FROM ci_cptbl c
LEFT JOIN ci_stores s ON s.store_id = c.store_id
LEFT JOIN ci_cptbl_mapper cm ON cm.c_id = c._id
WHERE c.coupon_id <> ''
AND c.store_id in ('22', '21')
AND s.s_status = '1'
AND c.c_status = '1'
AND DATE( c.c_end_date ) >= '2014-10-04'
ORDER BY c.c_id DESC
ci_cptbl Has the collection of product including the store_id. And ci_stores holds the store name, etc.. including s_status(0, 1).
CREATE TABLE `ci_stores` (
`store_id` int(10) NOT NULL AUTO_INCREMENT,
`cat_id` int(10) NOT NULL,
`s_name` varchar(255) NOT NULL,
`s_slug` varchar(255) NOT NULL,
`logo` varchar(255) NOT NULL,
`display_name` varchar(255) NOT NULL,
`s_description` text NOT NULL,
`network_id` int(10) NOT NULL,
`s_status` tinyint(1) NOT NULL DEFAULT '0',
`merged_stores` text NOT NULL,
`stat` bigint(20) NOT NULL,
PRIMARY KEY (`store_id`),
KEY `network_id` (`network_id`),
KEY `cat_id` (`cat_id`),
FULLTEXT KEY `display_name` (`display_name`)
) ENGINE=MyISAM AUTO_INCREMENT=127 DEFAULT CHARSET=utf8
Now condition is i have only first store id( 22 ) enabled and rest is disabled (21, .....) in my stores table and my AND s.s_status = '1' statement works only if all the stores are enabled but i want all stores including disabled.
BUT FIRST STORE ID MUST BE ENABLED
You could try changing you condition to test on store_id and enabled
(c.c_status = '1' or c.store_id <> '22')
so:
SELECT c . * , s.s_name, s.logo, s.s_slug, cm.c_code, cm.c_shorturl, cm.c_shorturl_id
FROM ci_cptbl c
LEFT JOIN ci_stores s ON s.store_id = c.store_id
LEFT JOIN ci_cptbl_mapper cm ON cm.c_id = c._id
WHERE c.coupon_id <> ''
AND c.store_id in ('22', '21')
AND s.s_status = '1'
AND (c.c_status = '1' or c.store_id <> '22')
AND DATE( c.c_end_date ) >= '2014-10-04'
ORDER BY c.c_id DESC
You need to move your "AND s.s_status = 1" clause to the LEFT JOIN part of your query. When you move it to the WHERE clause, it forces to an implied INNER JOIN and thus leaving out of you result set.
LEFT JOIN ci_stores s
ON c.store_id = s.store_id
AND s.s_status = '1'
FROM ci_cptbl c
LEFT JOIN ci_stores s ON s.store_id = c.store_id
The purpose of that LEFT JOIN is to allow unmatched records in the "joined to" table to be listed in the results
e.g. if there is a store 657 in ci_cptbl but no such store in ci_stores 657 would still be listed
However, using the WHERE clause:
if we then ALSO insist EVERY row has s.s_status = '1'
then store 657 would NOT be listed (how can it? it does not exist in ci_stores so ci_stores.s_status has to be NULL and can never ever be equal to 1
So; when using any outer join such as a LEFT OUTER JOIN you have to be very careful how you reference those tables in the WHERE clause. In many cases, such as here, it is better to move the additional condition(s) to the JOIN like this:
FROM ci_cptbl c
LEFT JOIN ci_stores s ON s.store_id = c.store_id
AND s.s_status = '1'

Case when nested select = null then else

Okay so i didn't quite know what to call this post i might update the title later.
I have the following problem:
Say for instance you have the following calculating nested sql:
CREATE
ALGORITHM = UNDEFINED
DEFINER = `root`#`localhost`
SQL SECURITY DEFINER
VIEW `v_user_category_stat` AS
SELECT
(SELECT
SUM(MS.score) + (SELECT
SUM(UHMS.score)
FROM
User_has_module_score UHMS
RIGHT OUTER JOIN
module M ON M.id = UHMS.module_id
JOIN
Category C ON C.id = M.category_id
WHERE
UHMS.user_id = U.id)
FROM
Module_score MS
RIGHT OUTER JOIN
Module M ON M.id = MS.module_id
JOIN
Category C ON C.id = M.category_id
WHERE
MS.user_id = U.id) AS total_score,
U.id
FROM
User U
This gives me the wanted result which is:
# total_score, id
NULL, '2'
NULL, '7'
NULL, '8'
NULL, '9'
NULL, '10'
NULL, '11'
NULL, '12'
NULL, '13'
'13', '14'
Now i thought to my self i want to prettyfi this by making sure that if the value is null then it should be 0 (instead of null)
My question is how do you make a CASE that says if null THEN 0 ELSE normal?
Check the COALESCE function. In your case you will do this:
COALESCE(normal, 0)

MySQL: How to optimize this query?

I almost spent a day to optimize this query:
SELECT
prod. *,
cat.slug category_slug,
sup.bname bname,
sup.slug bname_slug
FROM bb_admin.bb_directory_products AS prod
LEFT JOIN bb_admin.bb_categories_products AS cat
ON prod.primary_category_id = cat.category_id
LEFT JOIN bb_admin.bb_directory_suppliers AS sup
ON prod.supplier_id = sup.supplier_id
LEFT JOIN bb_admin.bb_directory_suppliers AS credit_sup
ON prod.credit_supplier_id = credit_sup.supplier_id
LEFT JOIN bb_admin.bb_directory_suppliers AS photo_sup
ON prod.photo_supplier_id = photo_sup.supplier_id
WHERE (
prod.status = '1'
OR prod.status = '3'
OR prod.status = '5'
)
AND (
sup.active_package_id != '1'
OR sup.active_package_id != '5'
OR sup.active_package_id != '6'
OR prod.supplier_id = '0'
)
AND (
sup.supplier_id = '1989'
OR credit_sup.supplier_id = '1989'
OR photo_sup.supplier_id = '1989'
)
GROUP BY prod.product_id
ORDER BY prod.priority_index ASC
Can you help me to optimized this query?
Update your column data types to be INT or one of its variants, since the ones you are checking against are all integer IDs (assumption).
Create indexes on following columns(if possible in all tables):
prod.status
supplier_id
active_package_id
Use IN clause instead of concatenating OR segments.
I'll be putting the updated WHERE clause here:
WHERE prod.status IN(1, 3, 5)
AND ( sup.active_package_id NOT IN(1, 5, 6)
OR prod.supplier_id = 0
)
AND 1989 IN (prod.supplier_id, prod.credit_supplier_id, prod.photo_supplier_id)