Reputation: 13
I have been stuck on this query for more than 2 days. I already tried sub-queries (in SELECT), I already tried "join everything with LEFT JOIN and count(column.id)
(NULL = 0)".
Campaigns Table:
CREATE TABLE `campaigns` (
`id` bigint(20) UNSIGNED NOT NULL,
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
ALTER TABLE `campaigns`
ADD PRIMARY KEY (`id`);
ALTER TABLE `campaigns`
MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT;
Operating Systems Table:
CREATE TABLE `operating_systems` (
`id` bigint(20) UNSIGNED NOT NULL,
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`version` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
ALTER TABLE `operating_systems`
ADD PRIMARY KEY (`id`),
ADD KEY `operating_systems_name_index` (`name`),
ADD KEY `operating_systems_version_index` (`version`),
ADD KEY `operating_systems_name_version_index` (`name`,`version`);
ALTER TABLE `operating_systems`
MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT;
Clicks Table:
CREATE TABLE `clicks` (
`id` bigint(20) UNSIGNED NOT NULL,
`campaign_id` bigint(20) UNSIGNED NOT NULL,
`operating_system_id` bigint(20) UNSIGNED NOT NULL,
`cost_integral` int(11) NOT NULL DEFAULT 0,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
ALTER TABLE `clicks`
ADD PRIMARY KEY (`id`),
ADD KEY `c_c_ca` (`campaign_id`,`created_at`),
ADD KEY `c_c_os_ca` (`campaign_id`,`operating_system_id`,`created_at`);
ALTER TABLE `clicks`
ADD CONSTRAINT `c_c` FOREIGN KEY (`campaign_id`) REFERENCES `campaigns` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
ADD CONSTRAINT `c_os` FOREIGN KEY (`operating_system_id`) REFERENCES `operating_systems` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE `clicks`
MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT;
Conversions Table:
CREATE TABLE `conversions` (
`id` bigint(20) UNSIGNED NOT NULL,
`click_id` bigint(20) UNSIGNED NOT NULL,
`payout_integral` int(11) NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
ALTER TABLE `conversions`
ADD PRIMARY KEY (`id`),
ADD KEY `conversions_created_at_index` (`created_at`),
ADD KEY `conversions_click_id_created_at_index` (`click_id`,`created_at`);
ALTER TABLE `conversions`
ADD CONSTRAINT `conversions_click_id_foreign` FOREIGN KEY (`click_id`) REFERENCES `clicks` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE `conversions`
MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT;
Slow Query:
select operating_systems.name,
ifnull(sum(metrics.clicks_count), 0) as clicks_count,
ifnull(sum(metrics.conversions_count), 0) as conversions_count,
ifnull(round(((100 / sum(metrics.clicks_count)) * sum(metrics.conversions_count)), 2), 0) as conversion_rate,
cast(ifnull(sum(metrics.cost_integral), 0) as unsigned) as cost_integral,
cast(ifnull((sum(metrics.cost_integral) / sum(metrics.clicks_count)), 0) as unsigned) as cpc_integral,
cast(ifnull(sum(metrics.revenue_integral), 0) as unsigned) as revenue_integral,
cast(ifnull((ifnull(sum(metrics.revenue_integral), 0) / sum(metrics.clicks_count)), 0) as unsigned) as epc_integral,
cast((ifnull(sum(metrics.revenue_integral), 0) - ifnull(sum(metrics.cost_integral), 0)) as signed) as profit_integral,
round((
case
when (ifnull(sum(metrics.revenue_integral), 0) = 0 and ifnull(sum(metrics.cost_integral), 0) = 0)
then 0
when (ifnull(sum(metrics.revenue_integral), 0) = 0 and ifnull(sum(metrics.cost_integral), 0) > 0)
then - 100
when (ifnull(sum(metrics.revenue_integral), 0) > 0 and ifnull(sum(metrics.cost_integral), 0) = 0)
then 100
when (ifnull(sum(metrics.revenue_integral), 0) > 0 and ifnull(sum(metrics.cost_integral), 0) > 0)
then ((sum(metrics.revenue_integral) / sum(metrics.cost_integral)) * 100)
end
), 2) as roi
from operating_systems
left join (
select c1.operating_system_id as operating_system_id,
count(c1.id) as clicks_count,
sum(c1.cost_integral) as cost_integral,
count(c2.id) as conversions_count,
sum(c2.payout_integral) as revenue_integral
from clicks as c1
left join conversions as c2 on c2.click_id = c1.id
where c1.campaign_id = '2' and c1.created_at >= '2021-07-06 00:00:00' and c1.created_at <= '2021-07-14 23:59:59'
group by c1.operating_system_id
) as metrics on operating_systems.id = metrics.operating_system_id
group by operating_systems.name;
Explain:
id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
---|---|---|---|---|---|---|---|---|---|---|---|
1 | PRIMARY | operating_systems | NULL | index | operating_systems_name_index,operating_systems_name_version_index | operating_systems_name_index | 1022 | NULL | 12 | 100.00 | Using index |
1 | PRIMARY | NULL | ref | <auto_key0> | <auto_key0> | 8 | dn_tracker.operating_systems.id | 777 | 100.00 | NULL | |
2 | DERIVED | c1 | NULL | ref | c_os,c_ca,c_c_v,c_c_ca,c_c_b_ca,c_c_os_ca | c_c_os_ca | 8 | const | 155472 | 50.00 | Using index condition |
2 | DERIVED | c2 | NULL | ref | conversions_click_id_created_at_index | conversions_click_id_created_at_index | 8 | dn_tracker.c1.id | 1 | 100.00 | NULL |
Profiling:
Query_ID | Duration | Query |
---|---|---|
1 | 1.49035375 | select operating_systems.name, ifnull(sum(metrics.clicks_count), 0) as clicks_count, ifnull(sum(metrics.conversions_count), 0) as conversions_count, ifnull(round(((100 / sum(metrics.clicks_count)) * sum(metrics.conversions_count)), 2), 0) as conversion_rate, cast(ifnull(sum(metri |
What could be the reason that this query, with "only" ~150,000 rows, takes between 1.5 and 6 seconds? I think I created the right indexes etc., so I don't understand why this query takes so long on a server with 12 CPUs (Dedicated) and 16 GB RAM.
Upvotes: 0
Views: 125
Reputation: 142518
Build and maintain a summary table. Add the day's data to the summary table after midnight each night. Then rewrite this "report" to work from the summary table.
More details: http://mysql.rjweb.org/doc.php/summarytables
In some cases, a summary table will speed 'reports' up by 10-fold.
Also, this may help:
conversions: INDEX(click_id, payout_integral)
MySQL has essentially no parallel execution.
Upvotes: 0