I have tables like this.
Categories (id[PK], name, parentid);
Product(prid[PK], product_name, product_price);
ProductCategories(id[PK], prid[FK], catid[FK]);
One product belongs to multiple categories.
I have a scenario, where I will get one catid from a user and I have to get the products belong to that category. Also at the same time, I have to get the subcategories of that category (if any) and get the products of sub categories too. Getting categories and its subcategories is easy task - with self join.
But I have to check that those categories having the products tagged or not. (Means if there is no product tagged under that category/subcategories then neglect that category)
e.g.
Automobile (0 products)
Two Wheelers (0 products)
Mopeds (2 products)
Bikes (5 products)
Sport Bikes (0 products)
Four Wheelers (0 products)
Convertible (0 products)
SUV (4 products)
TUV (2 products)
Tyres (0 products)
So I want the result like (those categories/subcategories don't have products I have to remove those).
Automobile
Two Wheelers
Mopeds
Bikes
Four Wheelers
SUV
TUV
I am doing this thing by looping over the categories. Can I do this in a single query?
Some code :
$rows = (new \yii\db\Query())
->select(["COUNT( * ) AS prodcount",'c1.parentid', "GROUP_CONCAT(c1.id, ':', c1.name) as catid"])
->from('category c1')
->join('inner join','category c2','c1.id=c2.id')
->where(['not in','c1.parentid','0'])
->andWhere(['!=','c1.parentid',1])
->andWhere(array('c1.status'=>1))
->andWhere(array('c2.status'=>1))
->groupBy('c1.parentid')
->orderBy('prodcount DESC')
->all();
$result=array();
foreach ($rows as $r)
{
$cats= explode(":",$r['catid']);
if( $this->hasProducts($cats[0]))
{
if($r['parentid']!=1)
{
$pnm= \backend\models\Category::find()->select('name')->where(['id'=>$r['parentid']])->one();
$result['parent']=$r['parentid'].":".$pnm['name'];
}
else{
$result['parent']=$r['parentid'].":".'Main';
}
$result['catid']=$r['catid'];
$this->cat[$result['parent']]=$result['catid'];
}
}
Here I am checking that category has at least a product or not?
public function hasProducts($catid)
{
$hasProducts=false;
$allCats= array();
$allCats = $this->getAllChildren($catid);
if($allCats!== NULL && !empty($allCats) && sizeOf($allCats)>0)
{
$cats = implode(",",$allCats);
$prodcatquery = (new \yii\db\Query())
->from('product_categories pc')
->where("pc.catid in ($cats)");
$products= $prodcatquery->all();
if (sizeOf($products)>0)
{
$hasProducts=true;
}
}
return $hasProducts;
}
Getting all subcategories of that category
public function getAllChildren($catid)
{
$cats=$catid;
$allcats=array();
$currentcats=array();
array_push($allcats, $catid);
$intialquery = (new \yii\db\Query())
->select(['id'])
->from('category')
->where("parentid in ($cats)");
$catidreturned = $intialquery->all();
$i=0;
while(sizeOf($catidreturned ) > 0 && $i <=3 )
{
foreach ($catidreturned as $categoryid )
{
array_push( $allcats,$categoryid['id']);
array_push( $currentcats,$categoryid['id']);
}
$cats= implode(',', $currentcats);
$intialquery1 = (new \yii\db\Query())
->select(['id'])
->from('category')
->where("parentid in ($cats)");
$catidreturned = $intialquery1->all();
$currentcats=array();
$i++;
}
return $allcats;
}
Question: I am doing this thing by looping over the categories. Can I do this in a single query?
答案 0 :(得分:0)
select products from categories where category=1 or subcategory in(select subcategory from sub_categories where id=1)
modify this depending on what is your tables looks like
答案 1 :(得分:0)
SELECT *
FROM (
SELECT
CASE
WHEN c2_id IS NULL THEN 1
WHEN c3_id IS NULL THEN 2
ELSE 3
END AS level,
CASE
WHEN c2_id IS NULL THEN c1_id
WHEN c3_id IS NULL THEN c2_id
ELSE c3_id
END AS id,
CASE
WHEN c2_id IS NULL THEN c1_name
WHEN c3_id IS NULL THEN c2_name
ELSE c3_name
END AS name,
CASE
WHEN c2_id IS NULL THEN c1_own_products_count
WHEN c3_id IS NULL THEN c2_own_products_count
ELSE c3_own_products_count
END AS own_products_count,
CASE
WHEN c2_id IS NULL THEN c1_nested_products_count
WHEN c3_id IS NULL THEN c2_nested_products_count
ELSE c3_nested_products_count
END AS nested_products_count
FROM (
SELECT
-- Level 1
c1.id AS c1_id,
c1.name AS c1_name,
COUNT(DISTINCT c1p.id) AS c1_own_products_count,
COUNT(DISTINCT c1p.id)+
COUNT(DISTINCT c2p.id)+
COUNT(DISTINCT c3p.id) AS c1_nested_products_count,
-- Level 2
c2.id AS c2_id,
c2.name AS c2_name,
COUNT(DISTINCT c2p.id) AS c2_own_products_count,
COUNT(DISTINCT c2p.id)+
COUNT(DISTINCT c3p.id) AS c2_nested_products_count,
-- Level 3
c3.id AS c3_id,
c3.name AS c3_name,
COUNT(DISTINCT c3p.id) AS c3_own_products_count,
COUNT(DISTINCT c3p.id) AS c3_nested_products_count
FROM Categories c1
LEFT JOIN Categories c2 ON(c2.parentid = c1.id)
LEFT JOIN Categories c3 ON(c3.parentid = c2.id)
LEFT JOIN ProductCategories c1p ON(c1p.catid=c1.id)
LEFT JOIN ProductCategories c2p ON(c2p.catid=c2.id)
LEFT JOIN ProductCategories c3p ON(c3p.catid=c3.id)
GROUP BY c1.id, c2.id, c3.id
WITH ROLLUP -- This will generate subtotals for level 1 and 2
) AS tree
WHERE c1_id IS NOT NULL -- Skip the row with total product count.
) AS list
WHERE nested_products_count = 0 -- Skip categories with no nested products