MySQL Order by, then Update Inner Join - php

I have a table categories which contains the columns ID & ParentID I would like to add a field called Level, which states which level in the category tree each category is.
I think i found my solution but it is in sql not mysql. So i have been converting it to the correct syntax. However, i think im missing a step. So, here is my code:
ALTER TABLE categories DROP Level;
ALTER TABLE categories ADD Level INT NULL;
UPDATE categories
SET Level = 0
WHERE ParentID IS NULL;
UPDATE categories AS A
INNER JOIN categories B ON A.ParentID = B.ID
SET A.Level = B.Level + 1
WHERE A.Level IS NULL AND
B.Level IS NOT NULL;
I think the problem may lie in the fact that in my DB The order of the categories do not come in any specific order, what i mean is as follows:
ID ParentID
2 NULL 0
4 55
7 2
.....more categories
55 2
So what i would like it do do is:
ID Parent Level
2 NULL 0
3 55 2
7 2 1
....
55 2 1
However, i think, but i might be wrong, is that i need to either order by ParentID first before i do the last operation, or my query is missformed.
I am not getting any errors however, but just not getting the results i am expecting this is what im getting;
ID Parent Level
2 NULL 0
3 55 NULL
7 2 1
....
55 2 1
Any ideas?

The question is how many levels do u have?
If they are 3 levels than u can do it like this
ALTER TABLE categories DROP Level;
ALTER TABLE categories ADD Level INT NULL;
UPDATE categories SET Level = 0 WHERE ParentID IS NULL;
UPDATE categories SET level = 1 where parentID = 2;
UPDATE categories SET level = 2 where parentID > 2;

Related

MySQL/MariaDB recursive statement, Return empty if children do not match condition

I have tables like this in the database:
Category
id_category
id_parent
level_depth
1
0
0
2
1
1
20
2
2
21
2
2
22
2
2
30
22
3
category_product
id_category
id_product
2
200
2
201
2
202
20
202
20
203
20
204
I have this statement that deletes the categories that have no products.
Db::getInstance()->execute('UPDATE '._DB_PREFIX_.'category SET `active` = 0 WHERE `id_category` NOT IN(SELECT `id_category` FROM '._DB_PREFIX_.'category_product)');
But this doesn't work for me because I don't want to delete parent categories if the child categories do have products.
I would like to get the categories that have no products where all of their children also have no products.
I've been experimenting with recursive select statements, but I would need it to return nothing in case any children have a product. This query is not correct because it returns all the categories that do not have any product and also all the children that do not have a product.
with recursive decendents as (
-- Category that has no Products
Select c.id_category
FROM category c
WHERE c.id_category NOT IN(SELECT id_category
FROM category_product)
join all
-- child categories
SELECT c.id_category
FROM category c, descendants d
WHERE c.id_parent = d.id_category AND c.id_category NOT IN(SELECT id_category
FROM category_product)
)
SELECT id_category From descendants
Any idea how I can get this? either with recursive query or not.
Thanks.
In mysql you can write update statement as follows
UPDATE category c JOIN category_product cp ON cp.`id_category` = c.`id_category` SET c.`active` = 0
and if your execute it from a tool make sure
sql_safe_updates
set o
as we are updating with out primary key

How can i use sql count in these multiple tables?

I am still a php/mysql newbie and I am working on mysql table relationship concept and i am having an issue with using mysql count in multiple table. Here is my db structure.
**product table**
id product_name product_img groupeid
1 Sneaker Mark sneaker_adi.png 1
2 bag Eric bageric.png 2
3 Sneaker Etoi sneakeretoi.jpg 1
**groupe table**
group_id group_name
1 men
2 women
**category table**
catid catname
1 sneaker-shoes
2 bag-woman
**productcategory table**
prod_id cat_ID
1 1
2 2
3 1
What i want to do is to determine the number of sneaker-shoes using mysql.
We can see that the number of sneaker-shoes in the db is 2.
But how can i use **count()** in these multiple tables.
I tried like this;
$sql = "SELECT COUNT(*) product.id,product_name,catname FROM product INNER JOIN productcategory ON product.id = prod_id INNER JOIN category ON catid = cat_ID WHERE catname='sneaker-shoes'";
i got error like:
Fatal error: Call to a member function execute() on a non-object in C:\wamp\www\kbashopping\Homme\index.php on line 32
Hope i exposed the issue clearly, any help and assistance will be appreciate
Thanks
If you are looking only for the count, mention only the count phrase in the Select clause.
Change :
SELECT COUNT(*) product.id,product_name,catname FROM
to :
SELECT COUNT(product.id) FROM
SELECT count (pc.cat_ID) FROM productcategory pc inner join category c on c.catid = pc.cat_ID where c.catname = 'sneaker shoes';
This will build a temporary table in mysql that joins category and product category but only including results where the catname is sneaker shoes. Then it selects a column to run the count operation on, and returns the result of count.

Not getting required result after joining 2 tables

I have a view where i need to get list of all dishes from menu table according to main_menuid for a particular user, But here i need to put some conditions
Currently i am using 3 tables for the entire process (table view for all 3 is given at the end)
1) main_menu
2) menu
3) cart
1) I need to get list of dishes according to main_menuid
so here if i need a list of dishes below MM1, so list of dishes that i should get from menu table is (main_menuid is the id of main_menu table)
id main_menuid dish
1 1 D1
2 1 D2
3 1 D3
code i used to get the above data is
$sql = "select * from menu where main_menuid = '".$mainmenuid."'";
$result = mysqli_query($con, $sql);
if (mysqli_num_rows($result) > 0)
{
while($row = mysqli_fetch_assoc($result))
{
print_r($row);
}
}
2) i will also have a userid with me so now i need to display the above list to a particular user (Let the userid be "1"), but if he already has added any of the above product to cart earlier then it's quanity should also get fetched from cart and for rest of the items(that are not added by him to the cart but is present in the above list) the quantity will be null, so the resulting view should be like this (menuid is the id of menu table)
id main_menuid menuid dish quantity userid
1 1 1 D1 3 1
2 1 1 D2 2 1
3 1 1 D3 0 1
code that i tried for the above result is (but it didn't gave me the desired result)
SELECT menu.main_menuid, menu.dish, cart.userid,
cart.quantity
FROM menu
LEFT JOIN cart ON menu.id=cart.menuid
WHERE main_menuid = '".$main_menuid."' and userid = '".$userid."'
Below is a sample view of all the tables that are being used in the above process
main menu
id mainmenu_name
1 MM1
2 MM2
3 MM3
4 MM4
menu
id main_menuid dish
1 1 D1
2 1 D2
3 1 D3
4 2 D4
5 3 D5
6 4 D6
cart
id userid menuid dish main_menuid quantity
1 1 1 D1 1 3
2 1 2 D2 1 2
3 2 1 D1 1 3
can anyone please tell how to achieve the desired result (DBMS: mysql)
move userID to the join as it is eliminating the null values generated from the left join.
SELECT menu.main_menuid, menu.menuid, menu.dish, cart.userid,
cart.quantity
FROM menu
LEFT JOIN cart ON menu.id=cart.menuid
and userid = '".$userid."'
WHERE main_menuid = '".$main_menuid."'
However this implies you want some dishes not associated to the user so userID will be NULL in some cases.
Why does this work and not in the where clause? Because the filter is applied BEFORE the join. This allows the null values which would occur as a result of the left join to remain.
You can't get userid from your cart table directly, if the desired menu item is not in cart. So your WHERE clause filters out these records. Try this request:
SELECT menu.main_menuid, menu.menuid, menu.dish, IF(ISNULL(cart.userid), ".$main_menuid.", cart.userid) AS userid,
IF(ISNULL(cart.quantity), 0, cart.quantity) AS quantity
FROM menu
LEFT JOIN cart ON menu.id=cart.menuid
WHERE menu.main_menuid = '".$main_menuid."'
I think the problem is on the LEFT JOIN. You should do this way:
LEFT JOIN cart ON menu.id=cart.menuid and cart.dish = menu.dish
EDIT
When you do this way, the userid column will be null at the last line, to avoid this, on select you should put something like this:
SELECT menu.main_menuid, menu.dish,
COALESCE(cart.quantity, 0), -- will put 0 when null
COALESCE(cart.userid, '".$userid."') as userid -- will put '".$userid."' when null

Shortest string within same path (branch)

I have a tree representation in mysql table based on id, depth, parent_id and path. Every root record within this table has a depth of 0, parent_id != null and path representation based on hex value of ID padded left with 0.
Every element of the tree is constructed by specifying depth = parent.depth + 1, path = parent.path + hex(id), parent_id = parent.id (pseudo code) for example:
id path depth parent_id assigned_user_id
------------------------------------------------------------
1 001 0 NULL NULL
2 002 0 NULL 1
3 001003 1 1 2
4 002004 1 2 1
5 001003005 2 3 2
6 001003005006 3 5 2
7 002004007 2 4 1
8 002004008 2 4 2
9 002004009 2 4 2
10 00200400800A 3 8 2
and so on...
The problem is how to get the records for specific user id limited to the shortest path in the same branch. For example for assigned_user_id = 2 retrive:
id path depth parent_id assigned_user_id
------------------------------------------------------------
3 001003 1 1 2
8 002004008 2 4 2
9 002004009 2 4 2
Instead of:
id path depth parent_id assigned_user_id
------------------------------------------------------------
3 001003 1 1 2
5 001003005 2 3 2
6 001003005006 3 5 2
8 002004008 2 4 2
9 002004009 2 4 2
10 00200400800A 3 8 2
SELECT t1.*
FROM atable t1
LEFT JOIN atable t2
ON t2.assigned_user_id = t1.assigned_user_id AND
t2.path = LEFT(t1.path, CHAR_LENGTH(t2.path)) AND
t2.id <> t1.id
WHERE t1.assigned_user_id = 2
AND t2.id IS NULL
If I get you right, it might be enough to exclude rows whose parent_id is among the ids selected. This is because if the parent and child is selected, they must be in the same branch. The parent's path will be shorter, therefore it's OK to exclude the child.
Something like:
SELECT *
FROM x
WHERE assigned_user_id = 2
AND parent_id NOT IN (SELECT id FROM x WHERE assigned_user_id = 2)
If you would have a tree like this (numbers are your assigned user ids):
A1 G2
/ \ / \
B2 C2 H2 I2
| \ | | \
D2 E2 L1 J2 K2
|
M2
B2, C2, G2 and M2 would be selected. I'm still not sure if this was your intention, though.
I would try something like this:
SELECT * FROM PATHS WHERE ASSIGNED_USER_ID = 2
AND NOT PARENT_ID IN (SELECT ID FROM PATHS WHERE ASSIGNED_USER_ID = 2)
Basically the idea is to select top parent nodes for the given user.
Idea behind this: B is shorter than A if A starts with B. Maybe there's something better than LIKE to do this "begins with".
SELECT a.* FROM node AS a
WHERE a.assigned_user_id = ?
AND NOT EXIST
(SELECT * FROM node AS b
WHERE b.assigned_user_id = ?
AND LENGTH(a.path) > LENGTH(b.path)
AND a.path LIKE CONCAT(b.path, '%') )
Both ? are mapped to the desired user id.
EDIT
Forgot to include the assigned_user_id. Changed the code.
2nd EDIT
Changed code to avoid the case of b=a.
Have you tried something like this?
select child.assigned_user_id, child.id
from node as child
left join node as parent
on child.path like CONCAT(parent.path, '%')
and child.assigned_user_id = parent.assigned_user_id
and child.id <> parent.id
group by child.assigned_user_id, child.id
having max(parent.id is null) = true
(Not sure it'll work exactly as above, but basically: to left join on the path in order to extract the full list of parents, and then to aggregate in such a way that you only keep the nodes without any parents when grouped by assigned_user_id.)

How do I get the count of all the products in a category and its sub categories (and sub-sub categories)?

The category table looks like somewhat as below:
id -- name -- parent_id
1 -- Men -- 0
2 -- Women -- 0
3 -- Shirts -- 1
4 -- Half-sleeve -- 3
5 -- Full-sleeve -- 3
Relationship table:
Product_id -- Category Id
1 -- 2
2 -- 2
3 -- 4 ....
I can retrieve the number of products in any one category and its immediate sub categories with ease with ease. But if there are more than 2 levels things get messy.
So my question is How do I get the number of all of the products in Men and its sub categories. Or Shirts and its subcategories?
Any ideas, Thanks.
UPDATE:
I know there is Nested Set Model but I am not in position to change the structure to that now.
If it is possible I would check out Managing Hierarchical Data in MySQL.
It is hard to get your head around at first, but it makes tasks like this much easier.
If you can't do this, you will have to do a recursive function, e.g.:
$prods = 0;
function getProdsInCat($cat)
{
global $prods;
$prods += mysql_result(mysql_query(SELECT COUNT(`Product_id`) FROM `prod_to_cat` WHERE `Category Id` = '".$cat."'),0);
$moreCats = mysql_query("SELECT `cat_id` FROM `cats` WHERE `parent_id` = '".$cat."'");
while($cats = mysql_fetch_assoc($moreCats)
{
getProdsInCat($cats['cat_id']);
}
}
Assuming you can add an extra column to the categories table.
Said column will have the path to the category.
id -- name -- parent_id path
1 -- Men -- 0 0/
2 -- Women -- 0 0/
3 -- Shirts -- 1 0/1
4 -- Half-sleeve -- 3 0/1/3
5 -- Full-sleeve -- 3 0/1/3
That way finding all the subcategories becomes one query:
SELECT id as CatId FROM categories WHERE path LIKE '0/1/%';
And to get the count of all the products within a category and its childrens is pretty easy too:
SELECT count(p.id) as Total
FROM products as p
JOIN categories as c ON p.category_id = c.id
WHERE c.path like '0/1/%';
Pretty efficient query.
This article provides more information: More Trees & Hierarchies in SQL

Categories