get the count of field and group concat - php

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.

Related

How could i display the items from 3 tables joined

How could i display the items from 3 tables below joining using the product_tbl id.
and using foreach. or if there is any alternative way to display it.
The tables
I couldn't get the 3rd table or pictures table.
using my existing code below.
still error.
$product_list = DB::table('product_tbl')
->leftjoin('brand_tbl', 'product_tbl.id', '=', 'brand_tbl.product_id')
->select('*')
->get();
return vehicles_list;
$prod_list = array();
foreach ($product_list as $key => $value) {
$prod_list [$value->products_name][] = $value;
}
print_r($prod_list);
please help me thank you.
If your table structure isn't set in stone you might try the following structure for a more normalized and indexable structure. I made some changes - changing product to category, and brand to product. These labels made more sense to me as I worked with this. I also do full namespaced field names to make joining possible without aliasing all field names.
see the bottom of this post for the SQL to generate and populate these tables
Then a different query will definitely help. You're not joining on the picture table in your code.
select * from so_category
left outer join so_product on ( product_category_id = category_id)
left outer join so_picture on ( picture_category_id = category_id )
But this will probably not return your data like you want to consume it.
In which case the Mysql Group Concat Function might help.
SELET
so_category.*,
so_product.*,
(select GROUP_CONCAT( DISTINCT picture_url order by picture_url asc separator "," )) AS images
FROM so_category
LEFT OUTER JOIN so_product ON ( product_category_id = category_id)
LEFT OUTER JOIN so_picture ON ( picture_category_id = category_id )
GROUP BY product_id
Which yields results that do not duplicate data.
SQL to create and populate tables.
-- Create syntax for TABLE 'so_category'
CREATE TABLE `so_category` (
`category_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`category_name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`category_id`)
) ENGINE=InnoDB CHARSET=utf8;
-- Create syntax for TABLE 'so_picture'
CREATE TABLE `so_picture` (
`picture_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`picture_url` varchar(100) DEFAULT NULL,
`picture_category_id` int(11) unsigned DEFAULT NULL,
PRIMARY KEY (`picture_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- Create syntax for TABLE 'so_product'
CREATE TABLE `so_product` (
`product_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`product_name` varchar(100) DEFAULT NULL,
`product_brand_id` int(11) unsigned DEFAULT NULL,
`product_category_id` int(11) unsigned DEFAULT NULL,
PRIMARY KEY (`product_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `so_category` (`category_id`, `category_name`)
VALUES
(1, 'chocolate'),
(2, 'flower');
INSERT INTO `so_picture` (`picture_id`, `picture_url`, `picture_category_id`)
VALUES
(1, 'dmc1', 1),
(2, 'dmc2', 1),
(3, 'dmc3', 2),
(4, 'dmc4', 2),
(5, 'dmc5', 2);
INSERT INTO `so_product` (`product_id`, `product_name`, `product_brand_id`, `product_category_id`)
VALUES
(1, 'hershey', NULL, 1),
(2, 'tobleron', NULL, 1),
(3, 'oreo', NULL, 1),
(4, 'roses', NULL, 2),
(5, 'sunflower', NULL, 2);

Searching String In All Tables And Displaying It With Pagination Technique

My database has five tables:
Computer|Mobile|Tablet|MusicSystem|Camera
All the tables are in same structure like:
productID|ProductBrand|Price|userId
Here, I want to search product's brand name in all the field of productBrand in all the tables, where userId=$userId and limit is 10 then display it with pagination technique.
How can I create such a query in MySQLi and display it in PHP?
Thanks in advance.
try this...
select * from Computer c,Mobile m,Tablet t,MusicSystem ms,Camera cam where userId = $userId and (c.productBrand = searchingProductBrand or m.productBrand = searchingProductBrand or t.productBrand = searchingProductBrand or ms.productBrand = searchingProductBrand or cam.productBrand = searchingProductBrand ) limit 10
If you are following same structure for each category table,better structure is to categorize all into single table as follows
CREATE TABLE IF NOT EXISTS `category` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`category_name` varchar(255) NOT NULL DEFAULT '',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=6 ;
INSERT INTO `category` (`id`, `category_name`) VALUES
(1, 'Computer'),
(2, 'Mobile'),
(3, 'Tablet'),
(4, 'MusicSystem'),
(5, 'Camera');
CREATE TABLE IF NOT EXISTS `products` (
`ProductId` int(11) NOT NULL AUTO_INCREMENT,
`ProductBrand` varchar(255) NOT NULL,
`Price` varchar(100) NOT NULL,
`UserId` int(11) unsigned NOT NULL,
`CatId` int(11) unsigned NOT NULL,
PRIMARY KEY (`ProductId`),
KEY `CatId` (`CatId`),
CONSTRAINT `products_ibfk_1` FOREIGN KEY (`CatId`) REFERENCES `category` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=6 ;
INSERT INTO `products` (`ProductId`, `ProductBrand`, `Price`, `UserId`, `CatId`) VALUES
(1, 'Lenovo', '35000', 5, 3),
(2, 'Asus', '28350', 5, 2),
(3, 'Dell', '25000', 5, 3),
(4, 'MotoG', '12500', 5, 2),
(5, 'Samsung', '52000', 4, 1);
You can get your result using
SELECT * FROM products WHERE userId = '5'
Check the FIDDLE

Select rows that match right side of one to many reationship in MySql

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)

How would I modify this MySQL Query to find a rank?

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

Executing multiple join with expressions on Zend Framework 2

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');

Categories