MySQL query to order by parent then child places - php

I have a table of pages in my database, each page can have a parent as below:
id parent_id title
1 0 Home
2 0 Sitemap
3 0 Products
4 3 Product 1
5 3 Product 2
6 4 Product 1 Review Page
What would be the best MySQL query to select all pages ordered by parent then child then child again if there is more than one level, there will be a maximum of three levels. The above example would produce the desired order:
Home
Sitemap
Products
Product 1
Product 1 Review Page
Product 2

If you have to stick with your model, i suggest this query:
SELECT p.id, p.title,
(
SELECT LPAD(parent.id, 5, '0')
FROM page parent
WHERE parent.id = p.id AND parent.parent_id = 0
UNION
SELECT CONCAT(LPAD(parent.id, 5, '0'), '.', LPAD(child.id, 5, '0'))
FROM page parent
INNER JOIN page child ON (parent.id = child.parent_id)
WHERE child.id = p.id AND parent.parent_id = 0
UNION
SELECT CONCAT(LPAD(parent.id, 5, '0'), '.', LPAD(child.id, 5, '0'), '.', LPAD(grandchild.id, 5, '0'))
FROM page parent
INNER JOIN page child ON (parent.id = child.parent_id)
INNER JOIN page grandchild ON (child.id = grandchild.parent_id)
WHERE grandchild.id = p.id AND parent.parent_id = 0
) AS level
FROM page p
ORDER BY level;
Example of result set:
+-----+-------------------------+-------------------+
| id | title | level |
+-----+-------------------------+-------------------+
| 1 | Home | 00001 |
| 2 | Sitemap | 00002 |
| 3 | Products | 00003 |
| 4 | Product 1 | 00003.00004 |
| 6 | Product 1 Review Page 1 | 00003.00004.00006 |
| 646 | Product 1 Review Page 2 | 00003.00004.00646 |
| 5 | Product 2 | 00003.00005 |
| 644 | Product 3 | 00003.00644 |
| 645 | Product 4 | 00003.00645 |
+-----+-------------------------+-------------------+
9 rows in set (0.01 sec)
Output of EXPLAIN:
+------+--------------------+--------------+--------+---------------+---------+---------+--------------------------+------+----------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+--------------------+--------------+--------+---------------+---------+---------+--------------------------+------+----------------+
| 1 | PRIMARY | p | ALL | NULL | NULL | NULL | NULL | 441 | Using filesort |
| 2 | DEPENDENT SUBQUERY | parent | eq_ref | PRIMARY,idx1 | PRIMARY | 4 | tmp.p.id | 1 | Using where |
| 3 | DEPENDENT UNION | child | eq_ref | PRIMARY,idx1 | PRIMARY | 4 | tmp.p.id | 1 | |
| 3 | DEPENDENT UNION | parent | eq_ref | PRIMARY,idx1 | PRIMARY | 4 | tmp.child.parent_id | 1 | Using where |
| 4 | DEPENDENT UNION | grandchild | eq_ref | PRIMARY,idx1 | PRIMARY | 4 | tmp.p.id | 1 | |
| 4 | DEPENDENT UNION | child | eq_ref | PRIMARY,idx1 | PRIMARY | 4 | tmp.grandchild.parent_id | 1 | |
| 4 | DEPENDENT UNION | parent | eq_ref | PRIMARY,idx1 | PRIMARY | 4 | tmp.child.parent_id | 1 | Using where |
| NULL | UNION RESULT | <union2,3,4> | ALL | NULL | NULL | NULL | NULL | NULL | |
+------+--------------------+--------------+--------+---------------+---------+---------+--------------------------+------+----------------+
8 rows in set (0.00 sec)
I used this table layout:
CREATE TABLE `page` (
`id` int(11) NOT NULL,
`parent_id` int(11) NOT NULL,
`title` varchar(255) default NULL,
PRIMARY KEY (`id`),
KEY `idx1` (`parent_id`)
);
Note that I included an index on parent_id to improve performance.

I think you should put one more field in your table, called level and store in it the level of the node, and then sort your query by level then by parent.

If you have some control over the table schema, you may want to consider using a nested set representation instead. Mike Hillyer wrote an article on this:
Managing Hierarchical Data in MySQL

work smarter not harder:
SELECT menu_name , CONCAT_WS('_', level3, level2, level1) as level FROM (SELECT
t1.menu_name as menu_name ,
t3.sorting AS level3,
t2.sorting AS level2,
t1.sorting AS level1
FROM
en_menu_items as t1
LEFT JOIN
en_menu_items as t2
on
t1.parent_id = t2.id
LEFT JOIN
en_menu_items as t3
on
t2.parent_id = t3.id
) as depth_table
ORDER BY
level
that is it..

Ugh. Queries like this involving trees are annoying and generally, if you want it to be scalable to any number of levels, you won't do it with a single query, you'll use a few building up the tree at each level.

Well, you can always get it all in one query and process it in PHP. That would be probably the simplier way to get a tree.

Related

Managing Hierarchical Data in MySQL using The Adjacency List Model

I would like to retrieve all categories with parent in order to create a breadcrumb path. for that reason I create the following schema:
CREATE TABLE category(
category_id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(20) NOT NULL,
parent INT DEFAULT NULL
);
INSERT INTO category VALUES(1,'ELECTRONICS',NULL),(2,'TELEVISIONS',1),(3,'TUBE',2),
(4,'LCD',2),(5,'PLASMA',2),(6,'PORTABLE ELECTRONICS',1),(7,'MP3 PLAYERS',6),(8,'FLASH',7),
(9,'CD PLAYERS',6),(10,'2 WAY RADIOS',6);
SELECT * FROM category ORDER BY category_id;
+-------------+----------------------+--------+
| category_id | name | parent |
+-------------+----------------------+--------+
| 1 | ELECTRONICS | NULL |
| 2 | TELEVISIONS | 1 |
| 3 | TUBE | 2 |
| 4 | LCD | 2 |
| 5 | PLASMA | 2 |
| 6 | PORTABLE ELECTRONICS | 1 |
| 7 | MP3 PLAYERS | 6 |
| 8 | FLASH | 7 |
| 9 | CD PLAYERS | 6 |
| 10 | 2 WAY RADIOS | 6 |
+-------------+----------------------+--------+
10 rows in set (0.00 sec)
With the example I follow I can retrieve the information using the following SQL:
SELECT t1.name AS lev1, t2.name as lev2, t3.name as lev3, t4.name as lev4
FROM category AS t1
LEFT JOIN category AS t2 ON t2.parent = t1.category_id
LEFT JOIN category AS t3 ON t3.parent = t2.category_id
LEFT JOIN category AS t4 ON t4.parent = t3.category_id
WHERE t1.name = 'ELECTRONICS';
The problem I found is before being able to see the full path of a category we have to know the level at which it resides.
my question, is there a way to retrieve the information as the following example but not defining the exact amount of levels?
+-------------+----------------------+-------------+-------+
| lev1 | lev2 | lev3 | lev4 |
+-------------+----------------------+-------------+-------+
| ELECTRONICS | PORTABLE ELECTRONICS | MP3 PLAYERS | FLASH |
my idea is to retrieve lev1 or lev2 etc taking into consideration the depth level.
UPDATE:
EXAMPLE OUTPUT
ELECTRONICS
ELECTRONICS / TELEVISIONS
ELECTRONICS / TELEVISIONS / TUBE
ELECTRONICS / TELEVISIONS / LCD
ETC
ETC
There is a workaround.
You can add collateral PATH column. The column should have chain of ids from the elemetn to parent. Thus for the root the column empty. All the children of root has _
+-------------+----------------------+--------+--------+
| category_id | name | parent | path |
+-------------+----------------------+--------+--------+
| 1 | ELECTRONICS | NULL | |
| 2 | TELEVISIONS | 1 |1_ |
| 3 | TUBE | 2 |1_2_ |
| 4 | LCD | 2 |1_2_ |
| 5 | PLASMA | 2 |1_2_ |
| 6 | PORTABLE ELECTRONICS | 1 |1_ |
| 7 | MP3 PLAYERS | 6 |1_6_ |
| 8 | FLASH | 7 |1_6_7_ |
| 9 | CD PLAYERS | 6 |1_6_ |
| 10 | 2 WAY RADIOS | 6 |1_6_ |
+-------------+----------------------+--------+--------+
On insert a new node you just copy parent node path and add '_'
So to retrieve all children of a node you just use
SELECT *
FROM THE_TABLE
WHERE PATH LIKE '<parent node path>%'
There is a restriction of the field size and amount of levels though

How to get distinct count of many fields in one row using best possible method

I have 2 tables in my demos database, in which I join them based on 2 keys, here I would like to count distinct of matched result,
my first table
MariaDB [demos]> select * from main_info;
+------+------+-------+-------+----------+
| key1 | key2 | info1 | info2 | date |
+------+------+-------+-------+----------+
| 1 | 1 | 15 | 90 | 20120501 |
| 1 | 2 | 14 | 92 | 20120601 |
| 1 | 3 | 15 | 82 | 20120801 |
| 2 | 1 | 17 | 90 | 20130302 |
| 2 | 2 | 16 | 88 | 20130601 |
+------+------+-------+-------+----------+
5 rows in set (0.00 sec)
And my second table
MariaDB [demos]> select * from product1;
+------+------+--------+--------------+
| key1 | key2 | serial | product_data |
+------+------+--------+--------------+
| 1 | 1 | 0 | NaN |
| 1 | 1 | 1 | NaN |
| 1 | 1 | 2 | NaN |
| 1 | 1 | 3 | NaN |
| 1 | 2 | 0 | 12.556 |
| 1 | 2 | 1 | 13.335 |
| 1 | 3 | 1 | NaN |
| 1 | 3 | 2 | 13.556 |
| 1 | 3 | 3 | 14.556 |
| 2 | 1 | 0 | 12.556 |
| 2 | 1 | 1 | 13.553 |
| 2 | 1 | 2 | NaN |
+------+------+--------+--------------+
12 rows in set (0.00 sec)
So distinct count of field serial is as follows, where serial is not zero
MariaDB [demos]> select count(distinct a.key1,a.key2) as serial_count from main_info a,product1 b where a.key1=b.key1 and a.key2=b.key2 and b.serial !=0;
+--------------+
| serial_count |
+--------------+
| 4 |
+--------------+
1 row in set (0.00 sec)
and distinct count of field product_data is as follows where product_data is not NaN
MariaDB [demos]> select count(distinct a.key1,a.key2) as product_count from main_info a,product1 b where a.key1=b.key1 and a.key2=b.key2 and b.product_data !='NaN';
+---------------+
| product_count |
+---------------+
| 3 |
+---------------+
1 row in set (0.00 sec)
In my original application I have many columns in my table, finally I would like to get result like below using single query, at present I am doing multiple query using PHP which is taking long time because for example if I have 100 columns, I have to execute function 100 times like above, so please someone guide me to speed up my application
+--------------+---------------+
| serial_count |product_count |
+--------------+---------------+
| 4 | 3 |
+--------------+---------------+
Below is structure of tables
DROP TABLE IF EXISTS `main_info`;
CREATE TABLE `main_info` (
`key1` int(11) NOT NULL,
`key2` int(11) NOT NULL,
`info1` int(11) NOT NULL,
`info2` int(11) NOT NULL,
`date` int(11) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
LOCK TABLES `main_info` WRITE;
INSERT INTO `main_info` VALUES (1,1,15,90,20120501),(1,2,14,92,20120601),(1,3,15,82,20120801),(2,1,17,90,20130302),(2,2,16,88,20130601);
UNLOCK TABLES;
DROP TABLE IF EXISTS `product1`;
CREATE TABLE `product1` (
`key1` int(11) NOT NULL,
`key2` int(11) NOT NULL,
`serial` int(11) NOT NULL,
`product_data` varchar(1000) DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
LOCK TABLES `product1` WRITE;
INSERT INTO `product1` VALUES (1,1,0,'NaN'),(1,1,1,'NaN'),(1,1,2,'NaN'),(1,1,3,'NaN'),(1,2,0,'12.556'),(1,2,1,'13.335'),(1,3,1,'NaN'),(1,3,2,'13.556'),(1,3,3,'14.556'),(2,1,0,'12.556'),(2,1,1,'13.553'),(2,1,2,'NaN');
UNLOCK TABLES;
I didn't understand this why datetype changed, if I run on terminal
$ mysql -u root -p demos -e 'select key1,if(key1 !=0,key1,999.999) as `test1` from main_info'
Enter password:
+------+-------+
| key1 | test1 |
+------+-------+
| 1 | 1.000 |
| 1 | 1.000 |
| 1 | 1.000 |
| 2 | 2.000 |
| 2 | 2.000 |
+------+-------+
I expect it should be integer if IF condition is true
You can do it in a single query using conditional aggregation:
select count(distinct if(b.product_data !='NaN',a.key1, null),
if(b.product_data !='NaN',a.key2, null)) as product_count,
count(distinct if(b.serial !=0,a.key1, null),
if(b.serial !=0,a.key2, null)) as serial_count
from main_info a
inner join product1 b on a.key1=b.key1 and a.key2=b.key2
Output:
product_count serial_count
-----------------------------
3 4
Demo here
Edit: Credit goes to #Paul Spiegel
The query can be simplified using CONCAT:
select count(distinct if(b.product_data !='NaN',
CONCAT(a.key1, ',', a.key2),
null)) as product_count,
count(distinct if(b.serial !=0,
CONCAT(a.key1, ',', a.key2),
null)) as serial_count
from main_info a
inner join product1 b on a.key1=b.key1 and a.key2=b.key2

How can I create this mysql "SELECT" query?

I have two mysql tables named 'categories' and 'products'.
These are data of that tables.
mysql> select * from categories;
+-------------+--------+----------------------+-------------+
| category_id | parent | name | description |
+-------------+--------+----------------------+-------------+
| 1 | NULL | Products | NULL |
| 2 | 1 | Computers | NULL |
| 3 | 2 | Laptops | NULL |
| 4 | 2 | Desktop Computers | NULL |
| 5 | 2 | Tab PCs | NULL |
| 6 | 2 | CRT Monitors | NULL |
| 7 | 2 | LCD Monitors | NULL |
| 8 | 2 | LED Monitors | NULL |
| 9 | 1 | Mobile Phones | NULL |
| 10 | 9 | LG Phone | NULL |
| 11 | 9 | Anroid Phone | NULL |
| 12 | 9 | Windows Mobile | NULL |
| 13 | 9 | iPad | NULL |
| 14 | 9 | Samsung Galaxy | NULL |
| 15 | 1 | Digital Cameras | NULL |
| 16 | 1 | Printers and Toners | NULL |
| 22 | 1 | Computer Accessaries | NULL |
| 23 | 22 | USB Cables | NULL |
| 24 | 22 | Network Cables | NULL |
+-------------+--------+----------------------+-------------+
24 rows in set (0.00 sec)
mysql> select product_id, category_id from products;
+------------+-------------+
| product_id | category_id |
+------------+-------------+
| 1 | 24 |
| 2 | 6 |
| 3 | 6 |
| 4 | 6 |
+------------+-------------+
4 rows in set (0.05 sec)
Now I need to create a select query to get products to each category. I have already have category_id.
This is how I tried it:
SELECT * FROM products
WHERE category_id = 6
ORDER BY added_date DESC
My question is when I creating this select query I need all the products if one category have its subcategories. That mean, if category_id is 2, then I need to get all the products including its sub categories.
Can anybody tell me how I create this select query?
Thank you.
SELECT *
FROM products
WHERE category_id = 2 OR
category_id IN (SELECT category_id
FROM categories
WHERE parent = 2)
ORDER BY added_date DESC
try this.
select a.*,b.* from categories a inner join products b on a.category_id = b.category_id where a.category_id='$Category_id' or a.parent_id='$category_id'
You can do it using a sub-query to get category_id values whose parent is 2:
SELECT *
FROM products
WHERE category_id = 2 OR
category_id IN (SELECT category_id
FROM categories
WHERE parent = 2)
ORDER BY added_date DESC
All of the above answers will work but only for one level deep of categories. If you need sub-sub-categories then I'd suggest using something called MPTT. This will require 2 new fields in your categories database table though (typically called left and right).

Matching column names in DB using JOIN NULL values

I am trying to create an "eBay" style category menu where when the user selects an item from a multiselect field it will add a new multiselect with any sub categories and if the sub category contains sub categories add a new multi select with the sub sub categories.
I have this mostly working but I am having trouble with the database side of things. I have a main table called categories that contains two columns CatID and Category. There are three rows in the categories table "Motorcycles", "Powersports" and "Parts & Accessories"
When a user selects Motorcycles it should create a new multi select and bring up Motorcycle Brands. If on the other hand the user selects Powersports it should create a new multi dropdown and list Powersport vehicle types "Dirtbike", "PWC", "Snowmobile" etc. Which upon selecting one of those will bring up a Powersports->Vehicle Type->Brand multiselect.
The problem is I don't know how to reference all of this information properly in the database.
For example:
Main Categories(categories)
+-------+---------------------+
| CatID | Category |
+-------+---------------------+
| 1 | Motorcycles |
| 3 | Parts & Accessories |
| 2 | Powersports |
+-------+---------------------+
Motorcycles Sub Category: (motorcycle_brands)
+-------+-------+-------------------------+
| CatID | SubID | SubName |
+-------+-------+-------------------------+
| 1 | 1 | American Classic Motors |
| 1 | 2 | American Ironhorse |
| 1 | 3 | Aprilia |
| 1 | 4 | Benelli |
| 1 | 5 | Big Dog |
+-------+-------+-------------------------+
Power Sports Sub Category: (powersport_categories)
+-------+-------+--------------------------------+----+
| CatID | SubID | SubName | ID |
+-------+-------+--------------------------------+----+
| 2 | 1 | ATVs | 1 |
| 2 | 2 | Dune Buggies / Sand Rails | 2 |
| 2 | 3 | Go Karts: High-Performance | 3 |
| 2 | 4 | Personal Watercraft | 4 |
| 2 | 5 | Powersport Vehicles Under 50cc | 5 |
+-------+-------+--------------------------------+----+
So if I run the following command:
SELECT * FROM categories C
LEFT JOIN motorcycle_brands MB ON MB.CatID = C.CatID
LEFT JOIN powersport_categories PC ON PC.CatID = C.CatID
WHERE C.CatID = 1 LIMIT 5;
I get this:
+-------+-------------+-------------------+-------+-------+-------------------------+-------+-------+---------+------+
| CatID | Category | CatDBTable | CatID | SubID | SubName | CatID | SubID | SubName | ID |
+-------+-------------+-------------------+-------+-------+-------------------------+-------+-------+---------+------+
| 1 | Motorcycles | motorcycle_brands | 1 | 1 | American Classic Motors | NULL | NULL | NULL | NULL |
| 1 | Motorcycles | motorcycle_brands | 1 | 2 | American Ironhorse | NULL | NULL | NULL | NULL |
| 1 | Motorcycles | motorcycle_brands | 1 | 3 | Aprilia | NULL | NULL | NULL | NULL |
| 1 | Motorcycles | motorcycle_brands | 1 | 4 | Benelli | NULL | NULL | NULL | NULL |
| 1 | Motorcycles | motorcycle_brands | 1 | 5 | Big Dog | NULL | NULL | NULL | NULL |
+-------+-------------+-------------------+-------+-------+-------------------------+-------+-------+---------+------+
Notice all the NULL values at the end. Is there a way to get rid of these null values? It is causing other problems in my code because when it returns this array back to my PHP script the SubID and SubName array fields are blank since it goes with the last set which are NULL. However if I run the same query as above replacing 1 with 2 then I get the values I want back since they are not over written by duplicate column names.
Maybe I am going about this all wrong I was planning on having the same SubID and SubName columns in all of my tables for sub categories but it looks like it may cause me grief. The reason I did this is because otherwise I was having to write a separate query depending on what value was selected I wanted to have basically one query that would do it all essentially for each sub option. Is there any improvements here?
1) Try something like this (you should also check my other point below):
SELECT C.CatID, C.Category, C.CatDBTable, MB.SubID AS MbSubID, MB.SubName AS MbSubName, PC.SubID AS PcSubID, PC.SubName AS PcSubName, PC.ID AS PcID FROM categories C
LEFT JOIN motorcycle_brands MB ON MB.CatID = C.CatID
LEFT JOIN powersport_categories PC ON PC.CatID = C.CatID
WHERE C.CatID = 1 LIMIT 5;
2) Your database structure might be made better and more extensible (if you needed for exemple to add a 4th degree sub) if you used the following approach :
Have only one table name categories which has the following columns :
ID
parent_id (nullable)
depth
name
you will use it as follows:
"Motorcycles" which is a main category will have: (ID => 1, parent_id => null, depth => 0, name => Motorcycles)
"American Classic Motors" which is a sub of "Motorcycles" will have: (ID => 4, parent_id => 1, depth => 1, name => American Classic Motors)
"ATVs" which is a sub of "American Classic Motors" will have: (ID => 5, parent_id => 4, depth => 2, name => ATVs)
Try this:
SELECT C.CatID,C.Category,MB.CatID as MBCatID,MB.SubID as MBSubID,MB.SubName as MBSubName,IFNULL(PC.CatID,'') as PCCatID,IFNULL(PC.SubID,'') as PcSubID,IFNULL(PC.SubName,'') as PcSubName,IFNULL(PC.ID,'') as PCID FROM categories C
LEFT JOIN motorcycle_brands MB ON MB.CatID = C.CatID
LEFT JOIN powersport_categories PC ON PC.CatID = C.CatID
WHERE C.CatID = 1 LIMIT 5;
See the result in SQL Fiddle

mysql multi group query with count

I have this query
SELECT
products_list.id_cat_unique,
products_categories_list.*,
COUNT(products_list.id_cat_unique) as counter
FROM products_categories_ids
LEFT JOIN products_list
ON products_categories_ids.id_cat_unique = products_list.id_cat_unique
LEFT JOIN products_categories_list
ON products_categories_list.id_cat_unique = products_categories_ids.id_cat_unique
GROUP BY products_categories_list.name_cat
ORDER BY products_categories_list.name_cat ASC
This is complicated to explain without having the tables on your screen but I'll try
what I want is count all the rows from products_list that can be associated with max two rows per id_cat_unique (contained in products_categories_list) that are English or Italian. This two items each could have infinite id_products in products_list of course.
I want the result to be grouped by products_categories_list.name_cat and by the language.
What I get with this query is categories grouped by name/unique id which is correct, the problem is that the counter value for both English and Italian turns out as the sum of all the products associated with one id_cat_unique regardless of the language. So if I have 12 rows from one category in English and 3 for the same category in Spanish I get 15 as counter for both the languages.
EDIT ADDING TABLES STRUCTURES
products_list
+-------------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------------+--------------+------+-----+---------+----------------+
| id_product | int(11) | NO | PRI | NULL | auto_increment |
| id_product_unique | int(5) | NO | | NULL | |
| lang | varchar(2) | NO | | NULL | |
| name_product | varchar(200) | NO | | NULL | |
| desc_product | text | NO | | NULL | |
| id_cat_unique | int(2) | NO | | NULL | |
| status | int(1) | NO | | NULL | |
+-------------------+--------------+------+-----+---------+----------------+
12 rows in set (0.00 sec)
products_categories_ids
+---------------+--------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+--------+------+-----+---------+----------------+
| id_cat_unique | int(5) | NO | PRI | NULL | auto_increment |
+---------------+--------+------+-----+---------+----------------+
1 row in set (0.00 sec)
products_categories_list
+---------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+--------------+------+-----+---------+----------------+
| id_cat | int(5) | NO | PRI | NULL | auto_increment |
| id_cat_unique | int(2) | NO | | NULL | |
| lang | varchar(2) | NO | | NULL | |
| name_cat | varchar(500) | NO | | NULL | |
| date_created | int(11) | NO | | NULL | |
| date_modified | int(11) | NO | | NULL | |
+---------------+--------------+------+-----+---------+----------------+
6 rows in set (0.01 sec)
result from query
| id_cat_unique | id_cat | id_cat_unique | lang | name_cat | date_created | date_modified | counter |
+---------------+--------+---------------+------+--------------------------------+--------------+---------------+---------+
| 1 | 18 | 1 | it | Carne di suino | 1308267538 | 1308267538 | 6 |
| 14 | 21 | 14 | it | Guanciali | 1308777322 | 1308777322 | 2 |
| 3 | 20 | 3 | it | Pollo a pezzi | 1308267892 | 1308267892 | 2 |
| 1 | 22 | 1 | en | Pork meat | 1308267538 | 1312383232 | 6 |
| 14 | 23 | 14 | en | Sheeps | 1308777322 | 1312383220 | 2 |
| 2 | 19 | 2 | it | That's a "test" | 1308267538 | 1308267538 | 7 |
+---------------+--------+---------------+------+--------------------------------+--------------+---------------+---------+
6 rows in set (0.00 sec)
"Pork meat" and "Carne di Suino" have respectively 1 and 5 products (from the same category but different language, i wanna group the counting by language too) instead it shows 6 for both
NEW question
I'd like to use this query for many purposes without using different things, so accordingly if I want to retrieve the categories with ONE language I'd add one where clause
select pcl.*,
(select count(*) from products_list pl
where pcl.id_cat_unique = pl.id_cat_unique
and pcl.lang = pl.lang) as counter
from products_categories_list pcl
where products_list.lang = 'en' <- added
ORDER BY pcl.name_cat ASC
I get Unknown column 'products_list.lang' in 'where clause'. Why?
I THINK this will work, if not, it's going to be close, let me know what you get, and I can modify it. I've modified the query around. Would this work?
select pcl.*,
(select count(*) from products_list pl
where pcl.id_cat_unique = pl.id_cat_unique
and pcl.lang = pl.lang) as counter
from products_categories_list pcl
ORDER BY pcl.name_cat ASC
The PCL and PL are just aliases for the tables, so I didn't have to always write out products_categories_list and products_list. You specify the alias in the from part of the statement, so from products_list pl allows you to refer to products_list as pl in the rest of the query.
The idea behind this is that you have a unique row for each products_categories_list and you want a count of a secondary table. This could have been done by group by or by a sub query. I put the correlated subquery in to count the number of rows in products_list that matched the products_categories_list and language, since you are getting all the columns out of the products_categories_list. The correlated subquery allows you to get a single value (count(*)) from a nested query.
Probably this: add DISTINCT
COUNT(DISTINCT products_list.id_cat_unique) as counter
This will ignore duplicate, so the 12+3 via the JOIN becomes 1+1

Categories