Mysql query with special characters? - mysql

I (newbie) have hard time to understand this query:
$result = mysql_query("
SELECT q.*, IF(v.id,1,0) AS voted
FROM quotes AS q
LEFT JOIN quotes_votes AS v
ON q.id = v.qid
AND v.ip =".$ip."
AND v.date_submit = '".$today."'
");
can anybody provide more info on what these short symbols like 'q.*' with the if statement and v.id,1,0. Any sources to read more about this?
Thank you very much.
this is how the tables looks like:
CREATE TABLE `quotes` (
`id` smallint(5) unsigned NOT NULL auto_increment,
`txt` varchar(255) collate utf8_unicode_ci NOT NULL default '',
`author` varchar(32) collate utf8_unicode_ci NOT NULL default '',
`bgc` varchar(32) collate utf8_unicode_ci NOT NULL default '',
`votes` mediumint(9) unsigned NOT NULL default '0',
`vsum` int(11) unsigned NOT NULL default '0',
`rating` double NOT NULL default '0',
PRIMARY KEY (`id`),
KEY `rating` (`rating`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=16 ;
CREATE TABLE `quotes_votes` (
`id` mediumint(9) unsigned NOT NULL auto_increment,
`qid` smallint(6) unsigned NOT NULL default '0',
`ip` int(10) NOT NULL default '0',
`vote` tinyint(1) NOT NULL default '0',
`date_submit` date NOT NULL default '0000-00-00',
`dt_submit` timestamp NOT NULL default CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `qid` (`qid`,`ip`,`date_submit`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

Regarding select q.*, it just means getting all fields from the table alias q, which happens to be table quotes. It is like select * but just for one table.
Regarding IF(v.id,1,0), that is really a MySQLism. The IF statement evaluates an expression given in the first argument and, if it is true, returns the second argument. Otherwise it returns the third argument. So you know that a 1 or a 0 will come out of the IF. You might now be wondering how can v.id be evaluated to return a logical value and the reason behind it is that MySQL treats booleans as if they were TINYINT(1) in which 0 is considered as false and non-zero values are considered as true.
So that would be rephrased into IF(v.id != 0, 1, 0) which might be easier to read. Given the fact that v.id can not be null then you could rewrite that this way IF(v.id = 0, 0, 1). Anyway, you could take a step further and just replace it with v.id != 0 :)

Related

Not equals query is so slow

I have a query like that:
SELECT * , (
( 1584392725 ) - ( suprayts.time )
) AS timeDiff
FROM (
`suprayts`
)
WHERE `suprayts`.`is_deleted` = '0'
AND `suprayts`.`is_approved` =1
AND `suprayts`.`username` != 'rayben1'
AND `suprayts`.`time` >1584306325
ORDER BY `suprayts`.`is_boosted_by_user` DESC , `suprayts`.`id` ASC
LIMIT 10
This query runs very slow (avg 0.2 seconds), if i delete the following line:
AND `suprayts`.`username` != 'rayben1'
It runs 10x faster. (avg 0.02 secs) How can i speed up this query?
My indexes:
Explain:
My table:
CREATE TABLE IF NOT EXISTS `suprayts` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(15) CHARACTER SET utf8 NOT NULL,
`question` varchar(70) COLLATE utf8mb4_unicode_ci NOT NULL,
`suprayt_photo` varchar(50) CHARACTER SET utf8 NOT NULL,
`time` int(11) NOT NULL,
`endTime` int(11) NOT NULL,
`datetext` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`like_count` int(10) unsigned NOT NULL DEFAULT '0',
`dislike_count` int(10) unsigned NOT NULL DEFAULT '0',
`is_approved` bit(1) NOT NULL DEFAULT b'0',
`is_deleted` enum('1','0') CHARACTER SET utf8 NOT NULL DEFAULT '0',
`is_end_notification_sent` bit(1) NOT NULL DEFAULT b'0',
`open_vote` enum('0','1') CHARACTER SET utf8 NOT NULL DEFAULT '1',
`boost` int(11) NOT NULL DEFAULT '0',
`is_boosted_by_user` tinyint(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE KEY `username_3` (`username`,`suprayt_photo`),
KEY `id` (`id`,`time`,`is_approved`,`is_deleted`),
KEY `username` (`username`,`is_deleted`),
KEY `username_2` (`username`,`datetext`),
KEY `id_2` (`id`,`username`,`time`,`is_approved`,`is_deleted`),
KEY `username_4` (`username`,`time`,`is_approved`,`is_deleted`),
KEY `ix1` (`id`,`time`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci AUTO_INCREMENT=130789 ;
Depending on the SQL pre-compiler if one is being used, or the SQL itself, possible it is sensitive to the order of the query "where" clause not being in DB key order and it is not using the index and instead doing a sequential scan? Just to eliminate the possibility, try putting the WHERE items in the DB index key order.
USE NOT EXISTS
SELECT * , (
( 1584392725 ) - ( suprayts.time )
) AS timeDiff
FROM (
`suprayts`
)
WHERE `suprayts`.`is_deleted` = '0'
AND `suprayts`.`is_approved` =1
AND NOT EXISTS (
SELECT x.no FROM (SELECT 1 AS no) x WHERE `suprayts`.`username` = 'rayben1'
)
AND `suprayts`.`time` >1584306325
ORDER BY `suprayts`.`is_boosted_by_user` DESC , `suprayts`.`id` ASC
LIMIT 10

MYSQL HAVING (not) with ROW_NUMBER() why it returns different row counts

I have simple query:
SELECT
definitions.rowid,
chars,
ROW_NUMBER() OVER (ORDER BY chars, definitions.rowid) AS nr
FROM definitions
INNER JOIN words ON definitions.word = words.rowid
WHERE definitions.valid = True
HAVING chars > 20
on my computer(intel) it returns 36k records
but on client computer(ryzen 2990wx) it returns 99k records (having not working - chars starts form 1)
if i remove join it works ok on both computers
if i remove row_number it works ok on both computers too
if i replace "having" with "and" it works ok on both computers too
on both computers are the same version of mysql (8.018) and the same tables ...
I have a presentation tomorrow - I need any ideas
CREATE TABLE `definitions` (
`rowid` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`word` INT(10) UNSIGNED NULL DEFAULT NULL,
`definition` VARCHAR(255) NULL DEFAULT NULL COLLATE 'utf8_polish_ci',
`chars` TINYINT(3) UNSIGNED NULL DEFAULT NULL,
`difficult` TINYINT(3) UNSIGNED NULL DEFAULT '99',
`islocked` BIT(1) NULL DEFAULT b'0',
`valid` BIT(1) NULL DEFAULT b'0',
PRIMARY KEY (`rowid`),
INDEX `word` (`word`)
)
COLLATE='utf8_polish_ci'
ENGINE=InnoDB
AUTO_INCREMENT=100038
;
CREATE TABLE `words` (
`rowid` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`word` VARCHAR(64) NULL DEFAULT NULL COLLATE 'utf8_polish_ci',
`display` VARCHAR(64) NULL DEFAULT NULL COLLATE 'utf8_polish_ci',
`difficult` TINYINT(4) UNSIGNED NULL DEFAULT NULL,
`islocked` BIT(1) NULL DEFAULT NULL,
`valid` BIT(1) NULL DEFAULT NULL,
PRIMARY KEY (`rowid`),
INDEX `word` (`word`),
INDEX `display` (`display`)
)
COLLATE='utf8_polish_ci'
ENGINE=InnoDB
AUTO_INCREMENT=57009
;
creation code the same on both computers - created by the same instalator ...
query is a part of table browser/editor with just in time loading (datagridview with virtual mode) so will be very difficult modify its logic by night
HAVING is used with GROUP BY to filter the aggregated data. In your case, you should just add HAVING chars > 20 to the WHERE clause as AND chars > 20

MySQL Join 1 to 1 in N items table

I need some help on my shop database structure. To fetch items from category, there are already some joins, but i would like to add an image to an item, from other table. Main items table doesn't have an image id, so we just get one from other table by it's weight = 1.
So, there is a structure:
CREATE TABLE IF NOT EXISTS `categories` (
`category_id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(255)
CHARACTER SET utf8mb4 DEFAULT NULL
COMMENT 'Category title',
`parent_id` INT(11) NOT NULL DEFAULT '0'
COMMENT 'Category Parent ID',
`status` TINYINT(1) DEFAULT '0'
COMMENT 'Category active status',
`weight` INT(11) DEFAULT '0',
`slug` VARCHAR(255)
CHARACTER SET utf8mb4 DEFAULT NULL
COMMENT 'Category url alias',
PRIMARY KEY (`category_id`)
)
ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS `items` (
`item_id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(255)
CHARACTER SET utf8mb4 NOT NULL DEFAULT ''
COMMENT 'Item name',
`description` TEXT CHARACTER SET utf8mb4 NOT NULL
COMMENT 'Item description',
`user_id` INT(11) UNSIGNED NOT NULL DEFAULT '0'
COMMENT 'User id',
`category_id` INT(11) UNSIGNED NOT NULL DEFAULT '0'
COMMENT 'Category id',
`price` DECIMAL(10, 2) NOT NULL DEFAULT '0.00'
COMMENT 'Item price',
`status` INT(1) UNSIGNED NOT NULL DEFAULT '1'
COMMENT 'Item status',
`deleted` INT(1) UNSIGNED NOT NULL DEFAULT '0'
COMMENT 'Delite status',
`blocked` INT(1) UNSIGNED NOT NULL DEFAULT '0'
COMMENT 'Block status',
PRIMARY KEY (`item_id`)
)
ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS `items_images` (
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`item_id` INT(10) UNSIGNED NOT NULL DEFAULT '0',
`file` VARCHAR(255)
CHARACTER SET utf8mb4 NOT NULL DEFAULT '',
`weight` INT(11) UNSIGNED NOT NULL DEFAULT '0',
`status` TINYINT(1) UNSIGNED NOT NULL DEFAULT '0',
`deleted` TINYINT(1) UNSIGNED NOT NULL DEFAULT '0',
`created` INT(11) UNSIGNED NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
)
ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_unicode_ci
ROW_FORMAT = COMPACT;
As you can see from there, category structure also contain parent_id inside, so we can always get the category tree.
Using this query is working as well and really fast.
SELECT
`i`.*,
`c1`.`name` AS `category_name`,
`c1`.`slug` AS `category_slug`,
`c2`.`name` AS `subcategory_name`,
`c2`.`slug` AS `subcategory_slug`
FROM `items` AS `i`
LEFT JOIN `categories` AS `c2` ON `c2`.`category_id` = `i`.`category_id`
LEFT JOIN `categories` AS `c1` ON `c1`.`category_id` = `c2`.`parent_id`
WHERE `i`.`deleted` = 0 AND `i`.`blocked` = 0 AND `i`.`status` = 1
ORDER BY `i`.`created` DESC
LIMIT 40
But, if i join items_images like:
SELECT
`i`.*,
`ii`.`file` AS `image`,
`c1`.`name` AS `category_name`,
`c1`.`slug` AS `category_slug`,
`c2`.`name` AS `subcategory_name`,
`c2`.`slug` AS `subcategory_slug`
FROM `items` AS `i`
LEFT JOIN `items_images` AS `ii`
ON `i`.`item_id` = `ii`.`item_id` AND `ii`.`weight` = 1
AND `ii`.`status` = 1 AND `ii`.`deleted` = 0
LEFT JOIN `categories` AS `c2` ON `c2`.`category_id` = `i`.`category_id`
LEFT JOIN `categories` AS `c1` ON `c1`.`category_id` = `c2`.`parent_id`
WHERE `i`.`deleted` = 0 AND `i`.`blocked` = 0 AND `i`.`status` = 1
ORDER BY `i`.`created` DESC LIMIT 40
Sometimes it takes up to 1 minute on 14k items with 40k images.
Is there something i can improve?! Please note, there might be items without images too. That is not requirement.
Small addition. Even single images join on items makes the query run over a minute. here a sample:
SELECT `i`.*, `ii`.`file` AS `image` FROM `items` AS `i`
LEFT JOIN `items_images` AS `ii` ON `i`.`item_id` = `ii`.`item_id`
AND `ii`.`weight` = 1 AND `ii`.`status` = 1 AND `ii`.`deleted` = 0
WHERE `i`.`deleted` =0 AND `i`.`sold` =0 AND `i`.`blocked` =0 AND `i`.`status` = 1
ORDER BY `i`.`created` DESC
You have inconsistency in naming convention.
Index parent_id
ALTER TABLE categories ADD INDEX parent_id_ind (parent_id ASC) ;
Try to run your query again.
In databases you would use indexes to improve the speed of data retrieval. An index is typically created on columns used in JOIN, WHERE, and ORDER BY clauses.
I suggest you to improve your structure, you can take it or leave it,
Here my suggestion:
DROP TABLE IF EXISTS `category`;
CREATE TABLE `category` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT 'Category title',
`category_id` int(11) NOT NULL DEFAULT '0' COMMENT 'Category Parent ID',
`status` enum('status1','status2') COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'Category active status',
`weight` int(11) DEFAULT '0',
`slug` varchar(255) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT 'Category url alias',
PRIMARY KEY (`id`),
KEY `category_id_ind` (`category_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
DROP TABLE IF EXISTS `item`;
CREATE TABLE `item` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) CHARACTER SET utf8mb4 NOT NULL DEFAULT '' COMMENT 'Item name',
`description` text CHARACTER SET utf8mb4 NOT NULL COMMENT 'Item description',
`user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT 'User id',
`category_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT 'Category id',
`price` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT 'Item price',
`status` enum('status1','status2') COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'status1' COMMENT 'Item status',
`is_deleted` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT 'Delite status',
`is_blocked` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT 'Block status',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
DROP TABLE IF EXISTS `item_images`;
CREATE TABLE `item_images` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`item_id` int(10) unsigned NOT NULL DEFAULT '0',
`file_path` varchar(255) CHARACTER SET utf8mb4 DEFAULT NULL ,
`weight` int(11) unsigned NOT NULL DEFAULT '0',
`status` tinyint(1) unsigned NOT NULL DEFAULT '0',
`is_deleted` tinyint(1) unsigned NOT NULL DEFAULT '0',
`creation_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=COMPACT;
Add composite indexes:
items: INDEX(deleted, blocked, status, created) -- created must be last
items_images: INDEX(item_id, weight, status, deleted)
categories: INDEX(category_id, deleted, blocked, status)
Except as noted, the order of columns is not important.
Remove LEFT unless you see a need for it.
Please provide EXPLAIN SELECT ....
The likely reason for the sluggishness is having to check lots of image rows for weight=1. On top of that, the lack of any useful index means a costly table scan.

SQL query running extremely slow

I have an sql query that collects the year to date totals for all products in the inventory. This query runs quickly, returning around 5000 results in a little over a second.
The query used is
SELECT `ITINCODE` as Code, `IT_product_id` as ID,
SUM(IF(YEAR(`ITDATE`) = YEAR(CURDATE() ), IF(ITTYPE = "O",`ITVAL`, -`ITVAL` ), 0) ) as 'YTD_VAL'
FROM `FITEMS`
WHERE (ITTYPE = 'O' OR `ITTYPE` = 'R') AND ITBACKORDER = 'N' AND ITAMNT > 0 AND YEAR(`ITDATE`) >= YEAR(CURDATE() ) -1
GROUP BY `ITINCODE`
ORDER BY YTD_VAL DESC
I want to take the values in YTD_VAL and store them in the actual products table so that they can be used as an ORDER BY on other queries. I added a new field called ytd_val to the products table and then ran
UPDATE products p SET p.ytd_val =
(SELECT SUM(IF(YEAR(`ITDATE`) = YEAR(CURDATE() ), IF(ITTYPE = 'O',`ITVAL`, -`ITVAL` ), 0) ) as 'YTD_VAL'
FROM `FITEMS`
WHERE ITINCODE = p.products_model AND (ITTYPE = 'O' OR `ITTYPE` = 'R') AND ITBACKORDER = 'N' AND ITAMNT > 0 AND YEAR(`ITDATE`) >= YEAR(CURDATE() ) -1
GROUP BY `ITINCODE`
ORDER BY YTD_VAL DESC)
The idea was to run this by cron job each night so that the values were updated to reflect the previous days sales.
However, running this query has taken 10 plus minutes and it still hasn't completed.
ITINCODE in FITEMS table is the same as products_model in the products table.
IT_product_id in the FITEMS table is the same as products_id in the products table.
What can I do to speed up the query? I thought that as the original results query returns quickly enough that simply updating the values on another table would take seconds longer!
Table structure is as follows:
show create table fitems\G;
Create Table: CREATE TABLE `fitems` (
`ITUNIQUEREF` int(11) unsigned NOT NULL,
`ITAMNT` int(11) NOT NULL,
`ITREF` int(11) unsigned NOT NULL,
`ITTYPE` char(1) NOT NULL,
`ITVAL` decimal(10,4) NOT NULL,
`ITVAT` decimal(10,4) NOT NULL,
`ITPRICE` decimal(10,4) NOT NULL,
`ITDATE` date NOT NULL,
`ITBACKORDER` char(1) NOT NULL,
`ITDISC` decimal(10,2) NOT NULL,
`ITORDERREF` int(11) NOT NULL,
`ITTREF` int(11) unsigned NOT NULL,
`ITDATEDLY` date NOT NULL,
`ITINCODE` char(20) NOT NULL,
`IT_product_id` int(11) unsigned NOT NULL,
`ITBUILT` int(11) NOT NULL,
PRIMARY KEY (`ITUNIQUEREF`),
KEY `ITREF` (`ITREF`,`ITTYPE`,`ITDATE`,`ITBACKORDER`,`ITORDERREF`,`ITDATEDLY`,`ITINCODE`,`IT_product_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
.
show create table products\G;
CREATE TABLE `products` (
`products_id` int(11) NOT NULL,
`products_type` int(11) NOT NULL DEFAULT '1',
`products_quantity` float NOT NULL DEFAULT '0',
`products_model` varchar(32) CHARACTER SET utf8 DEFAULT NULL,
`products_image` varchar(64) CHARACTER SET utf8 DEFAULT NULL,
`products_price` decimal(15,4) NOT NULL DEFAULT '0.0000',
`products_group_a_price` decimal(15,4) NOT NULL,
`products_group_b_price` decimal(15,4) NOT NULL,
`products_group_c_price` decimal(15,4) NOT NULL,
`products_group_d_price` decimal(15,4) NOT NULL,
`products_group_e_price` decimal(15,4) NOT NULL,
`products_group_f_price` decimal(15,4) NOT NULL,
`products_group_g_price` decimal(15,4) NOT NULL,
`products_virtual` tinyint(1) NOT NULL DEFAULT '0',
`products_date_added` datetime NOT NULL DEFAULT '0001-01-01 00:00:00',
`products_last_modified` datetime DEFAULT NULL,
`products_date_available` datetime DEFAULT NULL,
`products_weight` float NOT NULL DEFAULT '0',
`products_status` tinyint(1) NOT NULL DEFAULT '0',
`products_tax_class_id` int(11) NOT NULL DEFAULT '0',
`manufacturers_id` int(11) DEFAULT NULL,
`products_ordered` float NOT NULL DEFAULT '0',
`products_quantity_order_min` float NOT NULL DEFAULT '1',
`products_quantity_order_units` float NOT NULL DEFAULT '1',
`products_priced_by_attribute` tinyint(1) NOT NULL DEFAULT '0',
`product_is_free` tinyint(1) NOT NULL DEFAULT '0',
`product_is_call` tinyint(1) NOT NULL DEFAULT '0',
`products_quantity_mixed` tinyint(1) NOT NULL DEFAULT '0',
`product_is_always_free_shipping` tinyint(1) NOT NULL DEFAULT '0',
`products_qty_box_status` tinyint(1) NOT NULL DEFAULT '1',
`products_quantity_order_max` float NOT NULL DEFAULT '0',
`products_sort_order` int(11) NOT NULL DEFAULT '0',
`products_canonical` text COLLATE utf8_unicode_ci NOT NULL,
`products_discount_type` tinyint(1) NOT NULL DEFAULT '0',
`products_discount_type_from` tinyint(1) NOT NULL DEFAULT '0',
`products_price_sorter` decimal(15,4) NOT NULL DEFAULT '0.0000',
`master_categories_id` int(11) NOT NULL DEFAULT '0',
`products_mixed_discount_quantity` tinyint(1) NOT NULL DEFAULT '1',
`metatags_title_status` tinyint(1) NOT NULL DEFAULT '0',
`metatags_products_name_status` tinyint(1) NOT NULL DEFAULT '0',
`metatags_model_status` tinyint(1) NOT NULL DEFAULT '0',
`metatags_price_status` tinyint(1) NOT NULL DEFAULT '0',
`metatags_title_tagline_status` tinyint(1) NOT NULL DEFAULT '0',
`pricing_group` varchar(16) COLLATE utf8_unicode_ci DEFAULT NULL,
`ytd_val` int(20) NOT NULL,
PRIMARY KEY (`products_id`),
KEY `idx_products_date_added_zen` (`products_date_added`),
KEY `idx_products_status_zen` (`products_status`),
KEY `idx_products_date_available_zen` (`products_date_available`),
KEY `idx_products_ordered_zen` (`products_ordered`),
KEY `idx_products_model_zen` (`products_model`),
KEY `idx_products_price_sorter_zen` (`products_price_sorter`),
KEY `idx_master_categories_id_zen` (`master_categories_id`),
KEY `idx_products_sort_order_zen` (`products_sort_order`),
KEY `idx_manufacturers_id_zen` (`manufacturers_id`),
KEY `products_price` (`products_price`),
KEY `products_status_products_price` (`products_status`,`products_price`),
FULLTEXT KEY `idx_enhanced_products_model` (`products_model`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
SELECT is always way faster then UPDATE.
To speed up an update:
Look at the indexes on the table you are updating: Are they all needed? If not, remove unneeded ones (I would at least remove idx_products_status_zen since that is also covered by products_status_products_price)
Look at the data model: Can you partition the table you are updating? If so, that will speed up the update since the indexes you are updating will be smaller and thus quicker to update;
Use InnoDB. It is faster;
Do you need to be ACID compliant? If not, then change the settings of InnoDB to speed up the system.
If you are really using MySQL: Switch to MariaDB: It is about 8% faster;
Install monitoring to see where your bottleneck is: IO or CPU: If it is IO on read, then try compression.
If you are satisfied with first query performance.
You can just transform your second query to use INNER JOIN like:
http://sqlfiddle.com/#!9/1028a/1
UPDATE products p
INNER JOIN (
SELECT SUM(IF(YEAR(`ITDATE`) = YEAR(CURDATE() ), IF(ITTYPE = 'O',`ITVAL`, -`ITVAL` ), 0) ) as 'YTD_VAL',
ITINCODE
FROM `FITEMS`
WHERE (ITTYPE = 'O' OR `ITTYPE` = 'R')
AND ITBACKORDER = 'N' AND ITAMNT > 0 AND YEAR(`ITDATE`) >= YEAR(CURDATE() ) -1
GROUP BY `ITINCODE`
) t
ON t.ITINCODE = p.products_model
SET p.ytd_val = t.YTD_VAL
UPDATE And by the way you don't need that ORDER BY YTD_VAL DESC it has no sense in this particular case I guess.
UPDATE 2
UPDATE products p
INNER JOIN (
SELECT SUM(IF(YEAR(`ITDATE`) = YEAR(CURDATE() ), IF(ITTYPE = 'O',`ITVAL`, -`ITVAL` ), 0) ) as 'YTD_VAL',
IT_product_id
FROM `FITEMS`
WHERE (ITTYPE = 'O' OR `ITTYPE` = 'R')
AND ITBACKORDER = 'N' AND ITAMNT > 0 AND YEAR(`ITDATE`) >= YEAR(CURDATE() ) -1
GROUP BY `IT_product_id`
) t
ON t.IT_product_id = p.products_id
SET p.ytd_val = t.YTD_VAL
I am not one to say do it, but consider the case for Covering Indexes. One in particular on table fitems. Those columns are relatively slim for that query. I will spook up one to try on particular columns. We have seen cases where terribly long queries can be completed in snappy time. But no promises. Ah, I see you have some. Was looking at the first edit with the alter tables below it. I will keep looking.
Covering Index
A covering index is one in which the query can be resolved via a quick stroll through the index b-tree, without requiring a lookup into the actual table data page. These are the ultimate nirvana for speed. Percona quick article on it.
Explain
And run the query (the update one) thru Explain and examine the output. See also the article Using Explain to Write Better Mysql Queries.
Note, some of these comments are for those that follows, not necessarily this op. He seems to have his house in order pretty well.
In your subquery, neither the order by nor the group by are necessary, so the update can be written as:
UPDATE products p
SET p.ytd_val = (SELECT SUM(IF(YEAR(`ITDATE`) = YEAR(CURDATE() ), IF(ITTYPE = 'O',`ITVAL`, -`ITVAL` ), 0) )
FROM `FITEMS`
WHERE FITEMS.ITINCODE = p.products_model AND
ITTYPE IN ('O', 'R') AND
ITBACKORDER = 'N' AND
ITAMNT > 0 AND
YEAR(`ITDATE`) >= YEAR(CURDATE() ) - 1
);
For this, you want an index on FITEMS(ITINCODE, ITBACKORDER, ITTYPE, ITAMNT, ITVAL). This might significantly speed your query.

MySQL Results pivot table - of a sort

I have a table as defined below:
CREATE TABLE `z_data` (
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`subscriberid` INT(11) NOT NULL DEFAULT '0',
`Email Address` VARCHAR(255) NOT NULL DEFAULT '',
`Title` VARCHAR(255) NOT NULL DEFAULT '',
`First Name` VARCHAR(255) NOT NULL DEFAULT '',
`Last Name` VARCHAR(255) NOT NULL DEFAULT '',
`Postal Code` VARCHAR(255) NOT NULL DEFAULT '',
`banned` INT(11) NOT NULL DEFAULT '0',
`bounced` INT(1) NOT NULL DEFAULT '0',
`unsub` INT(1) NOT NULL DEFAULT '0',
`duplicate` INT(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `idx_subscriberid` (`subscriberid`),
KEY `idx_banned` (`banned`),
KEY `idx_bounced` (`bounced`),
KEY `idx_unsub` (`unsub`),
KEY `idx_duplicate` (`duplicate`),
KEY `idx_email` (`Email Address`),
FULLTEXT KEY `idx_emailaddress` (`Email Address`)
) ENGINE=MYISAM AUTO_INCREMENT=20 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC
This table is populated from a CSV file and a query updates the subscriberid column with a number if the emailaddress is allready present in the main database with a query like:
UPDATE `z_data` AS z
LEFT JOIN main_subscribers AS b ON (z.`Email Address`=b.emailaddress && b.listid=1 && b.unsubscribed!=0)
SET z.subscriberid = b.subscriberid
WHERE b.subscriberid IS NOT NULL;";
Now, my question. I need to select the subscriberids of all of the records in z_data in rows of ten fields, this can be comma seperated lists.
my result could be similar to:
1293572,1293573,1293574,1293575,1099590,1174275,1293576,1293577,1293578,1293579,
673070,813617,1293580,1293581,1293582,1131221,1293583,1182045,419085,1293584,
1050278,1293585,1064945,638483,737691,1293586,1293587,799800,1110596,1293588,
1293589,1293590,1293591,421394,1293592,1293593,1293594,1293595,1293596,851491,
1293597,1293598,1293599,628250,1293600,1293601,1293602,535366,1293603,256590,
1293604,1293605,736956,1293606,1209511,673075,1293607,1293608,1293609,754357,
The reason for this is so that I can store these values in a TEXT field for later use in a IN clause and yet have a reasonably human readable script.
I have managed to get the first row thus:
SELECT GROUP_CONCAT(a.subscriberid) AS 'IDs' FROM (
SELECT subscriberid FROM
`z_data`
WHERE subscriberid!=0
LIMIT 1,10
) AS a
but do not know how to 'walk' though the rest of the table when the number of rows with subscriberids is unknown.
Any suggestions would be gratefully received.