How to optimise that query? - mysql

I've a vote system which is designed like this:
CREATE TABLE `vote` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`weight` int(11) NOT NULL,
`submited_date` datetime NOT NULL,
`resource_type` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2963832 DEFAULT CHARSET=latin1;
CREATE TABLE `article_preselection_vote` (
`id` int(11) NOT NULL,
`article_id` int(11) DEFAULT NULL,
`user_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `IDX_9B145DEA62922701` (`article_id`),
KEY `IDX_9B145DEAA76ED395` (`user_id`),
CONSTRAINT `article_preselection_vote_ibfk_4` FOREIGN KEY (`article_id`) REFERENCES `article` (`id`),
CONSTRAINT `article_preselection_vote_ibfk_5` FOREIGN KEY (`id`) REFERENCES `vote` (`id`) ON DELETE CASCADE,
CONSTRAINT `article_preselection_vote_ibfk_6` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
v.weight can be +1 or -1, I need, given a bunch of articles ID, to get the sum of each positive vote (+1) and the sum of negative vote (-1) per articles id.
Then my result should be
article_id | vote_up | vote_down
-----------|---------|----------
1 | 36 | 20
-----------|---------|----------
68 | 12 | 56
-----------|---------|----------
25 | 90 | 12
-----------|---------|----------
I can get that result by doing the following request, but it's quite heavy and slow on 2,000,000 votes.
SELECT apv.article_id, COALESCE(SUM(up),0) as up, COALESCE(SUM(down),0) as down
FROM article_preselection_vote apv
LEFT JOIN(
SELECT id, weight up FROM vote WHERE weight > 0 AND vote.resource_type = 'article') v1 ON apv.id = v1.id
LEFT JOIN(
SELECT id, weight down FROM vote WHERE weight < 0 AND vote.resource_type = 'article') v2 ON apv.id = v2.id
WHERE apv.article_id IN (11702,11703,11704,11632,11652,11658)
GROUP BY apv.article_id
Any ideas?
Thanks in advance.

Subselects, IN (...) and GROUP BY in one query are killers.
You should redesign to have a more traditional solution:
Have a table with the votes article_id, votes_up, votes_down, vote_date, ...
Update (cron) the summary fields in your article table votes_up, votes_down, ... with one UPDATE.
That way, you can better handle the row/table locks and have fast queries

You can try a single join:
SELECT
apv.article_id,
SUM(COALESCE(weight, 0) > 0) AS up,
SUM(COALESCE(weight, 0) < 0) AS down
FROM article_preselection_vote apv
LEFT JOIN vote
ON apv.id = vote.id
AND vote.resource_type = 'article'
WHERE apv.article_id IN (11702, 11703, 11704, 11632, 11652, 11658)
GROUP BY apv.article_id
If you need to calculate this often it might be worthwhile to denormalize your database and store a cached copy of the results.

Instead of weighting the votes, why don't you just create two tables, one for up votes and one for down votes? The only thing it will complicate is vote combination, which will still be a simple sum of the counts of two different queries.

in a nut shell do something like this:
select * from article where article_id in (1,2,3);
+------------+-----------+---------------+-----------------+
| article_id | title | up_vote_count | down_vote_count |
+------------+-----------+---------------+-----------------+
| 1 | article 1 | 2 | 3 |
| 2 | article 2 | 2 | 1 |
| 3 | article 3 | 1 | 1 |
+------------+-----------+---------------+-----------------+
3 rows in set (0.00 sec)
drop table if exists article;
create table article
(
article_id int unsigned not null auto_increment primary key,
title varchar(255) not null,
up_vote_count int unsigned not null default 0,
down_vote_count int unsigned not null default 0
)
engine = innodb;
drop table if exists article_vote;
create table article_vote
(
article_id int unsigned not null,
user_id int unsigned not null,
score tinyint not null default 0,
primary key (article_id, user_id)
)
engine=innodb;
delimiter #
create trigger article_vote_after_ins_trig after insert on article_vote
for each row
begin
if new.score < 0 then
update article set down_vote_count = down_vote_count + 1 where article_id = new.article_id;
else
update article set up_vote_count = up_vote_count + 1 where article_id = new.article_id;
end if;
end#
delimiter ;
insert into article (title) values ('article 1'),('article 2'), ('article 3');
insert into article_vote (article_id, user_id, score) values
(1,1,-1),(1,2,-1),(1,3,-1),(1,4,1),(1,5,1),
(2,1,1),(2,2,1),(2,3,-1),
(3,1,1),(3,5,-1);
select * from article where article_id in (1,2,3);

Related

MySQL Search for duplicate of a one to many relationship

I need to find the most efficient way in MySQL to compare 2 different instances of a one to many relationship. Take this table
CREATE TABLE `Table` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`ParentID` int(11) NOT NULL,
`ChildID` int(11) NOT NULL,
PRIMARY KEY (`ID`),
UNIQUE KEY `pach` (`ParentID`,`ChildID`),
KEY `ParentID` (`ParentID`),
KEY `ChildID` (`ChildID`)
) ENGINE=InnoDB AUTO_INCREMENT=1;
insert into `Table` (`ID`,`ParentID`,`ChildID`) values
(1,1,1),
(2,1,2),
(3,1,3),
(4,1,4),
(5,2,1),
(6,2,3),
(7,3,1),
(8,3,3),
(9,3,4),
(10,4,1),
(11,4,4),
(12,4,3);
ParentID 3 has an identical set of children to ParentID 4, and that's what i need my query to be able to identify - Given ParentID=4, return ParentID 3 because it has exaclty the same children.
So far the only thing i can come up with is a very ugly group_concat query (see below). What would be a better approach to solve this problem?
select distinct(b.ParentID)
from `Table` a, `Table` b where
(select group_concat(ChildID order by ParentID asc) from `Table` where ParentID=a.ParentID )
=
(select group_concat(ChildID order by ParentID asc) from `Table` where ParentID=b.ParentID )
and b.ParentID!=a.ParentID
and a.parentID=4;
+----------+
| ParentID |
+----------+
| 3 |
+----------+

MYSQL full join 4 tables using a reference table

I need some help building a SQL to fetch something like a "FULL OUTER JOIN" over four tables. I have this structure and cannot really modify much on it, cause its a already in use database:
-- ----------------------------
-- Table structure for article
-- ----------------------------
CREATE TABLE `article` (
`ID` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`Name` varchar(255) NULL DEFAULT NULL,
PRIMARY KEY (`ID`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3;
INSERT INTO `article` VALUES (1, 'Coffeemaker');
INSERT INTO `article` VALUES (2, 'Toaster');
-- ----------------------------
-- Table structure for language
-- ----------------------------
CREATE TABLE `language` (
`ID` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`Name` varchar(255) NULL DEFAULT NULL,
PRIMARY KEY (`ID`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3;
INSERT INTO `language` VALUES (1, 'German');
INSERT INTO `language` VALUES (2, 'English');
-- ----------------------------
-- Table structure for property
-- ----------------------------
CREATE TABLE `property` (
`ID` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`Name` varchar(255) NULL DEFAULT NULL,
PRIMARY KEY (`ID`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3;
INSERT INTO `property` VALUES (1, 'DescriptionText');
INSERT INTO `property` VALUES (2, 'EAN-Code');
-- ----------------------------
-- Table structure for data
-- ----------------------------
CREATE TABLE `data` (
`ArticleID` int(10) UNSIGNED NOT NULL,
`PropertyID` int(10) UNSIGNED NOT NULL,
`LanguageID` int(10) UNSIGNED NOT NULL,
`Value` varchar(255) NULL DEFAULT NULL,
PRIMARY KEY (`ArticleID`, `PropertyID`, `LanguageID`) USING BTREE,
CONSTRAINT `FK_ArticleID` FOREIGN KEY (`ArticleID`) REFERENCES `article` (`ID`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `FK_LanguageID` FOREIGN KEY (`LanguageID`) REFERENCES `language` (`ID`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `FK_PropertyID` FOREIGN KEY (`PropertyID`) REFERENCES `property` (`ID`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE = InnoDB;
INSERT INTO `data` VALUES (1, 1, 1, 'Eine Kaffemaschine');
INSERT INTO `data` VALUES (2, 1, 2, 'A toaster');
SQL: http://sqlfiddle.com/#!9/91dc8/1
What i want to get is a new VIEW which contains a join over all entity-tables showing a row for all articles, all properties and all languages but using the already existing data if available or null if not.
Is it possible? How would the SQL look like?
You seem to be after this...
SELECT a.id articleid
, p.id propertyid
, l.id languageid
, d.value
FROM article a
CROSS -- optional keyword
JOIN property p
CROSS -- optional keyword
JOIN language l
LEFT -- not optional
JOIN data d
ON d.articleid = a.id
AND d.propertyid = p.id
AND d.languageid = l.id;
+-----------+------------+------------+--------------------+
| articleid | propertyid | languageid | value |
+-----------+------------+------------+--------------------+
| 1 | 1 | 1 | Eine Kaffemaschine |
| 2 | 1 | 1 | NULL |
| 1 | 2 | 1 | NULL |
| 2 | 2 | 1 | NULL |
| 1 | 1 | 2 | NULL |
| 2 | 1 | 2 | A toaster |
| 1 | 2 | 2 | NULL |
| 2 | 2 | 2 | NULL |
+-----------+------------+------------+--------------------+
If you want all articles, you don't want a "full join". You want a left join that starts with the articles table. Further, you don't even need that, because all your articles have values in data.
But the question does specify all articles, so:
SELECT a.*, p.*, l.*, d.*
FROM article a LEFT JOIN
data d
ON d.ArticleID = a.ID LEFT JOIN
property p
ON d.PropertyID = p.id LEFT JOIN
language l
ON d.LanguageID = l.id;
I'm not sure what you mean by all articles and all languages (I missed the second part when I first read the question). If you want all combinations then:
SELECT a.*, p.*, l.*, d.*
FROM article a CROSS JOIN
languages l LEFT JOIN
data d
ON d.ArticleID = a.ID AND
d.LanguageID = l.ID LEFT JOIN
property p
ON d.PropertyID = p.id ;

How to use helper functions to create a stored procedure that updates two fields mySQL

So I have a few tables on mySQL:
CREATE TABLE IF NOT EXISTS `salarygrade` (
`GRADE` INT(11) NOT NULL,
`HOURLYRATE` FLOAT NOT NULL,
PRIMARY KEY (`GRADE`));
===========================================================================
CREATE TABLE IF NOT EXISTS `staffongrade` (
`STAFFNO` INT(11) NOT NULL,
`GRADE` INT(11) NOT NULL,
`STARTDATE` DATE NULL DEFAULT NULL,
`FINISHDATE` DATE NULL DEFAULT NULL,
INDEX `STAFFONGRADE_FK` (`STAFFNO` ASC),
INDEX `STAFFONGRADE2_FK` (`GRADE` ASC),
PRIMARY KEY (`GRADE`, `STAFFNO`),
CONSTRAINT `FK_STAFFONG_STAFFONGR_SALARYGR`
FOREIGN KEY (`GRADE`)
REFERENCES `salarygrade` (`GRADE`),
CONSTRAINT `FK_STAFFONG_STAFFONGR_STAFF`
FOREIGN KEY (`STAFFNO`)
REFERENCES `staff` (`STAFFNO`));
===========================================================================
CREATE TABLE IF NOT EXISTS `campaign` (
`CAMPAIGN_NO` INT(11) NOT NULL,
`TITLE` VARCHAR(30) NOT NULL,
`CUSTOMER_ID` INT(11) NOT NULL,
`THEME` VARCHAR(40) NULL DEFAULT NULL,
`CAMPAIGNSTARTDATE` DATE NULL DEFAULT NULL,
`CAMPAIGNFINISHDATE` DATE NULL DEFAULT NULL,
`ESTIMATEDCOST` INT(11) NULL DEFAULT NULL,
`ACTUALCOST` FLOAT NULL DEFAULT NULL,
PRIMARY KEY (`CAMPAIGN_NO`),
INDEX `OWNS_FK` (`CUSTOMER_ID` ASC),
CONSTRAINT `FK_CAMPAIGN_OWNS_CUSTOMER`
FOREIGN KEY (`CUSTOMER_ID`)
REFERENCES `customer` (`CUSTOMER_ID`)
ON DELETE RESTRICT
ON UPDATE RESTRICT);
===========================================================================
CREATE TABLE IF NOT EXISTS `workson` (
`STAFFNO` INT(11) NOT NULL,
`CAMPAIGN_NO` INT(11) NOT NULL,
`WDATE` DATE NOT NULL,
`HOUR` FLOAT NULL DEFAULT NULL,
PRIMARY KEY (`STAFFNO`, `CAMPAIGN_NO`, `WDATE`),
INDEX `WORKSON_FK` (`STAFFNO` ASC),
INDEX `FK_WORKSON_WORKSON2_CAMPAIGN_idx` (`CAMPAIGN_NO` ASC),
CONSTRAINT `FK_WORKSON_WORKSON2_CAMPAIGN`
FOREIGN KEY (`CAMPAIGN_NO`)
REFERENCES `campaign` (`CAMPAIGN_NO`)
ON DELETE RESTRICT
ON UPDATE RESTRICT,
CONSTRAINT `FK_WORKSON_WORKSON_STAFF`
FOREIGN KEY (`STAFFNO`)
REFERENCES `staff` (`STAFFNO`));
And I want to create a stored procedure called sp_finish_campaign (in c_title varchar(30)) that takes a title of a campaign and finishes the campaign by updating the CAMPAIGNFINISHDATE to the current date and ACTUALCOST to the cost of the campaign, which is calculated from the number of hours different staff put into it on different dates, and the salary grade (this changes based on staffID and the timeframe based on the STARTDATE and FINISHDATE of the staffongrade table.
To calculate the ACTUALCOST, I created a helper function:
DELIMITER //
CREATE FUNCTION rate_on_date(staff_id int, given_date date)
RETURNS int
DETERMINISTIC
BEGIN
DECLARE salaryGrade int;
SET salaryGrade = (select grade from staffongrade
where staffno = staff_id AND (given_date BETWEEN STARTDATE AND FINISHDATE));
RETURN salaryGrade;
END //
DELIMITER ;
Which returns the pay grade based on the staff_id and given_date parameters I give it:
select rate_on_date(1, "2018-02-02") as Grade_On_Date;
For the parameters of this I assume I would have to get it from the workson table which looks like this:
I have tried using a select statement to get the paygrade:
select hourlyrate as 'grade' from salarygrade
where rate_on_date(1, "2018-02-02") = grade;
To calculate ACTUALCOST I assume I would have to do a calculation by multiplying HOUR column with the grade costs, and use the WDATE and STAFFNO columns in the workson table as parameters for my stored procedure that will calculate and update the CAMPAIGNFINISHDATE and ACTUALCOST of the campaign by inputting the campaign title into it.
But how would I go about doing this?
I'm just confused as to how to go about creating this procedure, and also confused about how to properly use these helper functions in my stored procedure.
I feel like this question is quite long but I don't really know what to ask or what direction I should take to solve this problem.
You don't really need a function. mysql can do multi-table updates (see https://dev.mysql.com/doc/refman/8.0/en/update.html) in your case it could look like this
update campaign c
join
(select c.campaign_no,
sum(hour * hourlyrate) cost
from campaign c
join workson w on w.campaign_no = c.campaign_no
join staffongrade s on s .staffno = w.staffno and w.wdate between s.startdate and s.finishdate
join salarygrade g on g.grade = s.grade
group by c.campaign_no
) s
on s.campaign_no = c.campaign_no
set actualcost = s.cost
where c.campaign_no = 1
;
Where the sub query does the needful
if you simplify your data this should be easy to prove;
drop table if exists salarygrade,campaign,workson,staffongrade;
CREATE TABLE `salarygrade`
( GRADE INT NOT NULL,
hOURLYRATE decimal(10,2) NOT NULL
);
insert into salarygrade values(1,10),(2,20);
cREATE TABLE IF NOT EXISTS `staffongrade` (
`STAFFNO` INT(11) NOT NULL,
`GRADE` INT(11) NOT NULL,
`STARTDATE` DATE NULL DEFAULT NULL,
`FINISHDATE` DATE NULL DEFAULT NULL
);
insert into staffongrade values
(1,1,'2019-01-01','2019-06-30'),(1,2,'2019-06-01','2019-12-31'),(2,1,'2019-01-01','2019-01-31');
CREATE TABLE IF NOT EXISTS `campaign` (
`CAMPAIGN_NO` INT(11) NOT NULL,
`CAMPAIGNSTARTDATE` DATE NULL DEFAULT NULL,
`CAMPAIGNFINISHDATE` DATE NULL DEFAULT NULL,
`ESTIMATEDCOST` INT(11) NULL DEFAULT NULL,
`ACTUALCOST` FLOAT NULL DEFAULT NULL
);
insert into campaign values (1,'2019-01-01','2019-12-31',null,null);
CREATE TABLE IF NOT EXISTS `workson` (
`STAFFNO` INT(11) NOT NULL,
`CAMPAIGN_NO` INT(11) NOT NULL,
`WDATE` DATE NOT NULL,
`HOUR` FLOAT NULL DEFAULT NULL
);
insert into workson values
(1,1,'2019-01-01',1),(1,1,'2019-12-01',1),(2,1,'2019-01-01',1);
select * from campaign;
+-------------+-------------------+--------------------+---------------+------------+
| CAMPAIGN_NO | CAMPAIGNSTARTDATE | CAMPAIGNFINISHDATE | ESTIMATEDCOST | ACTUALCOST |
+-------------+-------------------+--------------------+---------------+------------+
| 1 | 2019-01-01 | 2019-12-31 | NULL | 40 |
+-------------+-------------------+--------------------+---------------+------------+
1 row in set (0.00 sec)
Got to dash so I'll leave you to drop the update into a procedure.
IF staffongrade has NULL for finishdate then a bit of data cleansing is required. for simplicity I would create a temporary table to fill in gaps and change the update statement to use the projectfinishdate (if that's not known then substitute a suitable future date). This code would be inserted in your procedure prior to the update
so
insert into staffongrade values
(1,1,'2019-01-01',null),(1,2,'2019-07-01',null),(2,1,'2019-01-01',null);
drop temporary table if exists staffongradetemp;
create temporary table staffongradetemp like staffongrade;
insert into staffongradetemp
select s.STAFFNO,s.GRADE,s.STARTDATE,
case when s.FINISHDATE is not null then s.finishdate
else date_sub((select s1.startdate
from staffongrade s1
where s1.STAFFNO = s.STAFFNO and s1.startdate > s.STARTDATE
order by startdate limit 1), interval 1 day)
end
from staffongrade s
;
select * from staffongradetemp;
+---------+-------+------------+------------+
| STAFFNO | GRADE | STARTDATE | FINISHDATE |
+---------+-------+------------+------------+
| 1 | 1 | 2019-01-01 | 2019-06-30 |
| 1 | 2 | 2019-07-01 | NULL |
| 2 | 1 | 2019-01-01 | NULL |
+---------+-------+------------+------------+
3 rows in set (0.00 sec)
Which leaves all the last finshdates as null which we can trap in the update statement using coalesce
update campaign c
join
(select c.campaign_no,
sum(hour * hourlyrate) cost
from campaign c
join workson w on w.campaign_no = c.campaign_no
join **staffongradetemp s** on s .staffno = w.staffno and w.wdate between s.startdate and **coalesce(s.finishdate,c.CAMPAIGNFINISHDATE)**
join salarygrade g on g.grade = s.grade
where c.campaign_no = 1
group by c.campaign_no
) s
on s.campaign_no = c.campaign_no
set actualcost = s.cost
where 1 = 1;

MySql group by optimization - avoid tmp table and/or filesort

I have a slow query, without the group by is fast (0.1-0.3 seconds), but with the (required) group by the duration is around 10-15s.
The query joins two tables, events (near 50 million rows) and events_locations (5 million rows).
Query:
SELECT `e`.`id` AS `event_id`,`e`.`time_stamp` AS `time_stamp`,`el`.`latitude` AS `latitude`,`el`.`longitude` AS `longitude`,
`el`.`time_span` AS `extra`,`e`.`entity_id` AS `asset_name`, `el`.`other_id` AS `geozone_id`,
`el`.`group_alias` AS `group_alias`,`e`.`event_type_id` AS `event_type_id`,
`e`.`entity_type_id`AS `entity_type_id`, el.some_id
FROM events e
INNER JOIN events_locations el ON el.event_id = e.id
WHERE 1=1
AND el.other_id = '1'
AND time_stamp >= '2018-01-01'
AND time_stamp <= '2019-06-02'
GROUP BY `e`.`event_type_id` , `el`.`some_id` , `el`.`group_alias`;
Table events:
CREATE TABLE `events` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`event_type_id` int(11) NOT NULL,
`entity_type_id` int(11) NOT NULL,
`entity_id` varchar(64) NOT NULL,
`alias` varchar(64) NOT NULL,
`time_stamp` datetime NOT NULL,
PRIMARY KEY (`id`),
KEY `entity_id` (`entity_id`),
KEY `event_type_idx` (`event_type_id`),
KEY `idx_events_time_stamp` (`time_stamp`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Table events_locations
CREATE TABLE `events_locations` (
`event_id` bigint(20) NOT NULL,
`latitude` double NOT NULL,
`longitude` double NOT NULL,
`some_id` bigint(20) DEFAULT NULL,
`other_id` bigint(20) DEFAULT NULL,
`time_span` bigint(20) DEFAULT NULL,
`group_alias` varchar(64) NOT NULL,
KEY `some_id_idx` (`some_id`),
KEY `idx_events_group_alias` (`group_alias`),
KEY `idx_event_id` (`event_id`),
CONSTRAINT `fk_event_id` FOREIGN KEY (`event_id`) REFERENCES `events` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
The explain:
+----+-------------+-------+--------+---------------------------------+---------+---------+-------------------------------------------+----------+------------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+---------------------------------+---------+---------+-------------------------------------------+----------+------------------------------------------------+
| 1 | SIMPLE | ea | ALL | 'idx_event_id' | NULL | NULL | NULL | 5152834 | 'Using where; Using temporary; Using filesort' |
| 1 | SIMPLE | e | eq_ref | 'PRIMARY,idx_events_time_stamp' | PRIMARY | '8' | 'name.ea.event_id' | 1 | |
+----+-------------+----------------+---------------------------------+---------+---------+-------------------------------------------+----------+------------------------------------------------+
2 rows in set (0.08 sec)
From the doc:
Temporary tables can be created under conditions such as these:
If there is an ORDER BY clause and a different GROUP BY clause, or if the ORDER BY or GROUP BY contains columns from tables other than the first table in the join queue, a temporary table is created.
DISTINCT combined with ORDER BY may require a temporary table.
If you use the SQL_SMALL_RESULT option, MySQL uses an in-memory temporary table, unless the query also contains elements (described later) that require on-disk storage.
I already tried:
Create an index by 'el.some_id , el.group_alias'
Decrease the varchar size to 20
Increase the size of sort_buffer_size and read_rnd_buffer_size;
Any suggestions for performance tuning would be much appreciated!
In your case events table has time_span as indexing property. So before joining both tables first select required records from events table for specific date range with required details. Then join the event_location by using table relation properties.
Check your MySql Explain keyword to check how does your approach your table records. It will tell you how much rows are scanned for before selecting required records.
Number of rows that are scanned also involve in query execution time. Use my below logic to reduce the number of rows that are scanned.
SELECT
`e`.`id` AS `event_id`,
`e`.`time_stamp` AS `time_stamp`,
`el`.`latitude` AS `latitude`,
`el`.`longitude` AS `longitude`,
`el`.`time_span` AS `extra`,
`e`.`entity_id` AS `asset_name`,
`el`.`other_id` AS `geozone_id`,
`el`.`group_alias` AS `group_alias`,
`e`.`event_type_id` AS `event_type_id`,
`e`.`entity_type_id` AS `entity_type_id`,
`el`.`some_id` as `some_id`
FROM
(select
`id` AS `event_id`,
`time_stamp` AS `time_stamp`,
`entity_id` AS `asset_name`,
`event_type_id` AS `event_type_id`,
`entity_type_id` AS `entity_type_id`
from
`events`
WHERE
time_stamp >= '2018-01-01'
AND time_stamp <= '2019-06-02'
) AS `e`
JOIN `events_locations` `el` ON `e`.`event_id` = `el`.`event_id`
WHERE
`el`.`other_id` = '1'
GROUP BY
`e`.`event_type_id` ,
`el`.`some_id` ,
`el`.`group_alias`;
The relationship between these tables is 1:1, so, I asked me why is a group by required and I found some duplicated rows, 200 in 50000 rows. So, somehow, my system is inserting duplicates and someone put that group by (years ago) instead of seek of the bug.
So, I will mark this as solved, more or less...

how to join tables with generalization

I'm having trouble performing a JOIN on tables the problem is this:
In a report cart system I have users, such as students, parents and school employees. I need to generate an SQL statement that when I enter the access ID of the parents it lists all students related to parents ID
Follow the Model:
Is this the best way to implement this "Generalization" and this relationship between parents and students, since they are all users? Can someone help me?
SQL code:
-- -----------------------------------------------------
-- Table `testeboletim`.`type_user`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `testeboletim`.`type_user` (
`idtype_user` INT NOT NULL AUTO_INCREMENT,
`role` VARCHAR(45) NULL,
PRIMARY KEY (`idtype_user`))
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `testeboletim`.`user`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `testeboletim`.`user` (
`iduser` INT NOT NULL AUTO_INCREMENT,
`name` VARCHAR(45) NULL,
`ID` VARCHAR(20) NULL,
`birth` DATE NULL,
`telephone` VARCHAR(20) NULL,
`phone` VARCHAR(20) NULL,
`email` VARCHAR(45) NULL,
`type_user_idtype_user` INT NOT NULL,
PRIMARY KEY (`iduser`, `type_user_idtype_user`),
INDEX `fk_usuario_tipo_usuario_idx` (`type_user_idtype_user` ASC),
CONSTRAINT `fk_usuario_tipo_usuario`
FOREIGN KEY (`type_user_idtype_user`)
REFERENCES `testeboletim`.`type_user` (`idtype_user`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `testeboletim`.`student`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `testeboletim`.`student` (
`idstudent` INT NOT NULL AUTO_INCREMENT,
`user_iduser` INT NOT NULL,
`user_type_user_idtype_user` INT NOT NULL,
PRIMARY KEY (`idstudent`, `user_iduser`, `user_type_user_idtype_user`),
INDEX `fk_aluno_usuario1_idx` (`user_iduser` ASC, `user_type_user_idtype_user` ASC),
CONSTRAINT `fk_aluno_usuario1`
FOREIGN KEY (`user_iduser` , `user_type_user_idtype_user`)
REFERENCES `testeboletim`.`user` (`iduser` , `type_user_idtype_user`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `testeboletim`.`parents`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `testeboletim`.`parents` (
`idparents` INT NOT NULL AUTO_INCREMENT,
`user_iduser` INT NOT NULL,
`user_type_user_idtype_user` INT NOT NULL,
PRIMARY KEY (`idparents`, `user_iduser`, `user_type_user_idtype_user`),
INDEX `fk_responsavel_usuario1_idx` (`user_iduser` ASC, `user_type_user_idtype_user` ASC),
CONSTRAINT `fk_responsavel_usuario1`
FOREIGN KEY (`user_iduser` , `user_type_user_idtype_user`)
REFERENCES `testeboletim`.`user` (`iduser` , `type_user_idtype_user`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `testeboletim`.`student_has_parents`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `testeboletim`.`student_has_parents` (
`student_idstudent` INT NOT NULL,
`student_user_iduser` INT NOT NULL,
`parents_idparents` INT NOT NULL,
`parents_user_iduser` INT NOT NULL,
PRIMARY KEY (`student_idstudent`, `student_user_iduser`, `parents_idparents`, `parents_user_iduser`),
INDEX `fk_aluno_has_responsavel_responsavel1_idx` (`parents_idparents` ASC, `parents_user_iduser` ASC),
INDEX `fk_aluno_has_responsavel_aluno1_idx` (`student_idstudent` ASC, `student_user_iduser` ASC),
CONSTRAINT `fk_aluno_has_responsavel_aluno1`
FOREIGN KEY (`student_idstudent` , `student_user_iduser`)
REFERENCES `testeboletim`.`student` (`idstudent` , `user_iduser`)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `fk_aluno_has_responsavel_responsavel1`
FOREIGN KEY (`parents_idparents` , `parents_user_iduser`)
REFERENCES `testeboletim`.`parents` (`idparents` , `user_iduser`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
SET SQL_MODE=#OLD_SQL_MODE;
SET FOREIGN_KEY_CHECKS=#OLD_FOREIGN_KEY_CHECKS;
SET UNIQUE_CHECKS=#OLD_UNIQUE_CHECKS;
In order to solve this problem, we would need to look at testeboletim.student, testeboletim.parents, and either testeboletim.student_has_parents or testeboletim.user. I have decided to solve your problem using testeboletim.user, because it was clearer in terms of refrence keys, and what not.
Solution using testeboletim.user
Based on your question, we are looking for all rows in testeboletim.student, that have a corresponding iduser testeboletim.user based on the user_iduser of testeboletim.parents.
-- SQL Definition:
SELECT * FROM `testeboletim`.`student` WHERE `user_iduser` IN
(SELECT DISTINCT(`iduser`) FROM `testeboletim`.`user` WHERE `iduser` IN
(SELECT DISTINCT(`user_iduser`) FROM `testeboletim`.`parents`)
);
Now to do the same thing with JOIN, would require the use of LEFT JOIN; in this case testeboletim.student.
SELECT * FROM `testeboletim`.`student` AS `student`
LEFT JOIN `testeboletim`.`user` AS `user`
ON `student`.`user_iduser` = `user`.`iduser`
LEFT JOIN `testeboletim`.`parents` AS `parents`
ON `user`.`iduser` = `parents`.`user_iduser`;
Since I don't have any values, I'm going to share with you the explanation, in order to "prove" that the query works.
mysql> SELECT * FROM `testeboletim`.`student` AS `student`
-> LEFT JOIN `testeboletim`.`user` AS `user`
-> ON `student`.`user_iduser` = `user`.`iduser`
-> LEFT JOIN `testeboletim`.`parents` AS `parents`
-> ON `user`.`iduser` = `parents`.`user_iduser`;
Empty set (0.01 sec)
mysql> EXPLAIN SELECT * FROM `testeboletim`.`student` AS `student`
-> LEFT JOIN `testeboletim`.`user` AS `user`
-> ON `student`.`user_iduser` = `user`.`iduser`
-> LEFT JOIN `testeboletim`.`parents` AS `parents`
-> ON `user`.`iduser` = `parents`.`user_iduser`;
+------+-------------+---------+-------+-----------------------------+-----------------------------+---------+--------------------------+------+-------------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+---------+-------+-----------------------------+-----------------------------+---------+--------------------------+------+-------------------------------------------------+
| 1 | SIMPLE | student | index | NULL | PRIMARY | 12 | NULL | 1 | Using index |
| 1 | SIMPLE | user | ALL | PRIMARY | NULL | NULL | NULL | 1 | Using where; Using join buffer (flat, BNL join) |
| 1 | SIMPLE | parents | ref | fk_responsavel_usuario1_idx | fk_responsavel_usuario1_idx | 4 | testeboletim.user.iduser | 1 | Using where; Using index |
+------+-------------+---------+-------+-----------------------------+-----------------------------+---------+--------------------------+------+-------------------------------------------------+
3 rows in set (0.00 sec)