Выберите записи, имеющие category_id где-нибудь в дереве категорий - PullRequest
0 голосов
/ 02 декабря 2018

Я отображаю дерево категорий, и рядом с каждой веткой я хочу подсчитать количество объявлений того же уровня или ниже.

У всех объявлений есть катида, которая находится на самом низком уровне категории.т. е. вы не можете применить конкретную категорию к списку, если доступны более низкие уровни категории.

Например, в приведенной ниже таблице «списки» вы не можете найти список, в котором есть catid = 24, потому что это еще не самый низкий уровень в дереве вдоль этой ветви.

В моей базе данных категорий есть максимум 4 уровня (0-3).

Вот таблицы:

all_categories (таблица)

record_id  parent_category_id   parent_id  title        level
--------------------------------------------------------------------- 
24         NULL                 NULL       Real Estate  0
5915       24                   24         Residential  1
7569       5915                 24         For sale     2

списки (таблица)

record_id    cat_id
--------------------
1            7569
2            8847

Итак, мое дерево категорий в HTML должно выглядеть примерно так:

HTML

Categories              Listing count
-------------------------------------
24                      1
  5915                  1
    7569                1

Итак, в своем коде html и jQuery я передаю в запрос определенную категорию любого уровня, и он должен находить списки, которые находятся на этом уровне или ниже.

Я пытался часами, и мои усилия пока не стоят того, чтобы их показывать.Но я все равно ...

РЕДАКТИРОВАТЬ: Мои усилия до сих пор (не смейтесь, это запутано после нескольких часов пробовать разные вещи):

select l.record_id 
from listings l
where catid in (
    select record_id 
    from all_categories 
    where record_id = 5915)
or catid in (
    select parent_category_id 
    from all_categories 
    where parent_category_id = 5915)
or catid in (
    select parent_id 
    from all_categories 
    where parent_id = 5915)

Ответы [ 6 ]

0 голосов
/ 16 декабря 2018

Я считаю, что это подходит для рекурсивного sql.

CREATE FUNCTION f_total_listings(@root_id INT) RETURNS INT AS
BEGIN
    DECLARE @listing_count INT;
    WITH cat (record_id, parent_category_id) AS
    (
        SELECT root.record_id, root.parent_category_id
        FROM all_categories AS root
        WHERE root.parent_category_id = @root_id
        UNION ALL
        SELECT child.record_id, child.parent_category_id
        FROM cat AS parent, all_categories AS child
        WHERE parent.record_id = child.parent_category_id
    )
    SELECT @listing_count = count(*)
    FROM listings l
    JOIN cat ON l.cat_id = cat.record_id;
    RETURN @listing_count;
END;

SELECT record_id, f_total_listings(record_id) FROM all_categories
0 голосов
/ 15 декабря 2018

У вас непонятная структура данных, но проблема не кажется такой сложной.Кажется, это делает то, что вы хотите:

select c2.record_id, count(*)
from listings l join
         all_categories c
         on l.cat_id = c.record_id join
         all_categories c2
         on c2.record_id in (c.record_id, c.parent_category_id, c.parent_id)
group by c2.record_id, l.cat_id
order by l.cat_id, c2.record_id;

Идея довольно проста.Таблица all_categories имеет полную иерархию.По сути, вам нужно переместить все уровни иерархии в отдельные строки , чтобы их можно было агрегировать.

Это то, что делает от join до c2.Остальное просто агрегация.

0 голосов
/ 13 декабря 2018

Для каждого record_id оставьте слева дочерние элементы, затем подсчитайте уникальные значения на каждом уровне и сложите их вместе.

select n.record_id
count(distinct n.record_id)+count(distinct n2.record_id)+count(distinct 
n3.record_id)+count(distinct n4.record_id) listingcount
from all_categories n
left join all_categories n2 on n.record_id=n2.parent_category_id
left join all_categories n3 on n2.record_id=n3.parent_category_id
left join all_categories n4 on n3.record_id=n4.parent_category_id
group by n.record_id
0 голосов
/ 12 декабря 2018

ну, в mysql 8 есть функция (с), которая запускается рекурсивно, вы можете получить уровень структуры catId

-> (id cat parent_id) -> (1,24, NULL), (2,5915,1), (3,7569,2), (4,3,1)

WITH recursive parent(id,parent_id,Levels) AS
(
    select id,parent_id, 0 as Levels from table where id =1 //condition
    union all
    select c.id,c.parent_id,(Levels+1) as Levels from table as c  inner join parent as p1 on p1.id = c.parent_id
)
SELECT *
FROM  parent

единственным ограничением этого является то, что это в моей версии sql8.x остальное код будет работать нормально, любая проблема, которую вы можете задать, называется этот метод, cet надеюсь, что он поможет

0 голосов
/ 11 декабря 2018

Предполагая, что дерево имеет максимальную глубину в четыре уровня, вы можете использовать несколько ЛЕВЫХ СОЕДИНЕНИЙ для получения полного дерева или поддерева.Это не будет супер эффективным, хотя.Рассмотрим следующий запрос:

SET @subtree_id = 1;

SELECT
    c0.category_id AS c0_id, c0.name AS c0_name,
    c1.category_id AS c1_id, c1.name AS c1_name,
    c2.category_id AS c2_id, c2.name AS c2_name,
    c3.category_id AS c3_id, c3.name AS c3_name,
    l.listing_id
FROM category AS c0
LEFT JOIN category AS c1 ON c1.parent_id = c0.category_id
LEFT JOIN category AS c2 ON c2.parent_id = c1.category_id
LEFT JOIN category AS c3 ON c3.parent_id = c2.category_id
LEFT JOIN listing AS l ON l.category_id = c0.category_id
                       OR l.category_id = c1.category_id
                       OR l.category_id = c2.category_id
                       OR l.category_id = c3.category_id
WHERE c0.category_id = @subtree_id;

Он выдаст следующие результаты:

| c0_id | c0_name     | c1_id | c1_name     | c2_id | c2_name   | c3_id | c3_name    | listing_id |
|-------|-------------|-------|-------------|-------|-----------|-------|------------|------------|
| 1     | Real Estate | 2     | Residential | 3     | House     | NULL  | NULL       | NULL       |
| 1     | Real Estate | 2     | Residential | 4     | Apartment | NULL  | NULL       | 1          |
| 1     | Real Estate | 2     | Residential | 4     | Apartment | NULL  | NULL       | 2          |
| 1     | Real Estate | 2     | Residential | 4     | Apartment | NULL  | NULL       | 3          |
| 1     | Real Estate | 2     | Residential | 5     | Condo     | NULL  | NULL       | NULL       |
| 1     | Real Estate | 6     | Commercial  | 7     | Office    | NULL  | NULL       | 4          |
| 1     | Real Estate | 6     | Commercial  | 8     | Retail    | NULL  | NULL       | 5          |
| 1     | Real Estate | 6     | Commercial  | 9     | Other     | 10    | Industrial | 6          |

К сожалению, он содержит только полные пути.Чтобы соответствовать ожидаемому результату, просто разбейте каждую строку на 4 строки:

SET @subtree_id = 1;

SELECT
    CASE WHEN level >= 0 THEN c0_id END AS c0_id, CASE WHEN level >= 0 THEN c0_name END AS c0_name,
    CASE WHEN level >= 1 THEN c1_id END AS c1_id, CASE WHEN level >= 1 THEN c1_name END AS c1_name,
    CASE WHEN level >= 2 THEN c2_id END AS c2_id, CASE WHEN level >= 2 THEN c2_name END AS c2_name,
    CASE WHEN level >= 3 THEN c3_id END AS c3_id, CASE WHEN level >= 3 THEN c3_name END AS c3_name,
    COUNT(listing_id) AS lc
FROM (
    SELECT
        c0.category_id AS c0_id, c0.name AS c0_name,
        c1.category_id AS c1_id, c1.name AS c1_name,
        c2.category_id AS c2_id, c2.name AS c2_name,
        c3.category_id AS c3_id, c3.name AS c3_name,
        l.listing_id
    FROM category AS c0
    LEFT JOIN category AS c1 ON c1.parent_id = c0.category_id
    LEFT JOIN category AS c2 ON c2.parent_id = c1.category_id
    LEFT JOIN category AS c3 ON c3.parent_id = c2.category_id
    LEFT JOIN listing AS l ON l.category_id = c0.category_id
                           OR l.category_id = c1.category_id
                           OR l.category_id = c2.category_id
                           OR l.category_id = c3.category_id
    WHERE c0.category_id = @subtree_id
) AS paths
INNER JOIN (
    SELECT 0 AS level UNION ALL
    SELECT 1 UNION ALL
    SELECT 2 UNION ALL
    SELECT 3
) AS levels ON level = 0 AND c0_id IS NOT NULL
            OR level = 1 AND c1_id IS NOT NULL
            OR level = 2 AND c2_id IS NOT NULL
            OR level = 3 AND c3_id IS NOT NULL
GROUP BY 1, 2, 3, 4, 5, 6, 7, 8
ORDER BY 2, 1, 4, 3, 6, 5, 8, 7

Демонстрация на БД <> Fiddle

0 голосов
/ 09 декабря 2018

Уровень 0 parent_id для ребенка сохраняется в таблице «all_categories».

Таким образом, ребенок может быть связан с другими дочерними элементами по их общему parent_id.
И затем включить уровень 0 вв то же время.

Сложность заключается в том, что указанный дочерний идентификатор отсутствует в таблице "списков".
Таким образом, чтобы получить эти списки record_id, он должен пройти через категории.

Тест на rextester можно найти здесь

SELECT 
cat.record_id as catId, 
pl.listId AS ListingCount,
cat.level
FROM 
(
    SELECT 
     cat1.parent_id, 
     MAX(list.record_id) AS listId
    FROM all_categories AS cat1
    JOIN all_categories AS cat2 ON cat2.parent_id = cat1.parent_id
    JOIN listings list ON list.cat_id = cat2.record_id
    WHERE cat1.record_id = 5915
    GROUP BY cat1.parent_id
) AS pl
LEFT JOIN all_categories AS cat ON (cat.parent_id = pl.parent_id OR cat.record_id = pl.parent_id)
ORDER BY cat.record_id, cat.level;

Результат:

catId   ListingCount    level
24      1               0
5915    1               1
7569    1               2

"Уровень" также включен в запрос, потому что это можетиспользоваться для генерации этого дерева категорий в HTML.

Обратите внимание, что запрос можно было бы значительно упростить, если бы эта таблица "списков" содержала бы только cat_id для уровня 0

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...