I am developing a league application in PHP. When I visit the ladder view page, I have a query that selects all the squads from that ladder and orders them by their experience(league_experience). I want to modify the query so that it finds the rank of the current squad.
$query_squads = "
SELECT
s.squad_id AS squad_id, s.ladder_id, s.team_id AS team_id,
x.experience_id, x.squad_id, SUM(x.value) as total_exp
FROM league_squads AS s
LEFT JOIN league_experience AS x ON (s.squad_id = x.squad_id)
WHERE s.ladder_id = ".$ladder_id."
GROUP BY s.squad_id, s.ladder_id, s.team_id, x.experience_id, x.squad_id
ORDER BY total_exp DESC
";
Here's my tables
--
-- Table structure for table `league_experience`
--
CREATE TABLE IF NOT EXISTS `league_experience` (
`experience_id` int(15) NOT NULL,
`squad_id` int(15) NOT NULL,
`value` int(15) NOT NULL,
`date_earned` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`description` varchar(255) NOT NULL,
PRIMARY KEY (`experience_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
--
-- Dumping data for table `league_experience`
--
INSERT INTO `league_experience` (`experience_id`, `squad_id`, `value`, `date_earned`, `description`) VALUES
(1, 1, 500, '2013-09-03 07:10:59', 'For being ballers.'),
(2, 2, 250, '2013-09-03 07:10:52', 'For being awesome.');
-- --------------------------------------------------------
--
-- Table structure for table `league_squads`
--
CREATE TABLE IF NOT EXISTS `league_squads` (
`squad_id` int(15) NOT NULL AUTO_INCREMENT,
`team_id` int(15) NOT NULL,
`ladder_id` int(15) NOT NULL,
`date_joined` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`status` tinyint(1) NOT NULL,
`last_rank` tinyint(5) NOT NULL,
PRIMARY KEY (`squad_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;
--
-- Dumping data for table `league_squads`
--
INSERT INTO `league_squads` (`squad_id`, `team_id`, `ladder_id`, `date_joined`, `status`, `last_rank`) VALUES
(1, 1, 1, '2013-09-03 08:16:27', 0, 1),
(2, 2, 1, '2013-09-03 08:16:25', 0, 2);
SELECT
s.squad_id AS squad_id, s.ladder_id, s.team_id AS team_id,
x.experience_id, x.squad_id, SUM(x.value) as total_exp,
#i:=#i+1 AS rank
FROM league_squads AS s
LEFT JOIN league_experience AS x ON (s.squad_id = x.squad_id),
(SELECT #i:=0) AS foo
WHERE s.ladder_id = 1
GROUP BY s.squad_id, s.ladder_id, s.team_id, x.experience_id, x.squad_id
ORDER BY total_exp DESC
sample fiddle
Related
I have 4 tables. One for companies, one for products one for company address, and one for company directors.
The products, director and address tables are in a one to many relationship to the company table.
So one company can have many products, many addresses and many directors.
CREATE TABLE IF NOT EXISTS `companies` (
`company_id` int(11) NOT NULL AUTO_INCREMENT,
`company_name` varchar(50) NOT NULL,
PRIMARY KEY (`company_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;
CREATE TABLE IF NOT EXISTS `products` (
`product_id` int(11) NOT NULL AUTO_INCREMENT,
`company_id` int(11) NOT NULL,
`product` varchar(50) NOT NULL,
PRIMARY KEY (`product_id`),
KEY `company_id` (`company_id`),
KEY `product` (`product`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=6 ;
CREATE TABLE IF NOT EXISTS `directors` (
`director_id` int(11) NOT NULL AUTO_INCREMENT,
`company_id` int(11) NOT NULL,
`surname` varchar(100) NOT NULL,
`dob` date NOT NULL,
PRIMARY KEY (`director_id`),
KEY `company_id` (`company_id`),
KEY `surname` (`surname`),
KEY `dob` (`dob`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=6 ;
CREATE TABLE IF NOT EXISTS `addresses` (
`address_id` int(11) NOT NULL AUTO_INCREMENT,
`company_id` int(1) NOT NULL,
`postcode` varchar(10) NOT NULL,
PRIMARY KEY (`address_id`),
KEY `company_id` (`company_id`),
KEY `postcode` (`postcode`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=11 ;
INSERT INTO `companies` (`company_id`, `company_name`) VALUES
(1, 'Honda'),
(2, 'Toyota');
INSERT INTO `products` (`product_id`, `company_id`, `product`) VALUES
(1, 1, 'Civic'),
(2, 1, 'Accord'),
(3, 2, 'Corolla'),
(4, 2, 'Prius'),
(5, 1, 'CRV');
INSERT INTO `directors` (`director_id`, `company_id`, `surname`, `dob`) VALUES
(1, 1, 'Jones', '1990-09-09'),
(2, 1, 'Smith', '1980-08-08'),
(3, 2, 'Lucas', '1970-07-07'),
(4, 1, 'Kelly', '1960-06-06'),
(5, 2, 'Monty', '1950-05-05');
INSERT INTO `addresses` (`address_id`, `company_id`, `postcode`) VALUES
(6, 1, '12345'),
(7, 2, '23456'),
(8, 1, '34567'),
(9, 2, '45678'),
(10, 1, '56789');
Im trying to write an efficient query (using MySql / PDO) to find products for companies that match match directors (surname AND dob) and addresses (postcode).
I just want to list one matching product per row, not list every director or postcode separately.
So far I have the below query, which seems to work, but it's ugly and I suspect a ridiculous way to go about this in terms of speed and efficiency.
SELECT product
FROM products p
LEFT JOIN companies c USING(company_id)
WHERE :lname IN (
SELECT surname
FROM directors d
WHERE c.company_id = d.company_id )
AND :dob IN (
SELECT dob
FROM directors d
WHERE c.company_id = d.company_id )
AND :postcode IN (
SELECT postcode
FROM addresses a
WHERE c.company_id = a.company_id )
Thank you in advance for your help.
Unsure why you need subqueries at all?
SELECT p.product FROM products p
INNER JOIN companies c USING(company_id)
INNER JOIN directors d ON d.company_id = c.company_id AND d.surname = 'Jones' AND d.dob = '1990-09-09'
INNER JOIN addresses a ON a.company_id = c.company_id AND a.postcode = '12345'
Or
SELECT p.product FROM products p
INNER JOIN companies c USING(company_id)
INNER JOIN directors d USING(company_id)
INNER JOIN addresses a USING(company_id)
WHERE d.surname = 'Jones'
AND d.dob = '1990-09-09'
AND a.postcode = '12345'
If you do an EXPLAIN on these two queries, you'll see they end up the same internally.
At the very least, the two subqueries on directors can be unified by rewriting them with the exists operator instead of in. For good measures, I rewrote the entire query with this operator, although it's not strictly necessary:
SELECT product
FROM products p
LEFT JOIN companies c USING(company_id)
WHERE EXISTS (SELECT *
FROM directors d
WHERE c.company_id = d.company_id AND
(:lname = d.lanme OR :dob = d.dob)) AND
EXISTS (SELECT *
FROM addresses a
WHERE c.company_id = a.company_id AND :postcode = a.postcode)
I'm writing code for a school transcript system. However, I'm stuck in joining the tables which are the courses table I named 'causes', courses registered table I named 'courses_registered' and the students table 'students' actually the join works, but the problem is that data are repeated in multiples depending on how many data rows that are on the courses_registered table for example, if have 2 courses registered, I get 4 rows echoed out from all tables instead of two. so my question is, how can i join these three tables perfectly without data being repeated, when i loop through the data. this is the table structure for the three tables.
--
-- Table structure for table `courses`
--
CREATE TABLE IF NOT EXISTS `courses` (
`id` int(255) NOT NULL AUTO_INCREMENT,
`course_code` varchar(255) NOT NULL,
`course_title` varchar(255) NOT NULL,
`cl` int(255) NOT NULL,
`level` int(255) NOT NULL,
`session` varchar(255) NOT NULL,
`semester` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;
--
-- Dumping data for table `courses`
--
INSERT INTO `courses` (`id`, `course_code`, `course_title`, `cl`, `level`, `session`, `semester`) VALUES
(1, 'GSS 111', 'Use of English', 3, 1, '2009/2010', '1'),
(2, 'GSS 112', 'Nigerian History', 2, 1, '2009/2010', '1');
--
-- Table structure for table `courses_registered`
--
CREATE TABLE IF NOT EXISTS `courses_registered` (
`id` int(255) NOT NULL AUTO_INCREMENT,
`course_id` int(255) NOT NULL,
`student_id` int(255) NOT NULL,
`score` int(255) NOT NULL,
`grade` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;
--
-- Dumping data for table `courses_registered`
--
INSERT INTO `courses_registered` (`id`, `course_id`, `student_id`, `score`, `grade`) VALUES
(1, 1, 2, 60, 'B'),
(2, 2, 2, 80, 'A');
-- --------------------------------------------------------
--
-- Table structure for table `students`
--
CREATE TABLE IF NOT EXISTS `students` (
`id` int(255) NOT NULL AUTO_INCREMENT,
`regno` varchar(255) NOT NULL,
`college` varchar(255) NOT NULL,
`dept` varchar(255) NOT NULL,
`level` varchar(255) NOT NULL,
`fname` varchar(255) NOT NULL,
`lname` varchar(255) NOT NULL,
`no_of_semesters` int(255) NOT NULL,
`gender` varchar(255) NOT NULL,
`dob` varchar(255) NOT NULL,
`age` int(255) NOT NULL,
`mstatus` varchar(255) NOT NULL,
`nationality` varchar(255) NOT NULL,
`religion` varchar(255) NOT NULL,
`address` varchar(255) NOT NULL,
`email` varchar(255) NOT NULL,
`phone` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;
--
-- Dumping data for table `students`
--
INSERT INTO `students` (`id`, `regno`, `college`, `dept`, `level`,
`fname`, `lname`, `no_of_semesters`, `gender`, `dob`, `age`,
`mstatus`, `nationality`, `religion`, `address`, `email`, `phone`)
VALUES
(2, 'MOUAU 09/14508', 'CEET', 'Electrical Electronics Engineering',
'1', 'Nnamdi', 'Okoro', 2, 'Male', '24-07-1989', 25, 'Single',
'Nigerian', 'christian', 'Okpu Umiobo road, Aba',
'nokoro#gmail.com', '09056733333');
Then this is the query
<?php
$sql = "
SELECT c.id as course_id
, c.course_code
, c.course_title
, c.cl
, c.level
, c.session
, c.semester
, r.id as rid
, r.course_id
, r.student_id
, r.score
, r.grade
, s.id
, s.regno
, s.fname
, s.lname
, s.no_of_semesters
FROM courses_registered r
JOIN courses c
ON r.course_id = course_id
JOIN students s
ON s.id = r.student_id
WHERE s.id = '$id'
";
$result = mysqli_query($con,$sql) or die ('Could not show students records.'. mysqli_error($con) );
if (mysqli_num_rows($result) > 0 ){
//echo mysqli_num_rows($result);
while($row = mysqli_fetch_array($result) ){
$score[] = $row['score'];
$course_code[] = $row['course_code'];
$course_title[] = $row['course_title'];
$cl[] = $row['cl'];
$grade[] = $row['grade'];
}
Try change this
ON r.course_id = course_id
to
ON r.course_id = c.id
table structure is as follows
-- Table structure for table category
CREATE TABLE `category` (
`cat_id` int(10) NOT NULL auto_increment,
`heading` varchar(255) NOT NULL,
PRIMARY KEY (`cat_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;
INSERT INTO `category` (`cat_id`, `heading`) VALUES
(1, 'Fashion'),
(2, 'Kids');
-- --------------------------------------------------------
-- Table structure for table `shop`
CREATE TABLE `shop` (
`store_id` int(10) NOT NULL auto_increment,
`shop_name` varchar(255) NOT NULL,
`cat_id` int(10) NOT NULL,
`subcat_id` int(10) NOT NULL,
PRIMARY KEY (`store_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=4 ;
INSERT INTO `shop` (`store_id`, `shop_name`, `cat_id`, `subcat_id`) VALUES
(1, 'Test Store', 1, 1),
(2, 'Test Store 1', 1, 1),
(3, 'Another Store', 1, 3);
-- --------------------------------------------------------
-- Table structure for table `subcategory`
CREATE TABLE `subcategory` (
`subcat_id` int(10) NOT NULL auto_increment,
`cat_id` int(10) NOT NULL,
`heading` varchar(255) NOT NULL,
PRIMARY KEY (`subcat_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=5 ;
INSERT INTO `subcategory` (`subcat_id`, `cat_id`, `heading`) VALUES
(1, 1, 'Women'),
(2, 1, 'General'),
(3, 1, 'Men'),
(4, 2, 'Children');
if i use the below query i get the following output
SELECT
`category`.`heading` AS `category`
, `subcategory`.`heading` AS `subcategory`
, COUNT(`shop`.`subcat_id`) AS cnt
FROM
`test`.`shop`
INNER JOIN `test`.`subcategory`
ON (`shop`.`subcat_id` = `subcategory`.`subcat_id`)
INNER JOIN `test`.`category`
ON (`shop`.`cat_id` = `category`.`cat_id`)
GROUP BY `shop`.`subcat_id`
HAVING (COUNT(`shop`.`subcat_id`) !='');
categorysubcategorycnt
FashionWomen2
FashionMen1
but i want to group concat the subcategory like below
categorysubcategory
FashionWomen,2|Men,1
Try this
SELECT t.category,
GROUP_CONCAT(CONCAT(t.subcategory,',',t.cnt) SEPARATOR '|') `concat`
FROM (
SELECT
`category`.`heading` AS `category`
, `subcategory`.`heading` AS `subcategory`
, COUNT(`shop`.`subcat_id`) AS cnt
FROM
`shop`
INNER JOIN `subcategory`
ON (`shop`.`subcat_id` = `subcategory`.`subcat_id`)
INNER JOIN `category`
ON (`shop`.`cat_id` = `category`.`cat_id`)
GROUP BY `shop`.`subcat_id`
) t
GROUP BY t.category
Note group concat has a default limit of 1024 character but it can be increased by following the manual
Fiddle Demo
Not a recommended output format, but easily done with a nested subquery:
SELECT category,
group_concat(subcategory, ',', cnt separator '|') as vals
FROM (SELECT c.`heading` AS `category`, sc.`heading` AS `subcategory`,
COUNT(`shop`.`subcat_id`) AS cnt
FROM `test`.`shop` s INNER JOIN
`test`.`subcategory` sc
ON s.`subcat_id` = sc.`subcat_id`) INNER JOIN
`test`.`category` c
ON s.`cat_id` = c.`cat_id`
GROUP BY c.`heading`, sc.`heading`
) sc
GROUP BY category;
Your having clause is unnecessary. It is just checking that there is at least one row for each group. But there is one, because you are using inner join.
I have three primary tables in this scenario: leage_ladders, league_squads and league_experience. I am currently working on the ladder view page and would like to find all squads on the given ladder that have experience. The query does not return any errors, however, it only returns one row, and I have no idea why.
Below is the query I am using:
$query_squads = "
SELECT
s.squad_id AS squad_id, s.ladder_id, s.team_id AS team_id,
x.experience_id, x.squad_id, SUM(x.value) as total_exp
FROM league_squads AS s
LEFT JOIN league_experience AS x ON (s.squad_id = x.squad_id)
WHERE s.ladder_id = ".$ladder_id."
HAVING total_exp > 0
ORDER BY total_exp DESC
";
Below are my tables:
--
-- Table structure for table `league_experience`
--
CREATE TABLE IF NOT EXISTS `league_experience` (
`experience_id` int(15) NOT NULL,
`squad_id` int(15) NOT NULL,
`value` int(15) NOT NULL,
`date_earned` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`description` varchar(255) NOT NULL,
PRIMARY KEY (`experience_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
--
-- Dumping data for table `league_experience`
--
INSERT INTO `league_experience` (`experience_id`, `squad_id`, `value`, `date_earned`, `description`) VALUES
(1, 1, 500, '2013-09-03 07:10:59', 'For being ballers.'),
(2, 2, 250, '2013-09-03 07:10:52', 'For being awesome.');
-- --------------------------------------------------------
--
-- Table structure for table `league_squads`
--
CREATE TABLE IF NOT EXISTS `league_squads` (
`squad_id` int(15) NOT NULL AUTO_INCREMENT,
`team_id` int(15) NOT NULL,
`ladder_id` int(15) NOT NULL,
`date_joined` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`status` tinyint(1) NOT NULL,
`last_rank` tinyint(5) NOT NULL,
PRIMARY KEY (`squad_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;
--
-- Dumping data for table `league_squads`
--
INSERT INTO `league_squads` (`squad_id`, `team_id`, `ladder_id`, `date_joined`, `status`, `last_rank`) VALUES
(1, 1, 1, '2013-09-02 09:43:53', 0, 0),
(2, 2, 1, '2013-09-03 06:14:49', 0, 0);
The query should return two results when ladder_id 1 is visited. But it only displays the first result.
Because you are using an aggregate function SUM() without GROUP BY clause. What it does is it calculates the total sum of all filtered records and return a random value for non-aggregated column. Try adding GROUP BY clause,
WHERE ...
GROUP BY s.squad_id, s.ladder_id, s.team_idd, x.experience_id, x.squad_id
ORDER BY ...
According to MySQL Docs,
If you use a group function in a statement containing no GROUP BY
clause, it is equivalent to grouping on all rows. For more
information, see Section 12.15.3, MySQL Extensions to GROUP BY.
You have aggregate function in your select part: SUM(x.value) as total_exp. And because there's no GROUP BY, you'll get one row with total_exp sum of matched rows.
This is because you use aggregating function:
SUM(x.value) as total_exp
Which automatically groups rows.
And because you didn't defined GROUP BY clause, it groups all rows together.
yes, you used SUM() aggregating function.
i fixed your SQL.
SELECT
s.squad_id AS squad_id, s.ladder_id, s.team_id AS team_id,
x.experience_id, x.squad_id, SUM(x.value) as total_exp
FROM league_squads AS s
LEFT JOIN league_experience AS x ON (s.squad_id = x.squad_id)
WHERE s.ladder_id = "1"
GROUP BY s.squad_id, s.ladder_id, s.team_id, x.experience_id
ORDER BY s.squad_id DESC
i just insert 1 line. to clarify. it make determining what to SUM(). surely, you can change this conditions.
GROUP BY s.squad_id, s.ladder_id, s.team_id, x.experience_id
thanks.
Actually I'm working on a project and I'm looking on how Zend Framework 2 handle complex queries (expecially on how to join n:m tables and how to use GROUP_CONCAT and other functions). Do you know the best practice to execute this query:
SELECT o. * , x.group_one, x.group_two
FROM table_one AS o
LEFT JOIN (
SELECT r.fk1, GROUP_CONCAT( t.field_one ) AS group_one, GROUP_CONCAT( t.field_two ) AS group_two
FROM table_three AS r
INNER JOIN table_two AS t ON r.fk2 = t.id
GROUP BY r.fk1
) AS x ON o.id = x.fk1
LIMIT 0 , 20;
using this db schema:
--
-- Database: `table-test-1`
--
-- --------------------------------------------------------
--
-- Structure of table `table_one`
--
CREATE TABLE `table_one` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`field_1` varchar(255) NOT NULL,
`field_2` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=6 ;
--
-- Dump for table `table_one`
--
INSERT INTO `table_one` (`id`, `field_1`, `field_2`) VALUES
(1, 'baz', 'bat'),
(2, 'foo', 'bar'),
(3, 'foo2', 'bat2'),
(4, 'fuz', 'bar2'),
(5, 'poo', 'pee');
-- --------------------------------------------------------
--
-- Structure of table `table_three`
--
CREATE TABLE `table_three` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`fk1` bigint(20) NOT NULL,
`fk2` bigint(20) NOT NULL,
PRIMARY KEY (`id`),
KEY `fk1` (`fk1`,`fk2`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=10 ;
--
-- Dump for table `table_three`
--
INSERT INTO `table_three` (`id`, `fk1`, `fk2`) VALUES
(5, 1, 1),
(1, 1, 2),
(6, 1, 4),
(2, 2, 2),
(4, 3, 2),
(7, 3, 3),
(3, 4, 1),
(8, 5, 3),
(9, 5, 4);
-- --------------------------------------------------------
--
-- Structure of table `table_two`
--
CREATE TABLE `table_two` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`field_one` varchar(255) NOT NULL,
`field_two` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=5 ;
--
-- Dump for table `table_two`
--
INSERT INTO `table_two` (`id`, `field_one`, `field_two`) VALUES
(1, 'label_name_1', 'label_extended_name_1'),
(2, 'label_name_2', 'label_extended_name2'),
(3, 'label_name_3', 'label_extended_name_3'),
(4, 'label_name_4', 'label_extended_name4');
At the moment I solved using a Zend\Db\Sql\Sql statement with an handmade query, but I'd like to know if there is, actually, a way to do this with a native Select() (possibly without using Doctrine or similar).
Thank you in advance :)
You have to import use Zend\Db\Sql\Predicate\Expression; to use group_concat.
Ex:
$sql = new Sql($this->adapter);
$select = $sql->select();
$select->columns(array('*'));
$select->from('tblCGii');
$select->join("tblCGFieldValues", "tblCGii.id = tblCGFieldValues.Cgii_id", array("field_values"=>new Expression("Group_Concat(tblCGFieldValues.field_values)")),"LEFT");
$select->group('tblCGii.id');