简体   繁体   中英

SQL query to retrieve all products under some categories in Adjacency List Model

I have a database with an Adjacency List approach to handle categorization of products in which one product may be found under many categories. Get a look at the following database layout:

cats
id    parent    title           desc
1     0         top             top level
2     1         Electronics
3     2         Gaming
4     2         Computers
5     4         Tablets
6     1         Food
7     3         Xbox

products
id        title         qty
1         ToshibaTV     5
2         I-PAD2        9
3         Laser Pen     24
4         Asus Notebook 5


cats_products
id   product_id   cat_id 
1    2            3
2    2            5
3    1            2
4    3            2
5    4            4 

In the above example I need an SQL query that is able to retrieve all the products found in Electronics category and any child category of it with any level (Xbox for example which it is not directly child of Electronics) without repeat of the product that found in more than one category like I-PAD2.

I could able to do this with the help of the application in PHP but I wonder if it is possible to do it using just pure sql in MySQL?

What you're trying to do is rather difficult using the Adjacency List Model. As @Mike suggested, using a Nested Set Model would make this so much easier. Or alternatively doing it via your php code would even be easier.

However, assuming you know how many parent-child levels there could be (or you can assume there wouldn't be more than X), you can try something like this. This would be easier to read if MySQL supported CTEs, but unfortunately, it does not. In this example, I've gone 4 levels deep -- you can get the idea of going deeper.

SELECT p.Id, p.Title, p.Qty
FROM Products p
   JOIN Cats_Products cp on p.id = cp.product_id
WHERE cp.cat_id IN (
   SELECT c.id
   FROM Cats c
   WHERE c.title = 'Electronics'
   UNION ALL
   SELECT c2.id
   FROM Cats c
      LEFT JOIN Cats c2 ON c.id = c2.parent
   WHERE c.title = 'Electronics'
   UNION ALL
   SELECT c3.id
   FROM Cats c
      LEFT JOIN Cats c2 ON c.id = c2.parent
      LEFT JOIN Cats c3 ON c2.id = c3.parent
   WHERE c.title = 'Electronics'
   UNION ALL
   SELECT c4.id
   FROM Cats c
      LEFT JOIN Cats c2 ON c.id = c2.parent
      LEFT JOIN Cats c3 ON c2.id = c3.parent
      LEFT JOIN Cats c4 ON c3.id = c4.parent
   WHERE c.title = 'Electronics'
)
GROUP BY p.Id, p.Title, p.Qty

And here is the SQL Fiddle .

Good luck.

This the first part of you question. Could you explain the child part in more detail. I guess it has something to do with parent in cats.

Try this:

SELECT distinct(p.title) 
FROM cats AS c
JOIN cats_products AS cp
ON cp.cat_id = c.id AND c.title = "Electronics"
JOIN products AS p
ON p.id = cp.product_id;

SAMPLE DATA:

CREATE TABLE cats
    (`id` int, `parent` int, `title` varchar(11), `desc` varchar(9))
;

INSERT INTO cats
    (`id`, `parent`, `title`, `desc`)
VALUES
    (1, 0, 'top', 'top level'),
    (2, 1, 'Electronics', ''),
    (3, 2, 'Gaming', ''),
    (4, 2, 'Computers', ''),
    (5, 4, 'Tablets', ''),
    (6, 1, 'Food', ''),
    (7, 3, 'Xbox', '')
;



CREATE TABLE products
    (`id` int, `title` varchar(13), `qty` int)
;

INSERT INTO products
    (`id`, `title`, `qty`)
VALUES
    (1, 'ToshibaTV', 5),
    (2, 'I-PAD2', 9),
    (3, 'Laser Pen', 24),
    (4, 'Asus Notebook', 5)
;



CREATE TABLE cats_products
    (`id` int, `product_id` int, `cat_id` int)
;

INSERT INTO cats_products
    (`id`, `product_id`, `cat_id`)
VALUES
    (1, 2, 3),
    (2, 2, 5),
    (3, 1, 2),
    (4, 3, 2),
    (5, 4, 4)
;

SQL FIDDLE DEMO

EDIT Second part :

SELECT distinct(v.title) FROM products AS v
JOIN (
  SELECT Y.product_id FROM cats_products AS Y
    JOIN(
      SELECT distinct(z.id) FROM cats AS Z
        JOIN
          (SELECT c.parent
           FROM cats AS c
           JOIN cats_products AS cp
           ON cp.cat_id = c.id 
           AND c.title = "Electronics") AS X
         ON x.parent = z.parent) AS W
    ON W.id = Y.cat_id) AS u
ON u.product_id = v.id
;

Sample data

CREATE TABLE cats
    (`id` int, `parent` int, `title` varchar(11), `desc` varchar(9))
;

INSERT INTO cats
    (`id`, `parent`, `title`, `desc`)
VALUES
    (1, 0, 'top', 'top level'),
    (2, 1, 'Electronics', ''),
    (3, 2, 'Gaming', ''),
    (4, 2, 'Computers', ''),
    (5, 4, 'Tablets', ''),
    (6, 1, 'Food', ''),
    (7, 3, 'Xbox', '')
;



CREATE TABLE products
    (`id` int, `title` varchar(13), `qty` int)
;

INSERT INTO products
    (`id`, `title`, `qty`)
VALUES
    (1, 'ToshibaTV', 5),
    (2, 'I-PAD2', 9),
    (3, 'Laser Pen', 24),
    (4, 'Asus Notebook', 5),
    (5, 'French Fries', 50)
;



CREATE TABLE cats_products
    (`id` int, `product_id` int, `cat_id` int)
;

INSERT INTO cats_products
    (`id`, `product_id`, `cat_id`)
VALUES
    (1, 2, 3),
    (2, 2, 5),
    (3, 1, 2),
    (4, 3, 2),
    (5, 4, 4),
    (5, 5, 6)

;

This is my interpretation of your question: Every category has a parent and you want to select all other categories with the same parent. More specific all products for with the same parent category. SQL FIDDLE DEMO

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM