获取所有类别和父类别中的产品都不包含关键字

时间:2017-04-13 05:51:30

标签: mysql sql

我正在尝试获取关键字匹配的那些产品的所有类别及其数量(该类别中的产品数量)。我试过的查询没有给我正确的结果。 此外,我希望父类别直到第1级及其数量。

e.g。我正在尝试使用关键字手表,然后类别“手表”应该有一些计数。父类别“附件”及其后代类别的总和也计算在内。

我的表结构是:

tblProducts:产品分为5类,fldCategoryId1,fldCategoryId2,fldCategoryId3,fldCategoryId4和fldCategoryId5。 fldProductStatus应为'A'

+-----------------------------+-------------------+
| Field                       | Type              |
+-----------------------------+-------------------+
| fldUniqueId                 | bigint(20)        |
| fldCategoryId1              | bigint(20)        |
| fldCategoryId2              | bigint(20)        |
| fldCategoryId3              | bigint(20)        |
| fldCategoryId4              | bigint(20)        |
| fldCategoryId5              | bigint(20)        |
| fldProductStatus            | enum('A','P','D') |
| fldForSearch                | longtext          |
+-----------------------------+-------------------+

tblCategory:

+------------------------------+-----------------------+
| Field                        | Type                  |
+------------------------------+-----------------------+
| fldCategoryId                | bigint(20)            |
| fldCategoryName              | varchar(128)          |
| fldCategoryParent            | int(11)               |
| fldCategoryLevel             | enum('0','1','2','3') |
| fldCategoryActive            | enum('Y','N')         |
+------------------------------+-----------------------+

搜索查询:

SELECT count( c.fldCategoryId ) AS cnt, c.fldCategoryLevel, c.fldCategoryParent, c.fldCategoryId, c.fldCategoryName, p.fldForSearch, c.fldCategoryParent
FROM tblCategory c, tblProducts p
WHERE (
    c.fldCategoryId = p.fldCategoryId1
    OR c.fldCategoryId = p.fldCategoryId2
    OR c.fldCategoryId = p.fldCategoryId3
    OR c.fldCategoryId = p.fldCategoryId4
    OR c.fldCategoryId = p.fldCategoryId5
)
AND p.fldProductStatus = 'A'
AND (
    MATCH ( p.fldForSearch )
    AGAINST (
        '+(watches watch)'
        IN BOOLEAN MODE
    )
)
GROUP BY c.fldCategoryId

注意:该表位于 InnoDB 引擎中,并且在'fldForSearch'列上有FULLTEXT搜索索引。

编辑:示例数据可在sqlfiddle

中找到

3 个答案:

答案 0 :(得分:2)

我不确定你的意思:

  

此外,我希望父类别直到第1级及其计数。

但是以下查询将显示每个类别的计数(包括那些找到0个产品的类别)和一般汇总:

SELECT 
    c.fldCategoryId, 
    c.fldCategoryLevel, 
    c.fldCategoryName, 
    COUNT( * ) AS cnt
FROM tblCategory c
    LEFT JOIN tblProducts p ON
            (c.fldCategoryId = p.fldCategoryId1
        OR  c.fldCategoryId = p.fldCategoryId2
        OR  c.fldCategoryId = p.fldCategoryId3
        OR  c.fldCategoryId = p.fldCategoryId4
        OR  c.fldCategoryId = p.fldCategoryId5)
        AND p.fldProductStatus = 'A'
        AND MATCH ( p.fldForSearch )
            AGAINST (
                '+(watches watch)'
                IN BOOLEAN MODE
            )
GROUP BY 
    c.fldCategoryId
    c.fldCategoryLevel,  
    c.fldCategoryName
WITH ROLLUP;

注意:

  • 如果您希望计算该类别中的所有产品,则无法选择p.fldForSearchfldForSearch基于每个产品,它会破坏分组目的
  • 我与产品保持联系,因此它会返回与您的关键字匹配的0个产品类别。如果您不希望发生这种情况,请删除LEFT关键字
  • 我还没有检查MATCH条件,我认为这是正确的。

答案 1 :(得分:1)

首先不要在列之间展开数组(fldCategoryId...)。而是添加一个新表。

完成后,查询会更改,例如删除OR子句。

希望任何进一步的问题都会落实到位。

答案 2 :(得分:1)

由于您的类别树具有固定的高度(4级),您可以随时创建transitive closure table

SELECT c1.fldCategoryId AS descendantId, c.fldCategoryId AS ancestorId
FROM tblcategory c1
LEFT JOIN tblcategory c2 ON c2.fldCategoryId = c1.fldCategoryParent
LEFT JOIN tblcategory c3 ON c3.fldCategoryId = c2.fldCategoryParent
JOIN tblcategory c ON c.fldCategoryId IN (
    c1.fldCategoryId,
    c1.fldCategoryParent,
    c2.fldCategoryParent,
    c3.fldCategoryParent
)

结果看起来像

| descendantId | ancestorId |
|--------------|------------|
|            1 |          1 |
|            2 |          1 |
|            2 |          2 |
|          ... |        ... |
|            5 |          1 |
|            5 |          2 |
|            5 |          5 |
|          ... |        ... |

现在,您可以在子查询(派生表)中使用它将其与使用descendantId的产品和使用ancestorId的类别相关联。这意味着来自X类的产品将间接与X的所有祖先(以及X)相关联。例如:5类是2的孩子 - 2是1的孩子。因此,类别5的所有产品必须计入类别5,2和1。

最终查询:

SELECT c.*, coalesce(sub.cnt, 0) as cnt
FROM tblCategory c
LEFT JOIN (
    SELECT tc.ancestorId, COUNT(DISTINCT p.fldUniqueId) AS cnt
    FROM tblProducts p
    JOIN (
        SELECT c1.fldCategoryId AS descendantId, c.fldCategoryId AS ancestorId
        FROM tblcategory c1
        LEFT JOIN tblcategory c2 ON c2.fldCategoryId = c1.fldCategoryParent
        LEFT JOIN tblcategory c3 ON c3.fldCategoryId = c2.fldCategoryParent
        JOIN tblcategory c ON c.fldCategoryId IN (
            c1.fldCategoryId,
            c1.fldCategoryParent,
            c2.fldCategoryParent,
            c3.fldCategoryParent
        )
    ) tc ON tc.descendantId IN (
        p.fldCategoryId1,
        p.fldCategoryId2,
        p.fldCategoryId3,
        p.fldCategoryId4,
        p.fldCategoryId5
    )
    WHERE p.fldProductStatus = 'A'
        AND MATCH ( p.fldForSearch )
            AGAINST ( '+(watches watch)' IN BOOLEAN MODE )
    GROUP BY tc.ancestorId
) sub ON c.fldCategoryId = sub.ancestorId

您的样本数据的结果(没有级别,因为它似乎是错误的):

| fldCategoryId | fldCategoryName | fldCategoryParent | fldCategoryActive | cnt |
|---------------|-----------------|-------------------|-------------------|-----|
|             1 |             Men |                 0 |                 Y |   5 |
|             2 |     Accessories |                 1 |                 Y |   5 |
|             3 |       Men Watch |                 1 |                 Y |   3 |
|             5 |           Watch |                 2 |                 Y |   5 |
|             6 |           Clock |                 2 |                 Y |   3 |
|             7 |     Wrist watch |                 1 |                 Y |   2 |
|             8 |           Watch |                 2 |                 Y |   4 |
|             9 |          watch2 |                 3 |                 Y |   2 |
|            10 |        fastrack |                 8 |                 Y |   3 |
|            11 |           swish |                 8 |                 Y |   2 |
|            12 |         digital |                 5 |                 Y |   2 |
|            13 |          analog |                 5 |                 Y |   2 |
|            14 |            dual |                 5 |                 Y |   1 |

演示:

请注意,外部(左连接)子查询在逻辑上不是必需的。但根据我的经验,没有它,MySQL表现不佳。

仍有一些方法可以进行性能优化。一种是将传递闭包表存储在索引的临时表中。如果类别很少更改,您也可以将其保留在常规表中。您也可以使用触发器进行管理。