Nested SELECT + INNER JOIN - mysql

First table
CREATE TABLE IF NOT EXISTS `city_node` (
`node_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`title` varchar(50) NOT NULL,
`parent_node_id` int(10) unsigned NOT NULL DEFAULT '0',
`lft` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'Nested set info ''left'' value',
`rgt` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'Nested set info ''right'' value',
`depth` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'Depth = 0: no parent',
PRIMARY KEY (`node_id`),
KEY `parent_node_id` (`parent_node_id`),
KEY `lft` (`lft`)) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=26;
And data
INSERT INTO `city_node` (`node_id`, `title`, `parent_node_id`, `lft`, `rgt`, `depth`) VALUES
(1, 'Great Britain', 0, 1, 20, 0),
(3, 'England', 1, 2, 9, 1),
(7, 'Scotland', 1, 16, 19, 1),
(8, 'Edinburgh', 7, 17, 18, 2),
(9, 'Wales', 1, 10, 15, 1),
(10, 'Cardiff', 9, 11, 12, 2),
(11, 'London', 3, 3, 4, 2),
(12, 'Birmingham', 3, 5, 6, 2),
(13, 'Germany', 0, 21, 26, 0),
(14, 'Stuttgart', 13, 22, 23, 1),
(15, 'Newport', 9, 13, 14, 2),
(16, 'Munich', 13, 24, 25, 1),
(17, 'Israel', 0, 27, 32, 0),
(18, 'Tel Aviv', 17, 28, 29, 1),
(19, 'Ashdod', 17, 30, 31, 1),
(20, 'USA', 0, 33, 38, 0),
(21, 'New York', 20, 34, 35, 1),
(24, 'Liverpool', 3, 7, 8, 2),
(25, 'Detroit', 20, 36, 37, 1);
Second table
CREATE TABLE IF NOT EXISTS `city_node_entity` (
`node_id` int(10) NOT NULL,
`entity` tinyint(3) unsigned NOT NULL DEFAULT '1',
KEY `node_id` (`node_id`,`entity`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
And data
INSERT INTO `city_node_entity` (`node_id`, `entity`) VALUES
(11, 1),
(12, 1),
(16, 1),
(19, 1);
I want to get node with entity 1 and its ancestors, like this
Great Britain
--England
----London
----Birmingham
Germany
--Munich
Israel
--Ashdod
So, my query is
SELECT DISTINCT(node_ext.node_id), node_ext.*
FROM city_node_entity AS entity
LEFT JOIN city_node AS node
ON entity.node_id = node.node_id
LEFT JOIN city_node AS node_ext
ON node_ext.lft <= node.lft AND node_ext.rgt >= node.rgt
WHERE entity.entity = 1
ORDER BY node_ext.lft
But explain shows
-Using where; Using index; Using temporary; Using filesort
Is there any other queries to get same result but with less [EXTRA]?

The only thing you can do for recursive queries is to run it with joins for each parent/child.... because your second table is the actual city name and you want to work backwards you can do it like this... just add more joins till all results are null and you will have yourself the full tree
SELECT node.title AS child, t2.title as parent1, t3.title as parent2, t4.title as parent3
FROM city_node_entity AS entity
LEFT JOIN city_node AS node ON entity.node_id = node.node_id
LEFT JOIN city_node AS t2 ON t2.node_id = node.parent_node_id
LEFT JOIN city_node AS t3 ON t3.node_id = t2.parent_node_id
LEFT JOIN city_node AS t4 ON t4.node_id = t3.parent_node_id;
Fiddle Demo

Related

How do I update query two tables and update in one table?

I have two tables, candidates and candidate_subjects, for storing candidate details and candidate scores respectively. I want a query to update candidate remark to 'FAIL' if the candidate pass less than 6 subjects. To pass a subject, the candidate sum of ca_score and exam_score for a subject must be greater than 40.
Below is the query I have written but it is not giving the result expected:
UPDATE candidates SET candidates.remark='FAIL' WHERE (select
count(candidate_subjects.id) AS total_pass from candidates,
candidate_subjects where candidates.id=candidate_subjects.candidate_id
and (candidate_subjects.ca_score + candidate_subjects.exam_score) >= 40) < 6
The tables:
CREATE TABLE candidate_subjects (
id INT(10) PRIMARY KEY NOT NULL AUTO_INCREMENT,
candidate_id INT(11),
exam_type_id INT(10),
subject_id INT(10),
ca_score INT(11),
exam_score INT(6),
score_grade VARCHAR(10),
date_created VARCHAR(10),
date_modified TIMESTAMP
);
INSERT INTO `candidate_subjects` (`id`, `candidate_id`, `exam_type_id`,
`subject_id`, `ca_score`, `exam_score`, `score_grade`, `date_created`,
`date_modified`) VALUES
(1, 2, 1, 32, 22, 61, NULL, '2017-02-01', '2017-08-28 13:10:33'),
(2, 2, 1, 5, 21, 38, NULL, '2017-02-01', '2017-08-28 13:10:33'),
(3, 2, 1, 14, 21, 51, NULL, '2017-02-01', '2017-08-28 13:10:33'),
(4, 2, 1, 1, 19, 34, NULL, '2017-02-01', '2017-08-28 13:10:33'),
(5, 2, 1, 2, 23, 39, NULL, '2017-02-01', '2017-08-28 13:10:33'),
(6, 2, 1, 38, 20, 32, NULL, '2017-02-01', '2017-08-28 13:10:33'),
(7, 2, 1, 53, 24, 47, NULL, '2017-02-01', '2017-08-28 13:10:33'),
(8, 4, 1, 32, 19, 61, NULL, '2017-02-01', '2017-08-28 13:11:27'),
(9, 4, 1, 5, 22, 41, NULL, '2017-02-01', '2017-08-28 13:11:27'),
(10, 4, 1, 14, 20, 46, NULL, '2017-02-01', '2017-08-28 13:11:27'),
(11, 4, 1, 1, 23, 37, NULL, '2017-02-01', '2017-08-28 13:11:27'),
(12, 4, 1, 2, 21, 36, NULL, '2017-02-01', '2017-08-28 13:11:27'),
(13, 4, 1, 38, 22, 34, NULL, '2017-02-01', '2017-08-28 13:11:27'),
(14, 4, 1, 53, 24, 52, NULL, '2017-02-01', '2017-08-28 13:11:27'),
(15, 5, 1, 32, 20, 62, NULL, '2017-02-01', '2017-08-28 13:11:44'),
(16, 5, 1, 5, 22, 38, NULL, '2017-02-01', '2017-08-28 13:11:44');
CREATE TABLE candidates (
id INT(11) PRIMARY KEY NOT NULL AUTO_INCREMENT,
exam_no VARCHAR(15),
surname VARCHAR(50),
other_names VARCHAR(100),
school_id INT(11),
registration_completed INT(11),
exam_scores_completed INT(5),
remark VARCHAR(10)
);
INSERT INTO candidates (id, exam_no, surname, other_names, school_id,
registration_completed, exam_scores_completed, remark) VALUES
(1, '1171052001', 'ABADO', 'MASENENGEN', 1052, 1, '1', ''),
(2, '1170938001', 'AGBA', 'NGUHER', 938, 1, '1', ''),
(3, '1170071001', 'ABEE', 'SESUUR', 71, 1, '1', ''),
(4, '1170938002', 'AHEN', 'REBECCA DOOSUUN', 938, 1, '1', '');
To update your candidates according to your criteria and their records exists in candidate_subjects you can use below query.
UPDATE candidates c
JOIN (select
count(id) AS total_pass ,candidate_id
from candidate_subjects
where(ca_score + exam_score) >= 40
group by candidate_id
) a on c.id = a.candidate_id
SET c.remark='FAIL'
WHERE total_pass < 6
DEMO
Can you try something like:
UPDATE candidates SET candidates.remark='FAIL' WHERE candidate_id IN (SELECT candidate_id FROM candidate_subject WHERE candidatescorecondition>40 HAVING COUNT(*) > 6);
Please run in a dev environment first!
If candidates can be marked as FAIL without doing all the exams you can do something like this:
update candidates set remark='FAIL'
where id in (
select candidate_id from candidate_subjects
where ca_score+exam_score > 40 -- passed subject
group by candidate_id having count(*)<6
)
but if a candidate must have data for at least six subjects in order to be evaluated you need to add an extra condition (so a candidate is not marked as failed if he/she is in the middle of the process):
update candidates set remark='FAIL'
where id in (
select candidate_id from candidate_subjects
where ca_score+exam_score > 40 -- passed subject
group by candidate_id having count(*)<6
)
and id in (
select candidate_id from candidate_subjects
group by candidate_id having count(*)>=6 -- the candidate has data for at leas 6 subjects
)

Filter result with two where clauses in it

I want to filter property with two type multi checkbox :
1 - Option
2 - Type
Here is my tables for this type:
propety_type:
CREATE TABLE IF NOT EXISTS `property_type` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`PropertyNumber` int(4) NOT NULL,
`TypeNumber` int(50) NOT NULL,
PRIMARY KEY (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_persian_ci AUTO_INCREMENT=25 ;
INSERT INTO `property_type` (`ID`, `PropertyNumber`, `TypeNumber`) VALUES
(13, 53, 1),
(14, 53, 2),
(15, 53, 3),
(16, 54, 3),
(17, 54, 5),
(18, 55, 6),
(19, 55, 8),
(20, 56, 3),
(21, 56, 2),
(22, 56, 1),
(23, 54, 1),
(24, 55, 1);
property_option:
CREATE TABLE IF NOT EXISTS `property_option` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`PropertyNumber` int(11) NOT NULL,
`OptionNumber` int(11) NOT NULL,
PRIMARY KEY (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=53 ;
INSERT INTO `property_option` (`ID`, `PropertyNumber`, `OptionNumber`) VALUES
(35, 53, 1),
(36, 53, 2),
(37, 53, 3),
(39, 54, 3),
(40, 54, 5),
(41, 55, 6),
(42, 55, 8),
(43, 56, 2),
(45, 56, 1),
(46, 56, 3),
(47, 56, 8),
(48, 53, 9),
(49, 53, 4),
(50, 55, 1),
(51, 54, 2),
(52, 54, 1);
My query :
SELECT property.PropertyNumber
FROM property
INNER JOIN property_option ON property.PropertyNumber = property_option.PropertyNumber
WHERE property_option.OptionNumber IN (1,3 )
GROUP BY property.PropertyNumber
HAVING COUNT(DISTINCT property_option.ID) =2
INNER JOIN property_type ON property.PropertyNumber = property_type.PropertyNumber
WHERE property_type.TypeNumber IN (1,2 )
GROUP BY property.PropertyNumber
HAVING COUNT(DISTINCT property_type.ID) =2
But when i test this in phpmyadmin, i get this error:
#1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'INNER JOIN property_option ON property.PropertyNumber = property_option.Property' at line 9
If you want to make sure the rows returned fulfills the requirement of having both specified options as well as both specified types then you can use the query below.
SELECT p.PropertyNumber
FROM property p
JOIN property_option USING (PropertyNumber)
JOIN property_type USING (PropertyNumber)
WHERE OptionNumber IN (1,8)
AND TypeNumber IN (1,2)
GROUP BY p.PropertyNumber
HAVING COUNT(DISTINCT OptionNumber) = 2
AND COUNT(DISTINCT TypeNumber) = 2;
Sample SQL Fiddle

Need help for my stored procedure please

I have this data and table structure,I am creating MySQL stored procedure to sum all the amount in the leftchild of the parentid, I don't have enough knowledge in stored procedure.and I need some help please because I'm lost.
CREATE TABLE IF NOT EXISTS `mytree` (
`parentid` int(11) NOT NULL,
`memberid` int(11) NOT NULL,
`position` char(1) NOT NULL,
`amount` decimal(10,2) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
INSERT INTO `mytree` (`parentid`, `memberid`, `position`, `amount`) VALUES
(8, 27, 'R', 0.00),
(8, 28, 'L', 0.00),
(24, 26, 'R', 0.00),
(0, 1, '', 5500.00),
(24, 25, 'L', 0.00),
(21, 24, 'L', 500.00),
(21, 23, 'R', 0.00),
(18, 20, 'R', 1500.00),
(18, 19, 'L', 0.00),
(15, 18, 'R', 2000.00),
(15, 17, 'L', 0.00),
(13, 16, 'L', 0.00),
(13, 15, 'R', 2500.00),
(12, 14, 'R', 0.00),
(12, 13, 'L', 3000.00),
(10, 12, 'R', 3500.00),
(10, 11, 'L', 0.00),
(7, 10, 'R', 4000.00),
(7, 9, 'L', 0.00),
(5, 8, 'R', 500.00),
(5, 7, 'L', 4500.00),
(1, 6, 'R', 0.00),
(1, 5, 'L', 5500.00),
(20, 22, 'R', 0.00),
(20, 21, 'L', 1000.00);
here is my stored proc
CREATE DEFINER=`root`#`localhost` PROCEDURE `count_left_right_amount`(IN `p_memid` INT, OUT `tot_left_amount` DECIMAL(10,2))
LANGUAGE SQL
NOT DETERMINISTIC
CONTAINS SQL
SQL SECURITY DEFINER
COMMENT ''
BEGIN
DECLARE row_total decimal(10,2);
DECLARE total decimal(10,2);
DECLARE m_left int;
DECLARE run_left int;
DECLARE p_id int;
set total = 0;
select memberid into p_id from mytree where position = 'L' AND parentid = p_memid;
WHILE p_id != 0 DO
select amount into row_total from mytree where parentid = p_id
GROUP BY parentid;
select memberid into m_left from mytree where parentid = p_id;
set total = total + row_total;
set p_id = m_left;
END WHILE;
END
Example if the parentid is 5 ,so the leftchild is 7 then go down to his subtree sum it all,so the total amount in the left child is '22500'? please correct me if I'm wrong.
please help me.
Thank you in advance.

How to prevent recursive limit in stored procedure

I need some help please,I'm having problem with the SP,how can I prevent this not to get recursive limit SQL ERROR(1456).Is there a way to change this SP not to use recursive...what I'm trying to achieve is to sum to all the amount in my left subtree on a given parentid...for example I have parentid 5,so my left is 7 so I want to sum all his amount on his subtree it's(22,500)if I'm not wrong.
Thank you in advance.
I have this table structure
CREATE TABLE `mytree` (
`parentid` INT(11) NOT NULL,
`memberid` INT(11) NOT NULL,
`position` CHAR(1) NOT NULL,
`amount` DECIMAL(10,2) NOT NULL,
UNIQUE INDEX `ck1tree` (`memberid`),
UNIQUE INDEX `ck2tree` (`parentid`, `position`)
)
COLLATE='latin1_swedish_ci'
ENGINE=MyISAM
;
and here is my data in the table mytree
INSERT INTO `mytree` (`parentid`, `memberid`, `position`, `amount`) VALUES
(8, 27, 'R', 0.00),
(8, 28, 'L', 0.00),
(24, 26, 'R', 0.00),
(0, 1, '', 5500.00),
(24, 25, 'L', 0.00),
(21, 24, 'L', 500.00),
(21, 23, 'R', 0.00),
(18, 20, 'R', 1500.00),
(18, 19, 'L', 0.00),
(15, 18, 'R', 2000.00),
(15, 17, 'L', 0.00),
(13, 16, 'L', 0.00),
(13, 15, 'R', 2500.00),
(12, 14, 'R', 0.00),
(12, 13, 'L', 3000.00),
(10, 12, 'R', 3500.00),
(10, 11, 'L', 0.00),
(7, 10, 'R', 4000.00),
(7, 9, 'L', 0.00),
(5, 8, 'R', 500.00),
(5, 7, 'L', 4500.00),
(1, 6, 'R', 0.00),
(1, 5, 'L', 5500.00),
(20, 22, 'R', 0.00),
(20, 21, 'L', 1000.00);
CREATE DEFINER=`root`#`localhost` PROCEDURE `sum_left_amount`(IN `p_memid` INT, OUT `tot_left_amount` DECIMAL(10,2))
LANGUAGE SQL
NOT DETERMINISTIC
CONTAINS SQL
SQL SECURITY DEFINER
COMMENT ''
BEGIN
DECLARE a decimal(10,2);
DECLARE m int;
DECLARE s decimal(10,2);
select memberid, amount into m, a from mytree
where parentid = p_memid and position = 'L';
call sum_left_amount(m,s);
set tot_left_amount = a + s;
END

mySQL Divide two values from Same Column

I wish to divide values from the same column using mySQL.
is there a better way of doing this??? Do I really need 3 select statements?
SELECT (SELECT FixAM From Fixes WHERE Id = 1) / (SELECT FixAM From Fixes WHERE Id = 2)
My table structure is as follows:
CREATE TABLE IF NOT EXISTS `Fixes` (
`Id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'PK',
`CurrencyId` int(11) NOT NULL COMMENT 'FK',
`MetalId` int(11) NOT NULL COMMENT 'FK',
`FixAM` decimal(10,5) NOT NULL,
`FixPM` decimal(10,5) DEFAULT NULL,
`TimeStamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`Id`),
KEY `CurrencyId` (`CurrencyId`),
KEY `MetalId` (`MetalId`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci AUTO_INCREMENT=13 ;
--
-- Dumping data for table `Fixes`
--
INSERT INTO `Fixes` (`Id`, `CurrencyId`, `MetalId`, `FixAM`, `FixPM`, `TimeStamp`) VALUES
(1, 1, 1, '1592.50000', '1586.25000', '2013-02-25 15:10:21'),
(2, 2, 1, '1051.84900', '1049.59300', '2013-02-25 15:10:21'),
(3, 3, 1, '1201.88700', '1194.10600', '2013-02-25 15:10:21'),
(4, 1, 2, '29.17000', NULL, '2013-02-25 13:54:02'),
(5, 2, 2, '19.27580', NULL, '2013-02-25 13:54:02'),
(6, 3, 2, '21.98190', NULL, '2013-02-25 13:54:02'),
(7, 1, 3, '1627.00000', '1620.00000', '2013-02-25 14:28:59'),
(8, 2, 3, '1074.65000', '1072.50000', '2013-02-25 14:28:59'),
(9, 3, 3, '1229.30000', '1218.95000', '2013-02-25 14:28:59'),
(10, 1, 4, '747.00000', '748.00000', '2013-02-25 14:28:59'),
(11, 2, 4, '493.40000', '495.20000', '2013-02-25 14:28:59'),
(12, 3, 4, '564.40000', '562.85000', '2013-02-25 14:28:59');
I think this is what you're looking for:
SELECT MetalId,
MAX(CASE WHEN CurrencyId = 1 THEN FixAM END) /
MAX(CASE WHEN CurrencyId = 2 THEN FixAM ELSE 1 END) Output
FROM Fixes
GROUP BY MetalId
This produces 1592.50000 / 1051.849000. If you want the opposite, swap the currency ids.
SQL Fiddle Demo
In case you don't have a CurrencyId = 2, I defaulted the dividing value to 1 so you wouldn't receive an error.