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)
Related
I am trying to make a DELETE TRIGGER so that when a row is deleted from a table Bike it will delete a row in another table called Available as long as it is not in a different table called in Rental. If it is in Rental table then it will cancel the delete (if that's possible I'm very new to sql).
BEGIN
DELETE FROM Available
Where old.bnumber = Available.bnumber;
END
right now I am getting
1451: Cannot delete or update a parent row: a foreign key constraint
fails (cecs535project.available, CONSTRAINT bnumber FOREIGN KEY
(bnumber) REFERENCES bike (bnumber))
bnumber is a foreign key in Available that references Bike.
Any help is appreciated.
CREATE TABLE `Bike` (
`bnumber` int NOT NULL,
`make` varchar(64) DEFAULT NULL,
`color` varchar(8) DEFAULT NULL,
`year` int DEFAULT NULL,
PRIMARY KEY (`bnumber`)
)
CREATE TABLE `Available` (
`bnumber` int NOT NULL,
`rack-id` int DEFAULT NULL,
PRIMARY KEY (`bnumber`),
KEY `bnumber_idx` (`rack-id`),
KEY `bnumber_idx1` (`bnumber`),
CONSTRAINT `bnumber` FOREIGN KEY (`bnumber`) REFERENCES `Bike` (`bnumber`),
CONSTRAINT `rack-id` FOREIGN KEY (`rack-id`) REFERENCES `Rack` (`id`)
)
CREATE TABLE `Rental` (
`date` date NOT NULL,
`time` time NOT NULL,
`bnumber` int NOT NULL,
`cust-id` int NOT NULL,
`src` int DEFAULT NULL,
PRIMARY KEY (`bnumber`,`cust-id`,`date`,`time`),
KEY `bnumber_idx` (`bnumber`),
KEY `cust-id_idx` (`cust-id`),
KEY `src_idx` (`src`),
CONSTRAINT `bike` FOREIGN KEY (`bnumber`) REFERENCES `Bike` (`bnumber`),
CONSTRAINT `cust-id` FOREIGN KEY (`cust-id`) REFERENCES `Customer` (`id`),
CONSTRAINT `src` FOREIGN KEY (`src`) REFERENCES `Rack` (`id`)
)
I am not a big fan of your current design, and I think it can be simplified. Consider just having a single table for all bike assets, with one column maintaining whether or not it be currently rented out, e.g.
Bike
id | name | type | rented (bit)
1 | bike1 | road | 1
2 | bike2 | mountain | 0
...
Now to record a bike being rented or not, you simply have to update the rented bit column above. Should you want to delete from your inventory, refraining from doing so if the bike be on loan, you can use:
DELETE
FROM Bike
WHERE rented = 0; -- AND your other conditions here
2 possible approaches
Use signal to identify where rental exists an throw an error https://dev.mysql.com/doc/refman/8.0/en/signal.html
test for rental existence in delete;
In both a before trigger is used since the constraint test occurs before an after trigger fires.
DROP TABLE IF EXISTS AVAILABLE;
drop table if exists rental;
drop table if exists BIKE;
CREATE TABLE `Bike` (
`bnumber` int NOT NULL,
`make` varchar(64) DEFAULT NULL,
`color` varchar(8) DEFAULT NULL,
`year` int DEFAULT NULL,
PRIMARY KEY (`bnumber`)
);
insert into bike values
(10,'aaa','red',2020),(20,'bbb','yell',2020);
CREATE TABLE `Available` (
`bnumber` int NOT NULL,
`rack-id` int DEFAULT NULL,
PRIMARY KEY (`bnumber`),
KEY `bnumber_idx` (`rack-id`),
KEY `bnumber_idx1` (`bnumber`),
CONSTRAINT `bnumber` FOREIGN KEY (`bnumber`) REFERENCES `Bike` (`bnumber`)#,
#CONSTRAINT `rack-id` FOREIGN KEY (`rack-id`) REFERENCES `Rack` (`id`)
) ;
insert into available values
(10,100),(20,200);
CREATE TABLE `Rental` (
#`date` date NOT NULL,
#`time` time NOT NULL,
`bnumber` int NOT NULL,
#`cust-id` int NOT NULL,
#`src` int DEFAULT NULL,
#PRIMARY KEY (`bnumber`,`cust-id`,`date`,`time`),
KEY `bnumber_idx` (`bnumber`),
#KEY `cust-id_idx` (`cust-id`),
#KEY `src_idx` (`src`),
CONSTRAINT `bike` FOREIGN KEY (`bnumber`) REFERENCES `Bike` (`bnumber`)#,
#CONSTRAINT `cust-id` FOREIGN KEY (`cust-id`) REFERENCES `Customer` (`id`),
#CONSTRAINT `src` FOREIGN KEY (`src`) REFERENCES `Rack` (`id`)
) ;
insert into rental values
(10);
Approach 1
drop trigger if exists t;
delimiter $$
create trigger t before delete on bike
for each row
begin
if exists (select 1 from rental r where r.bnumber = old.bnumber) then
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'An error occurred Rental exists';
end if;
delete a from available a where a.bnumber = old.bnumber;
end $$
delimiter ;
delete b from bike b;
ERROR 1644 (45000): An error occurred Rental exists
+---------+------+-------+------+
| bnumber | make | color | year |
+---------+------+-------+------+
| 10 | aaa | red | 2020 |
| 20 | bbb | yell | 2020 |
+---------+------+-------+------+
2 rows in set (0.001 sec)
select * from available;
+---------+---------+
| bnumber | rack-id |
+---------+---------+
| 10 | 100 |
| 20 | 200 |
+---------+---------+
2 rows in set (0.001 sec)
select * from rental;
+---------+
| bnumber |
+---------+
| 10 |
+---------+
1 row in set (0.001 sec)
Approach 2
drop trigger if exists t;
delimiter $$
create trigger t before delete on bike
for each row
begin
delete a from available a where a.bnumber = old.bnumber;
end $$
delimiter ;
delete b from bike b where
#bnumber = 10 and
not exists(select 1 from rental r where r.bnumber = b.bnumber);
select * from bike;
+---------+------+-------+------+
| bnumber | make | color | year |
+---------+------+-------+------+
| 10 | aaa | red | 2020 |
+---------+------+-------+------+
1 row in set (0.001 sec)
select * from available;
+---------+---------+
| bnumber | rack-id |
+---------+---------+
| 10 | 100 |
+---------+---------+
1 row in set (0.001 sec)
select * from rental;
+---------+
| bnumber |
+---------+
| 10 |
+---------+
1 row in set (0.001 sec)
Creating an AFTER DELETE instead could also help to skip the Foreign Key Constraint issue
CREATE TRIGGER trigger_name
AFTER DELETE
ON table_name FOR EACH ROW
trigger_body;
ref: https://www.mysqltutorial.org/mysql-triggers/mysql-after-delete-trigger/
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 ;
1.Below query is taking around 49 sec to execute.
2.our target is need to get result in 1 to 2sec.
3.query having indexes and it was using
4.how to avoide this long time execution.
5.it is a simple query
6.if possible help me to rewrite the query.
query:
select cppm.* from cat_ctlg_product_product_map cppm, cat_product_product_map ppm where cppm.product_product_map_id = ppm.product_product_map_id and ppm.product_id = 2585682 and cppm.catalog_id in ( 2136359, 2136371);
Explain plan:
+----+-------------+-------+-------+------------------------------------+---------------------------+---------+-------+--------+--------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+------------------------------------+---------------------------+---------+-------+--------+--------------------------------+
| 1 | SIMPLE | ppm | ref | PRIMARY,idx_3342,idx_5419 | idx_3342 | 4 | const | 1 | Using index |
| 1 | SIMPLE | cppm | range | idx_3472,fkey_cat_ctlg_produc_4100 | fkey_cat_ctlg_produc_4100 | 4 | NULL | 135334 | Using where; Using join buffer |
+----+-------------+-------+-------+------------------------------------+---------------------------+---------+-------+--------+--------------------------------+
2 rows in set (0.00 sec)
Table structures:
mysql> show create table cat_ctlg_product_product_map\G
*************************** 1. row ***************************
Table: cat_ctlg_product_product_map
Create Table: CREATE TABLE `cat_ctlg_product_product_map` (
`row_mod` datetime DEFAULT NULL,
`row_create` datetime DEFAULT NULL,
`product_product_map_id` int(11) NOT NULL,
`catalog_id` int(11) NOT NULL,
UNIQUE KEY `idx_3472` (`product_product_map_id`,`catalog_id`),
KEY `fkey_cat_ctlg_produc_4100` (`catalog_id`),
CONSTRAINT `fkey_cat_ctlg_produc_4100` FOREIGN KEY (`catalog_id`) REFERENCES `cat_catalogs` (`catalog_id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT `fkey_cat_ctlg_produc_5415` FOREIGN KEY (`product_product_map_id`) REFERENCES `cat_product_product_map` (`product_product_map_id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_bin
1 row in set (0.00 sec)
mysql> show create table cat_product_product_map\G
*************************** 1. row ***************************
Table: cat_product_product_map
Create Table: CREATE TABLE `cat_product_product_map` (
`row_mod` datetime DEFAULT NULL,
`row_create` datetime DEFAULT NULL,
`product_product_map_id` int(11) NOT NULL,
`owner_catalog_id` int(11) NOT NULL,
`parent_product_id` int(11) NOT NULL,
`product_id` int(11) NOT NULL,
`precedence` int(11) DEFAULT '100',
`is_default` int(11) DEFAULT NULL,
`product_product_type` enum('cross-sell','skuoption','up-sell','addon','works','kit','autocross') COLLATE latin1_bin DEFAULT NULL,
PRIMARY KEY (`product_product_map_id`),
KEY `idx_3342` (`product_id`,`product_product_type`),
KEY `idx_5251` (`parent_product_id`,`product_product_type`,`product_id`),
KEY `idx_5419` (`product_product_map_id`,`parent_product_id`,`product_id`),
KEY `fkey_cat_product_pro_4229` (`owner_catalog_id`),
KEY `cat_product_product_map_n1` (`parent_product_id`,`product_product_type`,`product_product_map_id`,`precedence`),
CONSTRAINT `fkey_cat_product_pro_3617` FOREIGN KEY (`product_id`) REFERENCES `cat_products` (`product_id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT `fkey_cat_product_pro_4229` FOREIGN KEY (`owner_catalog_id`) REFERENCES `cat_catalogs` (`catalog_id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT `fkey_cat_product_pro_4362` FOREIGN KEY (`parent_product_id`) REFERENCES `cat_products` (`product_id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_bin
1 row in set (0.00 sec)
Use join
select cppm.* from cat_ctlg_product_product_map cppm
INNER JOIN cat_product_product_map ppm ON (cppm.product_product_map_id = ppm.product_product_map_id AND cppm.catalog_id in ( 2136359, 2136371))
WHERE ppm.product_id = 2585682;
Suggest you create the following indexes:
ALTER TABLE cat_ctlg_product_product_map
ADD INDEX (catalog_id, product_product_map_id);
ALTER TABLE cat_product_product_map
ADD INDEX (product_id, product_product_map_id);
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);
CREATE TABLE accounts (
account_name VARCHAR(100) NOT NULL PRIMARY KEY
);
CREATE TABLE products (
product_id INTEGER NOT NULL PRIMARY KEY,
product_name VARCHAR(100)
);
CREATE TABLE bugs (
bug_id INTEGER NOT NULL PRIMARY KEY,
bug_description VARCHAR(100),
bug_status VARCHAR(20),
reported_by VARCHAR(100) REFERENCES accounts(account_name),
assigned_to VARCHAR(100) REFERENCES accounts(account_name),
verified_by VARCHAR(100) REFERENCES accounts(account_name)
);
CREATE TABLE bugs_products (
bug_id INTEGER NOT NULL REFERENCES bugs,
product_id INTEGER NOT NULL REFERENCES products,
PRIMARY KEY (bug_id, product_id)
);
if i execute 'describe bugs_products' i get:
Field | Type | Null | Key | Default | Extra |
+------------+---------+------+-----+---------+-------+
| bug_id | int(11) | NO | PRI | NULL | |
| product_id | int(11) | NO | PRI | NULL | |
+------------+---------+------+-----+---------+-------+
how can i also get references information?
On testing, the foreign keys are not created on my machine using this syntax:
CREATE TABLE bugs (
...
reported_by VARCHAR(100) REFERENCES accounts(account_name),
...
) ENGINE = INNODB;
But they are when I use this create statement:
CREATE TABLE bugs (
...
reported_by VARCHAR(100),
...
FOREIGN KEY (reported_by) REFERENCES accounts(account_name)
) ENGINE = INNODB;
An easy way to see if foreign keys exist on a table is:
show create table bugs_products
Or you can query the information schema:
select
table_schema
, table_name
, column_name
, referenced_table_schema
, referenced_table_name
, referenced_column_name
from information_schema.KEY_COLUMN_USAGE
where table_name = 'bugs'
Also check you're using the InnoDB storage engine. The MyISAM engine does not support foreign keys. You can find the engine like:
select table_schema, table_name, engine
from information_schema.TABLES
where table_name = 'bugs'
If you try to create a foreign key on a MyISAM table, it will silently discard the references and pretend to succeed.