Can't specify target table t4 for update in FROM clause - mysql

I am trying to update my table:
UPDATE offers t1
SET t1.deleted_at = NOW()
WHERE t1.id
NOT IN
(
SELECT f.id
FROM (
SELECT ean, MIN(net_price) as minprice
FROM offers group BY ean
)
as x inner join offers as f on f.ean = x.ean and f.net_price = x.minprice
);
can anybody help me with this issue?
I realized that I needed to solve the issue through left join, but did not understand how to apply it in my case. Thank you.
Here is create table:
CREATE TABLE `offers` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`net_price` decimal(10,2) unsigned DEFAULT NULL,
`retail_price` decimal(10,2) unsigned DEFAULT NULL,
`supplier_id` bigint unsigned DEFAULT NULL,
`manufacturer_id` bigint unsigned DEFAULT NULL,
`stock` int DEFAULT NULL,
`ean` varchar(14) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`mnn_id` bigint unsigned DEFAULT NULL,
`source_id` bigint unsigned NOT NULL,
`nds` smallint unsigned DEFAULT NULL,
`to_export` tinyint(1) DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
`deleted_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `offers_supplier_id_foreign` (`supplier_id`),
KEY `offers_manufacturer_id_foreign` (`manufacturer_id`),
KEY `offers_mnn_id_foreign` (`mnn_id`),
KEY `offers_source_id_foreign` (`source_id`),
CONSTRAINT `offers_manufacturer_id_foreign` FOREIGN KEY (`manufacturer_id`) REFERENCES `manufacturers` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `offers_mnn_id_foreign` FOREIGN KEY (`mnn_id`) REFERENCES `mnns` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `offers_source_id_foreign` FOREIGN KEY (`source_id`) REFERENCES `sources` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `offers_supplier_id_foreign` FOREIGN KEY (`supplier_id`) REFERENCES `suppliers` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=25762 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

The error you are getting is
You can't specify target table 't1' for update in FROM clause
This is a problem specific to MySQL. You cannot directly access the updated table in a subquery, you need a sub subquery :-)
Replace
inner join offers as f
by
inner join (select * from offers) as f
hence to get the query working.
You don't need the join by the way. You can just write
UPDATE offers
SET deleted_at = NOW()
WHERE (ean, net_price) NOT IN
(
SELECT ean, MIN(net_price)
FROM (select * from offers) o
GROUP BY ean
);
Another way to write this (update all rows for which exists a lower price for the same EAN):
UPDATE offers t1
SET t1.deleted_at = NOW()
WHERE EXISTS
(
SELECT NULL
FROM (SELECT * FROM offers) t2
WHERE t2.ean = t1.ean
AND t2.net_price < t1.net_price
);
Demo: https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=bfa6d74a72d40fa445ea5fa9f95803d1
By the way: You may want to add the condition deleted_at IS NULL to your update statement in order not to update rows that are already logically deleted, if such rows exists.

Related

How to order by calculated column in mysql

It is very slow to order by calculated column. Like this one:
select
`users`.`id`,
`username`,
`about_me`,
(
select
count(*)
from
`interests`
inner join `users_interests` on `interests`.`id` = `users_interests`.`interest_id`
where
`users`.`id` = `users_interests`.`user_id`
and `interest_id` in (1, 2, 4) --this is ids of interests which authenticated user has chosen
) as `interests_count`
from
`users`
order by
`interests_count` desc
So in this query i am ordering users By interests which is best suiting authenticated user. And it is very slow in large data tables. Any ideas what will be alternative and much faster solution. Thanks in advance
EDIT
Users Table
CREATE TABLE `users` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`roles_id` bigint(20) unsigned NOT NULL DEFAULT 2,
`username` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,
`about_me` varchar(70) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`email` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`status` tinyint(1) NOT NULL DEFAULT 0,
`email_verified_at` timestamp NULL DEFAULT NULL,
`password` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `users_username_unique` (`username`),
UNIQUE KEY `users_email_unique` (`email`),
KEY `users_roles_id_foreign` (`roles_id`),
CONSTRAINT `users_roles_id_foreign` FOREIGN KEY (`roles_id`) REFERENCES
`user_roles` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=50102 DEFAULT CHARSET=utf8mb4
COLLATE=utf8mb4_unicode_ci
Interests Table
CREATE TABLE `interests` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,
`interest_category_id` bigint(20) unsigned NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `interests_interest_category_id_foreign` (`interest_category_id`),
CONSTRAINT `interests_interest_category_id_foreign` FOREIGN KEY
(`interest_category_id`) REFERENCES `interests_categories` (`id`) ON DELETE
CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=61 DEFAULT CHARSET=utf8mb4
COLLATE=utf8mb4_unicode_ci
Pivot table between Users and Interests
CREATE TABLE `users_interests` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`user_id` bigint(20) unsigned NOT NULL,
`interest_id` bigint(20) unsigned NOT NULL,
PRIMARY KEY (`id`),
KEY `users_interests_user_id_foreign` (`user_id`),
KEY `users_interests_interest_id_foreign` (`interest_id`),
CONSTRAINT `users_interests_interest_id_foreign` FOREIGN KEY (`interest_id`) REFERENCES `interests` (`id`) ON DELETE CASCADE,
CONSTRAINT `users_interests_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=251552 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
Without test data it's difficult to know if this would be faster than your current query, but you might find the use of a Common Table Expression (a.k.a. WITH clause) to be helpful here:
with cteCount AS (select ui.`user_id`,
count(*) as `interests_count`
from `interests` i
inner join `users_interests` ui
on i.`id` = ui.`interest_id`
where ui.`interest_id` in (1, 2, 4))
select u.`id`,
`username`,
`about_me`,
cc.`interests_count`
from `users` u
inner join cteCount cc
on cc.`user_id` = u.`id`
order by
cc.`interests_count` desc
The performance of this query will be affected by the presence or absence of indexes on the appropriate columns. Just glancing at it I'd say you should have the following indexes available:
interests
Index 1: id
users_interests
Index 1: interest_id
users
Index 1: id
Try to directly join and aggregate.
SELECT u.id,
u.username,
u.about_me,
count(ui.interest_id) interests_count
FROM users u
LEFT JOIN users_interests ui
ON ui.user_id = u.id
AND ui.interest_id IN (1, 2, 4)
GROUP BY u.id,
u.username,
u.about_me;
Additionally create an index supporting the aggregation.
CREATE INDEX users_id_username_about_me
ON users
(id,
username,
about_me);

MySQL not selecting row if foreign key column has NULL value

The following SQL works, but if a row has transfers.driver as NULL, that row won't be selected. Once I add a value in for driver that row will be selected. How can I select the row even if driver is NULL?
SELECT
transfers.id,
transfers.name AS name,
transfers.pickup_time AS pickup_time,
transfers.voucher AS voucher,
transfer_types.name AS type,
drivers.name AS driver
FROM
transfers
INNER JOIN transfer_types ON transfer_types.id = transfers.type
INNER JOIN drivers ON drivers.id = transfers.driver
Here's the structure of the table:
CREATE TABLE `transfers` (
`id` int(10) UNSIGNED NOT NULL,
`voucher` varchar(24) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`type` int(10) UNSIGNED DEFAULT NULL,
`pickup_time` time DEFAULT NULL,
`name` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`driver` int(10) UNSIGNED DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
And here's the foreign keys:
ALTER TABLE `transfers`
ADD CONSTRAINT `transfers_driver_foreign`
FOREIGN KEY (`driver`) REFERENCES `drivers` (`id`),
ADD CONSTRAINT `transfers_type_foreign`
FOREIGN KEY (`type`) REFERENCES `transfer_types` (`id`);
use left join instead of inner join:
SELECT
transfers.id,
transfers.name AS name,
transfers.pickup_time AS pickup_time,
transfers.voucher AS voucher,
transfer_types.name AS type,
drivers.name AS driver
FROM
transfers
LEFT JOIN transfer_types ON transfer_types.id = transfers.type
LEFT JOIN drivers ON drivers.id = transfers.driver

Mysql update query with inner table join

I have problem with update query in mysql.
When I try this
update planned_expense p1
set deleted=1
where case_id=204
and deleted =0
and type='MONTHLY'
and planned_date>='2017-04-01'
and id > (select min(id) from planned_expense p2 where
p2.case_id = p1.case_id
and p2.planned_date = p1.planned_date
and p2.account = p1.account
and p2.type = p1.type and p2.deleted = 0)
I get
You can't specify target table 'p1' for update in FROM clause
And when I try
update planned_expense p1
set deleted=1
where case_id=204
and deleted =0
and type='MONTHLY'
and planned_date>='2017-04-01'
and id > (select min(id)
from (select * from planned_expense p2
where p2.case_id=p1.case_id and
p2.planned_date=p1.planned_date
and p2.account=p1.account and p2.type=p1.type and p2.deleted=0) p3)
I get
Unknown column 'p1.case_id' in 'where clause'
What should I write in order to update those records?
Tnank you.
This is my table
DROP TABLE IF EXISTS `bes-ers`.`planned_expense`;
CREATE TABLE `bes-ers`.`planned_expense` (
`ID` bigint(20) NOT NULL AUTO_INCREMENT,
`case_id` bigint(20) DEFAULT NULL,
`deleted` tinyint(1) DEFAULT '0',
`planned_date` datetime DEFAULT NULL,
`addition_mark` int(11) DEFAULT NULL,
`code` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`planned_amount` decimal(22,4) DEFAULT NULL,
`account` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`type` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`search_field` varchar(4096) COLLATE utf8_unicode_ci DEFAULT NULL,
`description` varchar(4096) COLLATE utf8_unicode_ci DEFAULT NULL,
`created_by_id` bigint(20) DEFAULT NULL,
`locked` tinyint(4) DEFAULT '0',
PRIMARY KEY (`ID`),
KEY `FK_planned_expenses_1` (`case_id`),
KEY `FK_planned_expenses_created_by_id` (`created_by_id`),
KEY `planned_expense_planned_date` (`planned_date`),
KEY `planned_expense_type` (`type`),
KEY `planned_expense_deleted` (`deleted`),
CONSTRAINT `FK_planned_expenses_1` FOREIGN KEY (`case_id`) REFERENCES `bankruptcy_case` (`ID`) ON DELETE SET NULL ON UPDATE SET NULL,
CONSTRAINT `FK_planned_expenses_created_by_id` FOREIGN KEY (`created_by_id`) REFERENCES `user` (`ID`)
) ENGINE=InnoDB AUTO_INCREMENT=13172954 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
Mysql don't allow the update of the table if is involved in select but you can avoid this problem using a dinamica select table eg:
update planned_expense
set deleted=1
where case_id=204
and deleted =0
and type='MONTHLY'
and planned_date>='2017-04-01'
and id > ( select t.min_id from (select min(id) min_id
from planned_expense p2
INNER JOIN planned_expense p1 on p2.case_id=p1.case_id
and p2.planned_date=p1.planned_date
and p2.account=p1.account
and p2.type=p1.type and p2.deleted=0) t )
try adding a composite index as
create index idx_test on planned_expense (case_id, planned_date, account)
As explained by #scaisEdge, you have to use a join and to speed up you query,
try creating a composite key as per your where clause.
Something like:
KEY case_id_deleted_type_planned_date_id (case_id, deleted, type, planned_date, id)
Use Explain to check if your query is using proper indexes or not.
Thank you all, I did what I wanted to by creating temp table and than working with it.
Thanks for

MySQL Improve performance execution time

I am trying to improve performance of following query which took 93.2 sec to execute query below:
SELECT year(date), month(date), `country_name_name`,
CEIL(count(res.`user_xmpp_login`) /DAY(LAST_DAY(date))) as avgUser,
CEIL(count(res.user)/DAY(LAST_DAY(date))) as avgPurchase
FROM
( SELECT DATE(`user_registration_timestamp`) as date,
user_country,
NULL as user, `user_xmpp_login`
FROM users
WHERE `user_registration_timestamp` >= "2015-01-01 00:00:00"
AND `user_registration_timestamp` < "2016-01-01 00:00:00"
UNION ALL
SELECT DATE(`ts`) as date, user_country, user, NULL as `user_xmpp_login`
FROM purchase_log p
INNER JOIN users u ON u.`user_xmpp_login` = p.`user`
WHERE `ts` >= "2015-01-01 00:00:00"
AND `ts` < "2016-01-01 00:00:00"
AND result in ('ok', 'cancelled', 'pending')
) AS res
INNER JOIN countries c ON c.`country_id` = res.`user_country`
INNER JOIN country_names cn
ON (cn.`country_name_country` = c.`country_id`
AND cn.`country_name_language` = 'en')
GROUP BY 1,2,3
ORDER BY 4 DESC,5 DESC, 3 ASC;
Explain command shows:
And structure of each table is:
purchase table:
CREATE TABLE `purchase` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`result` varchar(32) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`),
KEY `iuser` (`user`),
) ENGINE=InnoDB AUTO_INCREMENT=12710221 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
users table:
CREATE TABLE `users` (
`user_id` int(11) NOT NULL AUTO_INCREMENT,
`user_country` int(11) DEFAULT NULL,
`user_xmpp_login` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`user_registration_timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`user_id`),
UNIQUE KEY `user_xmpp_login_UNIQUE` (`user_xmpp_login`),
KEY `user_country_FK` (`user_country`),
KEY `user_registration_timestamp` (`user_registration_timestamp`),
CONSTRAINT `users_country_FK` FOREIGN KEY (`user_country`)
REFERENCES `countries` (`country_id`) ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=33504745 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
countries table
CREATE TABLE `countries` (
`country_id` int(11) NOT NULL AUTO_INCREMENT,
`country_code` varchar(2) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`country_id`),
) ENGINE=InnoDB AUTO_INCREMENT=508 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
country names
CREATE TABLE `country_names` (
`country_name_id` int(11) NOT NULL AUTO_INCREMENT,
`country_name_country` int(11) NOT NULL,
`country_name_language` char(2) COLLATE utf8_unicode_ci NOT NULL,
`country_name_name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`country_name_id`),
UNIQUE KEY `country_name_country_language_UNIQUE`
(`country_name_country`,`country_name_language`),
KEY `country_name_language` (`country_name_language`),
CONSTRAINT `country_name_country` FOREIGN KEY (`country_name_country`)
REFERENCES `countries` (`country_id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=45793 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
Is there any recommendations?
If you time each subquery, I think you will find users is the slowest component.
The purchase_log subquery can probably be improved with this "covering" INDEX(result, ts, user).
Combine the two "country" tables!. Use CHAR(2) CHARACTER SET ascii for the PRIMARY KEY and the JOINs to other tables. It is only 2 bytes, unlike INT, which is 4 bytes and VARCHAR..., which is 3 bytes (in this case).
You mention ts, but I don't see where it is coming from. If it is in purchase_log, then that table needs INDEX(user, ts).
What percentage of the users involved 2015? If it is more than about 20%, the INDEX(user_registration_timestamp) won't help.
Consider: Get rid of PRIMARY KEY (country_name_id), and promote the UNIQUE key to PRIMARY.
The biggest problem seems to be in your users table. Remember, mysql can only use one index per table for most situations. On your users table, the user_xmpp_login_UNIQUE column has been used to join it to the purchase_log table. There fore, the user_registration_timestamp index is not being used on the comparison involving the timestamp column.
One suggestion is to create a composite index on the user_xmpp_login and user_registration_timestamp columns.

Need help to optimize MySQL query

I have 6 tables:
CREATE TABLE IF NOT EXISTS `sbpr_groups` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`active` tinyint(1) DEFAULT '0',
`dnd` tinyint(1) DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=32 ;
CREATE TABLE IF NOT EXISTS `sbpr_newsletter` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`created_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`from` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`mail` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`subject` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`body` text COLLATE utf8_unicode_ci,
`attach1` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`attach2` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`attach3` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=14;
CREATE TABLE IF NOT EXISTS `sbpr_news_groups` (
`newsletter_id` int(11) NOT NULL,
`groups` int(11) NOT NULL,
KEY `fk_sbpr_news_groups_sbpr_newsletter1` (`newsletter_id`),
KEY `fk_sbpr_news_groups_sbpr_groups1` (`groups`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
CREATE TABLE IF NOT EXISTS `sbpr_news_recs` (
`newsletter_id` int(11) NOT NULL,
`recipients` int(11) NOT NULL,
KEY `fk_sbpr_news_recs_sbpr_newsletter1` (`newsletter_id`),
KEY `fk_sbpr_news_recs_sbpr_recipients1` (`recipients`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
CREATE TABLE IF NOT EXISTS `sbpr_recipients` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`mail` varchar(160) DEFAULT NULL,
`date_reg` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`active` tinyint(1) DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=3008 ;
CREATE TABLE IF NOT EXISTS `sbpr_rec_groups` (
`rec_id` int(11) NOT NULL,
`group` int(11) NOT NULL,
KEY `fk_sbpr_rec_groups_sbpr_recipients` (`rec_id`),
KEY `fk_sbpr_rec_groups_sbpr_groups1` (`group`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
With this foreign keys:
ALTER TABLE `sbpr_news_groups`
ADD CONSTRAINT `fk_sbpr_news_groups_sbpr_groups1`
FOREIGN KEY (`groups`) REFERENCES `sbpr_groups` (`id`)
ON DELETE CASCADE ON UPDATE NO ACTION,
ADD CONSTRAINT `fk_sbpr_news_groups_sbpr_newsletter1`
FOREIGN KEY (`newsletter_id`) REFERENCES `sbpr_newsletter` (`id`)
ON DELETE CASCADE ON UPDATE NO ACTION;
ALTER TABLE `sbpr_news_recs`
ADD CONSTRAINT `fk_sbpr_news_recs_sbpr_newsletter1`
FOREIGN KEY (`newsletter_id`) REFERENCES `sbpr_newsletter` (`id`)
ON DELETE CASCADE ON UPDATE NO ACTION,
ADD CONSTRAINT `fk_sbpr_news_recs_sbpr_recipients1`
FOREIGN KEY (`recipients`) REFERENCES `sbpr_recipients` (`id`)
ON DELETE CASCADE ON UPDATE NO ACTION;
ALTER TABLE `sbpr_rec_groups`
ADD CONSTRAINT `fk_sbpr_rec_groups_sbpr_groups1`
FOREIGN KEY (`group`) REFERENCES `sbpr_groups` (`id`)
ON DELETE CASCADE ON UPDATE NO ACTION,
ADD CONSTRAINT `fk_sbpr_rec_groups_sbpr_recipients`
FOREIGN KEY (`rec_id`) REFERENCES `sbpr_recipients` (`id`)
ON DELETE CASCADE ON UPDATE NO ACTION;
Visual structure of tables:
I want to select all rows from sbpr_newsletter table, and add to each of these lines the number of rows from sbpr_recipients whose id prescribed in sbpr_news_recs or prescribed in sbpr_rec_groups depends on FKs.
Ex. I want to select count of all recipients of current newsletter wihch are in sbpr_news_recs or exists in group which are in sbpr_rec_groups plus count of active recipients.
I have working SQL:
SELECT d.id, d.subject , d.created_date,
(SELECT count(*) FROM sbpr_recipients r
LEFT JOIN sbpr_news_recs nr ON nr.recipients = r.id
LEFT JOIN sbpr_rec_groups g ON g.rec_id = r.id
LEFT JOIN sbpr_news_groups ng ON ng.groups = g.group
WHERE nr.newsletter_id = d.id OR ng.newsletter_id = d.id) AS repicients,
(SELECT count(*) FROM sbpr_recipients r
LEFT JOIN sbpr_news_recs nr ON nr.recipients = r.id
LEFT JOIN sbpr_rec_groups g ON g.rec_id = r.id
LEFT JOIN sbpr_news_groups ng ON ng.groups = g.group
WHERE (nr.newsletter_id = d.id OR ng.newsletter_id = d.id)
AND r.active = 1) AS active_repicients
FROM sbpr_newsletter d
ORDER BY d.id ASC, d.id
Explain of this sql:
Question:
How can I optimize my sql?
Just approach to optimize, two SELECT queries are transfered into JOIN clause -
SELECT d.id
, d.subject
, d.created_date
, count(if(nr_newsletter_id is not null or ng_newsletter_id is not null, 1, null)) repicients
, count(if((nr_newsletter_id is not null or ng_newsletter_id is not null) and t.active = 1, 1, null)) active_repicients
FROM
sbpr_newsletter d
LEFT JOIN (
SELECT nr.newsletter_id nr_newsletter_id
, ng.newsletter_id ng_newsletter_id
, r.active
FROM
sbpr_recipients r
LEFT JOIN sbpr_news_recs nr
ON nr.recipients = r.id
LEFT JOIN sbpr_rec_groups g
ON g.rec_id = r.id
LEFT JOIN sbpr_news_groups ng
ON ng.groups = g.group
) t
ON nr_newsletter_id = d.id OR ng_newsletter_id = d.id
GROUP BY
d.id;
I rewrited your query a little, it is not tested, but try it.
You COULD create a view and query that instead - trade off is storage but should vastly remove load from server...
The subquery for recipients / active_recipients runs twice, and each time returns 3311 records, so it would be worth defining as a view.
Otherwise, define indexes on the foreign keys you use in joins.