MySQL Multiple COUNT in the same table in a single request - mysql

I need some help with my SQL request. I have this table:
CREATE TABLE `contact` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`first_name` varchar(65) DEFAULT NULL,
`last_name` varchar(65) DEFAULT NULL,
`phonenumber` varchar(45) NOT NULL,
`avinumber` varchar(45) DEFAULT NULL,
`date` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
`source` varchar(45) NOT NULL,
`nbrtryies` int(11) NOT NULL DEFAULT '0',
`treaty_at` date DEFAULT NULL,
`comment` varchar(255) DEFAULT '',
`status` varchar(45) DEFAULT 'KO',
`campagne_id` int(11) NOT NULL,
`treated_by` int(11) DEFAULT NULL,
`nodetree_id` int(11) DEFAULT NULL,
`avi` varchar(45) DEFAULT NULL,
`sessionid` varchar(45) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `id_UNIQUE` (`id`),
KEY `fk_contact_campagne1_idx` (`campagne_id`),
KEY `fk_contact_agent1_idx` (`treated_by`),
CONSTRAINT `fk_contact_agent` FOREIGN KEY (`treated_by`) REFERENCES `user` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT `fk_contact_campagne1` FOREIGN KEY (`campagne_id`) REFERENCES `campagne` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=82 DEFAULT CHARSET=latin1;
I need to count the number of the rows and the number of the rows when the column:
treated_by
has a certain value and group the result by that column
Thsi is what I did but I doesn't seem to work:
SELECT
co.treated_by AS userId,
COUNT(*) AS treated,
SUM(CASE WHEN ca.userId=1 THEN 1 ELSE 0 END) AS total
FROM
contact AS co
INNER JOIN
campagne AS ca ON ca.id = co.campagne_id
WHERE
(co.date BETWEEN '2013-07-09' AND '2014-08-15')
AND co.treated_by IN (2 , 40)
GROUP BY co.treated_by
This is what I got:
----------------------------
| userId | treated | total |
----------------------------
| 2 | 5 | 5 |
----------------------------
| 40 | 3 | 3 |
----------------------------
And I need something like:
----------------------------
| userId | treated | total |
----------------------------
| 2 | 5 | 20 |
----------------------------
| 40 | 3 | 20 |
----------------------------
Thanks a lot for your help

GROUP BY co.treated_by , It produces record COUNT(*) AS treated 5 and 3 , so SUM(CASE WHEN ca.userId=1 THEN 1 ELSE 0 END) AS total , It is impossible much than 20 and 20.

If the condition is on co.treated_by, then why does the logic contain ca.userId?
Perhaps this will work:
SELECT co.treated_by AS userId,
COUNT(*) AS treated,
SUM(co.treated_by) AS total
FROM contact co INNER JOIN
campagne ca
ON ca.id = co.campagne_id
WHERE (co.date BETWEEN '2013-07-09' AND '2014-08-15') AND
co.treated_by IN (2 , 40)
GROUP BY co.treated_by;
Note that I also replaced the calculation for total with a simpler version supported by MySQL.

Related

need help in query optimization

I have below query where it is taking long time to execute as it is having OR opeartion.Here Institution 59958 is a global institution. It can have stats itself and its children can have stats; so 'parentinstitutionid' with 59958 or institutionid 59958 we require usage data for.so I am using OR opeartor.
select date(SUBDATE(m.timestamp, INTERVAL (day(m.timestamp) -1) day)) as month,
count(*) as c,
sum(case when a.streamid = 5 then 1 else 0 end) as education,
sum(case when a.streamid in(7, 1) then 1 else 0 end) as research,
sum(case when searchterms <> '' then 1 else 0 end) as search
from stats_to_institution as s
join masterstats_innodb as m on s.statid = m.id
left join articles as a on (a.productid >= 49 and a.productid = m.article)
where m.timestamp >= '2022-01-01'
and (s.institutionid = 59958 or s.institutionid in ( select institutionid from institutions where parentinstitutionid = 59958))
group by month;
Here below condition is taking time
(s.institutionid = 59958 or s.institutionid in (select institutionid from institutions where parentinstitutionid = 59958))
I cannot use CTE as it is on 5.6 version.Is any other way to modify above condition for good performance?.
If I remove s.institutionid = 59958 it takes only 5 secs to run as it will not have OR operator.
Any suggestion on this?
table structure as follows
CREATE TABLE `institutions` (
`InstitutionID` int(11) NOT NULL AUTO_INCREMENT,
`Name` varchar(200) DEFAULT NULL,
`Approved` tinyint(1) NOT NULL DEFAULT '0',
`DateAdded` datetime DEFAULT CURRENT_TIMESTAMP,
`IsAcademic` tinyint(1) DEFAULT NULL,
`IsIndustry` tinyint(1) DEFAULT NULL,
`LogoFile` varchar(50) DEFAULT NULL,
`NotifyLibEveryXRequests` int(11) DEFAULT NULL,
`IsParentInstitution` int(1) NOT NULL DEFAULT '0',
`ParentInstitutionID` int(11) DEFAULT NULL,
PRIMARY KEY (`InstitutionID`),
KEY `Institutions_Name` (`Name`),
KEY `ParentInstitutionID` (`ParentInstitutionID`),
FULLTEXT KEY `Name` (`Name`)
) ;
CREATE TABLE `masterstats_innodb` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`page` text COLLATE utf8_unicode_ci NOT NULL,
`video` int(11) NOT NULL,
`language` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
`referrer` text COLLATE utf8_unicode_ci NOT NULL,
`joveuser` varchar(64) COLLATE utf8_unicode_ci DEFAULT NULL,
`timestamp` date NOT NULL DEFAULT '0000-00-00',
PRIMARY KEY (`id`,`timestamp`),
KEY `joveuser` (`joveuser`),
KEY `institutionid` (`institutionid`),
KEY `timestamp` (`timestamp`),
KEY `idx__video_timestamp` (`video`,`timestamp`)
) ;
CREATE TABLE `stats_to_institution` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`statid` int(11) NOT NULL,
`institutionid` int(11) NOT NULL,
PRIMARY KEY (`id`,`institutionid`),
UNIQUE KEY `statid_2` (`statid`,`institutionid`),
KEY `statid` (`statid`),
KEY `institutionid` (`institutionid`)
) ;
CREATE TABLE `articles` (
`ProductID` int(11) NOT NULL,
`Name` varchar(1000) DEFAULT NULL,
`Tags` varchar(1000) NOT NULL,
`D` varchar(2000) DEFAULT NULL,
`Active` tinyint(1) DEFAULT NULL,
`UserID` int(11) DEFAULT NULL,
`DateAdded` datetime DEFAULT NULL,
`Detail_Abstract` text,
`StreamID` int(11) DEFAULT NULL COMMENT '-1 = Errata, 1= Article, 2= Advertisment, 3 = Editorial, 4= Junk, 5=SE',
`DatePublished` datetime DEFAULT NULL,
`AccessType` int(11) DEFAULT NULL COMMENT '-1=Unpublished, 0=Closed, 1=Free, 2=Open, 3 = Open UK',
`Rep_Results` text,
`Stage` int(11) DEFAULT NULL,
`SectionID` int(11) DEFAULT NULL,
PRIMARY KEY (`ProductID`),
KEY `Articles_StreamID_Active_DatePublished` (`StreamID`,`Active`,`DatePublished`),
KEY `articles_idx_sectionid` (`SectionID`),
FULLTEXT KEY `DetailAbstractTest` (`Detail_Abstract`,`Name`),
FULLTEXT KEY `Materials` (`Materials`),
FULLTEXT KEY `title` (`Name`)
);
explain result
+----+-------------+--------------+--------+------------------------------------------------------------------------------------------------------------------------------------------+---------------------+---------+-----------------+----------+----------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------------+--------+------------------------------------------------------------------------------------------------------------------------------------------+---------------------+---------+-----------------+----------+----------------------------------------------+
| 1 | PRIMARY | m | ALL | PRIMARY,timestamp_video,joveuser,institutionid,video_institutionid,user_id,ip_binary,time_on_page,Article,timestamp,idx__video_timestamp | NULL | NULL | NULL | 19653526 | Using where; Using temporary; Using filesort |
| 1 | PRIMARY | a | eq_ref | PRIMARY | PRIMARY | 4 | stats.m.Article | 1 | Using where |
| 1 | PRIMARY | s | ref | statid_2,statid,institutionid | statid_2 | 4 | stats.m.id | 1 | Using where; Using index |
| 2 | SUBQUERY | institutions | ref | PRIMARY,ParentInstitutionID | ParentInstitutionID | 5 | const | 173 | Using index |
+----+-------------+--------------+--------+------------------------------------------------------------------------------------------------------------------------------------------+---------------------+---------+-----------------+----------+----------------------------------------------+
No CTE how about join?
select date(SUBDATE(m.timestamp, INTERVAL (day(m.timestamp) -1) day)) as month,
count(*) as c,
sum(case when a.streamid = 5 then 1 else 0 end) as education,
sum(case when a.streamid in(7, 1) then 1 else 0 end) as research,
sum(case when searchterms <> '' then 1 else 0 end) as search
from stats_to_institution as s
join (select institutionid
from institutions
where parentinstitutionid = 59958
union all select 59998
) x on x.institutionid = s.institutionid
join masterstats_innodb as m on s.statid = m.id
left join articles as a on (a.productid >= 49 and a.productid = m.article)
where m.timestamp >= '2022-01-01'
group by month;

Modify child record (update modified_date and is_delete) when parents' respective values are changed

My question is in reference to this question.
Say I have three tables, user, country and user_activity whose schema/table-structure are given below:-
|---------------------------------------------|
| id | fname | lname | status |
|---------------------------------------------|
country:-
|--------------------------------|
| id | name | status |
|--------------------------------|
user_activity
|-------------------------------------------------------------------------|
| id | user_id | activity_type | country_id | location | status |
|-------------------------------------------------------------------------|
Say, I create the user table like this:-
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`fname` varchar(255) NOT NULL,
`lname` varchar(255) NOT NULL,
`status` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `iduser` (`id`,`status`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
Then, I create the country table like this:-
CREATE TABLE `country` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`status` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `idcountry` (`id`,`status`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
I want to create a table user_activity where the status of user_activity record would change according to the status of user if user_activity.user_id matches user.id
At the same time, if status of user_activity record would change according to the status of country if user_activity.country_id matches country.id.
How can I do that? How can I achieve the objective at the database level, instead of setting it via PHP scripting?

mysql sum with group by query need specific sum

Run code in http://rextester.com/OZKM95674
I am trying to come up with an fast and easy way to get the sum of quantity for all variations of an item but also include data such as all tags (comma seperated) and one of the images for an item (doesn't matter which one). There are many tables involve shown below:
Query 1: This query below gets the data I want for quantity but is missing data I need for my application (see query 2).
SELECT phppos_items.item_id,
phppos_items.name,
SUM(phppos_location_item_variations.quantity) as quantity
FROM `phppos_items`
LEFT JOIN `phppos_item_variations`
ON `phppos_item_variations`.`item_id` = `phppos_items`.`item_id`
LEFT JOIN `phppos_location_item_variations`
ON `phppos_location_item_variations`.`item_variation_id` = `phppos_item_variations`.`id`
and `phppos_location_item_variations`.`location_id` = 1
GROUP BY `phppos_items`.`item_id`
Result:
+---------+------+----------+
| item_id | name | quantity |
+---------+------+----------+
| 1 | TEST | 10 |
+---------+------+----------+
Query 2: But in the application I actually need more data about an item such as tags and one image (could be many just need one). I need the exact output as below but instead of the quantity being 60 I need it to be 20 like before. I know the reason this is happening because when I do group by; there is more than one row because there are 3 tags and 1 image.
SELECT phppos_items.item_id,
phppos_items.name, SUM(phppos_location_item_variations.quantity) as quantity,
`phppos_item_images`.`image_id` as `image_id`,
GROUP_CONCAT(DISTINCT phppos_tags.name) as tags
FROM `phppos_items`
LEFT JOIN `phppos_item_variations`
ON `phppos_item_variations`.`item_id` = `phppos_items`.`item_id`
LEFT JOIN `phppos_location_item_variations`
ON `phppos_location_item_variations`.`item_variation_id` = `phppos_item_variations`.`id`
and `phppos_location_item_variations`.`location_id` = 1
LEFT JOIN `phppos_items_tags`
ON `phppos_items_tags`.`item_id` = `phppos_items`.`item_id`
LEFT JOIN `phppos_tags`
ON `phppos_tags`.`id` = `phppos_items_tags`.`tag_id`
LEFT JOIN `phppos_item_images`
ON `phppos_items`.`item_id` = `phppos_item_images`.`item_id`
WHERE `phppos_items`.`deleted` = 0
AND `phppos_items`.`system_item` = 0
GROUP BY `phppos_items`.`item_id`
+---------+------+----------+----------+--------------------+
| item_id | name | quantity | image_id | tags |
+---------+------+----------+----------+--------------------+
| 1 | TEST | 60 | 1 | test,test 2,test 3 |
+---------+------+----------+----------+--------------------+
What is an efficient way to get all this data in one query?
Sample data:
phppos_items:
+---------+------+
| item_id | name |
+---------+------+
| 1 | TEST |
+---------+------+
phppos_item_variations:
+---------+----+
| item_id | id |
+---------+----+
| 1 | 1 |
+---------+----+
phppos_location_item_variations:
+-------------------+----------+
| item_variation_id | quantity |
+-------------------+----------+
| 1 | 10 |
+-------------------+----------+
phppos_tags:
+--------+----+
| name | id |
+--------+----+
| test | 1 |
| test 2 | 2 |
| test 3 | 3 |
+--------+----+
phppos_items_tags:
+---------+--------+
| item_id | tag_id |
+---------+--------+
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
+---------+--------+
phppos_item_images:
+---------+----------+
| item_id | image_id |
+---------+----------+
| 1 | 1 |
| 1 | 4 |
+---------+----------+
Tables involved
CREATE TABLE `phppos_items` (
`name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`item_id` int(10) NOT NULL AUTO_INCREMENT,
`deleted` int(1) NOT NULL DEFAULT '0',
`system_item` int(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`item_id`),
UNIQUE KEY `item_number` (`item_number`),
KEY `deleted` (`deleted`),
KEY `deleted_system_item` (`deleted`,`system_item`),
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci |
CREATE TABLE `phppos_item_variations` (
`id` int(10) NOT NULL AUTO_INCREMENT,
`item_id` int(10) NOT NULL,
PRIMARY KEY (`id`),
KEY `phppos_item_variations_ibfk_1` (`item_id`),
CONSTRAINT `phppos_item_variations_ibfk_1` FOREIGN KEY (`item_id`) REFERENCES `phppos_items` (`item_id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci |
CREATE TABLE `phppos_location_item_variations` (
`item_variation_id` int(10) NOT NULL,
`location_id` int(10) NOT NULL,
`quantity` int(1) DEFAULT NULL,
`reorder_level` decimal(23,10) DEFAULT NULL,
`replenish_level` decimal(23,10) DEFAULT NULL,
PRIMARY KEY (`item_variation_id`,`location_id`),
KEY `phppos_item_attribute_location_values_ibfk_2` (`location_id`),
CONSTRAINT `phppos_item_attribute_location_values_ibfk_1` FOREIGN KEY (`item_variation_id`) REFERENCES `phppos_item_variations` (`id`),
CONSTRAINT `phppos_item_attribute_location_values_ibfk_2` FOREIGN KEY (`location_id`) REFERENCES `phppos_locations` (`location_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci |
CREATE TABLE `phppos_items_tags` (
`item_id` int(11) NOT NULL,
`tag_id` int(11) NOT NULL,
PRIMARY KEY (`item_id`,`tag_id`),
KEY `phppos_items_tags_ibfk_2` (`tag_id`),
CONSTRAINT `phppos_items_tags_ibfk_1` FOREIGN KEY (`item_id`) REFERENCES `phppos_items` (`item_id`),
CONSTRAINT `phppos_items_tags_ibfk_2` FOREIGN KEY (`tag_id`) REFERENCES `phppos_tags` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci |
CREATE TABLE `phppos_tags` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`ecommerce_tag_id` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`last_modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`deleted` int(1) NOT NULL DEFAULT '0',
`name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `tag_name` (`name`),
KEY `deleted` (`deleted`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci |
CREATE TABLE `phppos_item_images` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
`alt_text` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
`item_id` int(11) DEFAULT NULL,
`item_variation_id` int(10) DEFAULT NULL,
`ecommerce_image_id` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`image_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `phppos_item_images_ibfk_1` (`item_id`),
KEY `phppos_item_images_ibfk_2` (`image_id`),
KEY `phppos_item_images_ibfk_3` (`item_variation_id`),
CONSTRAINT `phppos_item_images_ibfk_1` FOREIGN KEY (`item_id`) REFERENCES `phppos_items` (`item_id`),
CONSTRAINT `phppos_item_images_ibfk_2` FOREIGN KEY (`image_id`) REFERENCES `phppos_app_files` (`file_id`),
CONSTRAINT `phppos_item_images_ibfk_3` FOREIGN KEY (`item_variation_id`) REFERENCES `phppos_item_variations` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci |
You said your first query already works so now you need get the remaining information
SQL DEMO
SELECT Q1.*, Q2.tags, Q3.image_id
FROM ( SELECT phppos_items.item_id,
phppos_items.name,
SUM(phppos_location_item_variations.quantity) as quantity
FROM `phppos_items`
LEFT JOIN `phppos_item_variations`
ON `phppos_item_variations`.`item_id` = `phppos_items`.`item_id`
LEFT JOIN `phppos_location_item_variations`
ON `phppos_location_item_variations`.`item_variation_id` = `phppos_item_variations`.`id`
and `phppos_location_item_variations`.`location_id` = 1
GROUP BY `phppos_items`.`item_id` ) as Q1
LEFT JOIN ( SELECT i.item_id,
GROUP_CONCAT(DISTINCT t.name) as tags
FROM phppos_items i
LEFT JOIN phppos_items_tags it
ON i.item_id = it.item_id
LEFT JOIN phppos_tags t
ON it.tag_id = t.id
GROUP BY i.item_id
) as Q2
ON Q1.item_id = Q2.item_id
LEFT JOIN ( SELECT item_id, MIN(image_id) as image_id
FROM phppos_item_images
GROUP BY item_id
) as Q3
ON Q1.item_id = Q3.item_id
OUTPUT

Having difficulty in constructing SQL query for events

I have a problem where I'm trying to get counts of data associated with multiple events occurring on separate days.
Let's say I'm following a group of plane-spotters, each of whom may spot many planes from around the world on any one day, while based at some particular airport. I'd like to produce a list consisting of one row per spotter per day, with columns for the spotter's ID, the date, how many planes he (it's always "he", right?) spotted on that day, how many individual airlines the planes belonged to, and how many countries the airlines belonged to. So, I'd like to have results like this:
+-----------+------------+---------+----------+-----------+
| | | Planes | | Airline |
| SpotterID | Date | spotted | Airlines | Countries |
+-----------+------------+---------+----------+-----------+
| 1234 | 2017-04-15 | 28 | 11 | 4 |
+-----------+------------+---------+----------+-----------+
| 1234 | 2017-04-16 | 65 | 19 | 7 |
+-----------+------------+---------+----------+-----------+
| 5678 | 2017-04-22 | 39 | 14 | 6 |
+-----------+------------+---------+----------+-----------+
| 6677 | 2017-04-28 | 74 | 29 | 9 |
+-----------+------------+---------+----------+-----------+
So, (MySQL 5.7.17) my test tables are as sketched out below (I may have forgotten a couple of indexes).
The plane-spotting events table is defined as:
CREATE TABLE `SpottingEvents` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`SpotterID` int(11) NOT NULL,
`SpotDateTime` datetime NOT NULL,
`PlaneID` int(11) NOT NULL,
`Notes` varchar(100) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `indx_SpotterID_PlaneID_DateTime` (`SpotterID`,`PlaneID`,`SpotDateTime`),
KEY `indx_SpotterID_DateTime` (`SpotterID`,`SpotDateTime`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8
the planes table is defined as:
CREATE TABLE `Planes` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`PlaneID` int(11) NOT NULL,
`PlaneTypeID` int(11) NOT NULL,
`AirlineID` int(11) NOT NULL,
`Notes` varchar(100) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `indx_PlaneID_AirlineID` (`PlaneID`,`AirlineID`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8
the airlines table is defined as:
CREATE TABLE `Airlines` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`AirlineID` int(11) NOT NULL,
`AirlineName` varchar(100) NOT NULL,
`CountryID` int(11) NOT NULL,
`Notes` varchar(100) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `indx_AirlineID` (`AirlineID`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8
and the countries table is defined as:
CREATE TABLE `Countries` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`CountryID` int(11) NOT NULL,
`CountryName` varchar(100) NOT NULL,
`Notes` varchar(100) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `indx_CountryID` (`CountryID`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8
My problem is with the last two columns, the Airlines and Countries counts. I've tried several ways to do this, including the following:
select distinct sev.SpotDateTime, sev.SpotterID, count(*) as planes_count,
(select count(*) from ( select distinct cnt.CountryID
from Countries as cnt
inner join Airlines as aln on al.CountryID = cnt.CountryID
inner join Planes as pl on pl.AirlineID = aln.AirlineID
where pl.PlaneID = sev.PlaneID ) as t1) as countries_count
from SpottingEvents as sev
# where sev.UserID = 1234
group by SpotDateTime
order by SpotDateTime
which leads to an error Unknown column 'sev.planeID' in 'where clause'
but since I'm no expert, I'm just not getting the intended results. So, how do I achieve the desired results?
You are looking for COUNT(DISTINCT). Join the tables needed, then count.
select
se.spotterid,
date(se.spotdatetime) as spot_date,
count(*) as planes,
count(distinct p.airlineid) as airlines,
count(distinct a.countryid) as countries
from spottingevents se
join planes p on p.planeid = se.planeid
join airlines a on a.airlineid = p.airlineid
group by se.spotterid, date(se.spotdatetime)
order by date(se.spotdatetime), se.spotterid;

How to optimize MySQL query with large data on left join?

The query below returns a set of User and for each row a number of relations from the user perpective who is searching (id = 4)
SELECT `users`.`firstname` AS firstname,
`users`.`lastname` AS lastname,
COUNT(`trusted_users`.`id`) AS number_of_friend_in_common,
CASE ... AS friend,
CASE ... AS facebook_invitable,
CASE ... AS address_book_invitable,
CASE ... AS virtual_user,
FROM `users`
LEFT OUTER JOIN `trusted_users`
ON `trusted_users`.`user_id` = 4 AND `trusted_users`.`trust_user_id` = `users`.`id`
LEFT OUTER JOIN `facebook_friends`
ON (`facebook_friends`.`user_id` = 4 AND `facebook_friends`.`friend_user_id` = `users`.`id`
OR `facebook_friends`.`user_id` = `users`.`id` AND `facebook_friends`.`friend_user_id` = 4)
LEFT OUTER JOIN `address_book_contacts`
ON `address_book_contacts`.`owner_id` = 4 AND `address_book_contacts`.`email_digest` = `users`.`email_digest`
LEFT OUTER JOIN `friends`
ON (`friends`.`me_id` = `users`.`id` AND `friends`.`him_id` = 4
OR `friends`.`me_id` = 4 AND `friends`.`him_id` = `users`.`id`)
WHERE `users`.`id` NOT IN
(SELECT CASE
WHEN `friends`.`me_id` = 4 THEN `friends`.`him_id`
ELSE `friends`.`me_id`
END
FROM `friends`
WHERE (`friends`.`status` = 0
AND `friends`.`him_id` = 4
AND `friends`.`him_status` = 7
OR `friends`.`status` = 0
AND `friends`.`me_id` = 4
AND `friends`.`me_status` = 7))
AND (`users`.`firstname` LIKE '%a%' OR `users`.`lastname` LIKE '%a%')
GROUP BY `users`.`id`
ORDER BY friend DESC,
facebook_invitable DESC,
address_book_invitable DESC,
number_of_friend_in_common DESC,
virtual_user DESC,
firstname,
lastname LIMIT 0, 20
Number of row for each table:
trusted_users: 255k
facebook_friends: 1k
address_book_contacts: 1.5M
friends: 70k
users: 32k
All fields of join are indexed. The query takes 1.1s which is not acceptable for the amount of data we have.
What am I doing wrong? Should I split in multiple query and paginate my self?
Edit 1: EXPLAIN result
+----+-------------+-----------------------+-------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------------+---------+----------------------------------+-------+----------------------------------------------------------------------------------------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-----------------------+-------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------------+---------+----------------------------------+-------+----------------------------------------------------------------------------------------------------------------------------+
| 1 | PRIMARY | users | ALL | PRIMARY,index_users_on_chat_id,index_users_login_facebook_id,index_users_on_login,index_users_on_parent_id,index_users_account_type,index_users_email_digest,index_users_id | NULL | NULL | NULL | 31847 | Using where; Using temporary; Using filesort |
| 1 | PRIMARY | trusted_users | ref | index_trusted_users_user,index_trusted_users_trust_user | index_trusted_users_trust_user | 5 | messenger_dev.users.id | 6 | Using where |
| 1 | PRIMARY | facebook_friends | index_merge | index_facebook_friends_user,index_facebook_friends_friend | index_facebook_friends_user,index_facebook_friends_friend | 5,5 | NULL | 2 | Using union(index_facebook_friends_user,index_facebook_friends_friend); Using where; Using join buffer (Block Nested Loop) |
| 1 | PRIMARY | address_book_contacts | ref | index_address_book_contacts_owner_id,index_address_book_contacts_email | index_address_book_contacts_email | 767 | messenger_dev.users.email_digest | 1 | Using where |
| 1 | PRIMARY | friends | index_merge | index_friends_me_him,index_friends_me,index_friends_him | index_friends_him,index_friends_me | 5,5 | NULL | 18 | Using union(index_friends_him,index_friends_me); Using where; Using join buffer (Block Nested Loop) |
| 2 | SUBQUERY | friends | index_merge | index_friends_me_him,index_friends_me,index_friends_him | index_friends_him,index_friends_me | 5,5 | NULL | 18 | Using union(index_friends_him,index_friends_me); Using where |
+----+-------------+-----------------------+-------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------------+---------+----------------------------------+-------+----------------------------------------------------------------------------------------------------------------------------+
6 rows in set (0,00 sec)
Edit 2: Table structure
CREATE TABLE `address_book_contacts` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`email_digest` varchar(191) DEFAULT NULL,
`code` varchar(191) DEFAULT NULL,
`created_at` datetime NOT NULL,
`updated_at` datetime NOT NULL,
`owner_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `index_address_book_contacts_owner_id` (`owner_id`),
KEY `index_address_book_contacts_email` (`email_digest`)
) ENGINE=InnoDB AUTO_INCREMENT=1598109 DEFAULT CHARSET=utf8mb4;
CREATE TABLE `trusted_users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`friend_id` int(11) DEFAULT NULL,
`user_id` int(11) DEFAULT NULL,
`trust_user_id` int(11) DEFAULT NULL,
`created_at` datetime NOT NULL,
`updated_at` datetime NOT NULL,
PRIMARY KEY (`id`),
KEY `index_trusted_users_on_friend_id` (`friend_id`),
KEY `index_trusted_users_user` (`user_id`),
KEY `index_trusted_users_trust_user` (`trust_user_id`),
CONSTRAINT `fk_rails_007c31c802` FOREIGN KEY (`trust_user_id`) REFERENCES `users` (`id`),
CONSTRAINT `fk_rails_ca24cb4e23` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=275576 DEFAULT CHARSET=utf8mb4;
CREATE TABLE `facebook_friends` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) DEFAULT NULL,
`friend_user_id` int(11) DEFAULT NULL,
`created_at` datetime NOT NULL,
`updated_at` datetime NOT NULL,
`code` varchar(191) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `index_facebook_friends_user` (`user_id`),
KEY `index_facebook_friends_friend` (`friend_user_id`),
KEY `index_facebook_friends_code` (`code`(5)),
CONSTRAINT `fk_rails_78285a074e` FOREIGN KEY (`friend_user_id`) REFERENCES `users` (`id`),
CONSTRAINT `fk_rails_aa3ac53a81` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1149 DEFAULT CHARSET=utf8mb4;
CREATE TABLE `friends` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`me_id` int(11) DEFAULT NULL,
`him_id` int(11) DEFAULT NULL,
`owner_id` int(11) DEFAULT NULL,
`status` int(11) DEFAULT NULL,
`created_at` datetime NOT NULL,
`updated_at` datetime NOT NULL,
`me_status` int(11) DEFAULT '0',
`him_status` int(11) DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE KEY `index_friends_me_him` (`me_id`,`him_id`),
KEY `index_friends_me` (`me_id`),
KEY `index_friends_him` (`him_id`),
KEY `index_friends_owner` (`owner_id`),
CONSTRAINT `fk_rails_9fa3474d31` FOREIGN KEY (`owner_id`) REFERENCES `users` (`id`),
CONSTRAINT `fk_rails_d3ebb6657f` FOREIGN KEY (`him_id`) REFERENCES `users` (`id`),
CONSTRAINT `fk_rails_fccfd1b821` FOREIGN KEY (`me_id`) REFERENCES `users` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=95724 DEFAULT CHARSET=utf8mb4;
CREATE TABLE `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`account_type` varchar(191) NOT NULL,
`firstname` varchar(191) DEFAULT NULL,
`lastname` varchar(191) DEFAULT NULL,
`login` varchar(191) DEFAULT NULL,
`avatar` varchar(191) DEFAULT NULL,
`gender` varchar(1) DEFAULT NULL,
`locale` varchar(191) DEFAULT NULL,
`birthdate` date DEFAULT NULL,
`password_digest` varchar(191) DEFAULT NULL,
`email_digest` varchar(191) DEFAULT NULL,
`created_at` datetime NOT NULL,
`updated_at` datetime NOT NULL,
PRIMARY KEY (`id`),
KEY `index_users_on_login` (`login`),
KEY `index_users_account_type` (`account_type`),
KEY `index_users_email_digest` (`email_digest`),
KEY `index_uses_firstname` (`firstname`),
KEY `index_users_lastname` (`lastname`)
) ENGINE=InnoDB AUTO_INCREMENT=32516 DEFAULT CHARSET=utf8mb4;
It looks like MySQL is not choosing any of the available indicies for the users table.
First, run ANALYZE TABLE users;, then re-run the EXPLAIN command. Is the value of the first rows cell now substantially lower than 31847? If so, your problem should be solved!
If not, run OPTIMIZE TABLE users;, then re-run the EXPLAIN command. Is the value of the first rows cell now substantially lower than 31847? If so, your problem should be solved!
If neither of those steps help, try adding USE INDEX (PRIMARY) or USE INDEX (users_id) immediately after the FROM users portion of your query.
Hope this helps!