SQL效率 - 多属性过滤,电子商务解决方案的最佳实践

时间:2017-02-17 20:02:43

标签: sql-server database stored-procedures

我有一个用于电子商务解决方案的现有MSSQL数据库。有许多产品属性组和许多属性。客户需要一个过滤解决方案,我觉得当前的实施效率不高,所以我想联系DBA社区,提出一些关于如何最好地改进的建议。

数据结构(简单的多对多关系):

ProductAttribute Table
------
ProductId 
AttributeId 

Attribute Table
-------
AttributeId
AttributeGroupId

AttributeGroup Table
-------
AttributeGroupId

这里的主要问题是,在客户端,他们可以根据需要选择尽可能多的过滤器。但是,对于组中的每个属性,它都是"或"选择,而其他组中的属性是"和"选择。

实施例: AttributeGroup颜色:红色,绿色 AttributeGroup长度:长 AttributeGroup材质:Silk,亚麻

所以基本上我需要退回任何符合红/长/丝绸,红/长/亚麻,绿/长/丝绸,绿/长/亚麻的产品。

现在,存储过程以苏联风格单独检查这些组中的每个组,超长过程完成工作。首先构建一个与第一组(Color)匹配的productIds的临时表,然后删除不与连续的AttributeGroup过滤器匹配的ID。示例:给我所有的红色和绿色产品。然后删除任何不是" Long"的产品。现在删除任何不属于亚麻或丝绸的剩余产品。

这个过程相当不错,但既然这个网站的繁忙季节已经到来,它似乎并没有很好地扩展,而且我希望提高效率。最多可以有12个不同的组,每个组中有许多不同的属性。

数据可以以任何方式传递,但是当前是一个多分隔的字符串,使用sql函数将其解析为表。 AttributeGroupId-属性ID,属性ID | AttributeGroupId-属性ID,属性Id 例如:1-104,114 | 2-125,140 | 3-215,317

编辑:解析输入数据的示例

AttributeGroupId| Attribute Id
------------------------------
1               | 104
1               | 114
2               | 125
2               | 140
3               | 215
3               | 317

没有深入了解当前的行动,有关如何最好地解决这个问题的任何建议吗?

2 个答案:

答案 0 :(得分:0)

根据您拥有的属性数量,您可以使用junk dimension策略。单列中会有重复项,但所有列的组合都是唯一的。换句话说,你有红色的多个记录,但你只有一个红/长/丝。

记录数是所有列中唯一组合的数量 所有过滤器都将针对同一个表,只有一个连接到ProductAttribute表。在您的示例中,您可以使每个组成为颜色等列。在单个列中,他们可以使用多值列表进行“或”选择。跨列将是“AND”选择。

Select AttributesID, Color, Length, Material From Attributes
Where Color in (@Color) and Length in (@Length) AND Material in (@Material)

答案 1 :(得分:0)

在您的存储过程中,您可以将过滤器逻辑绑定在 where语句中,该语句将每个组的属性与OR匹配,然后将AND与其他组匹配。

注意:像这样的查询很可能将查询优化器发送到轨道,并且会发生可怕的事情。如果您确实实现了类似的功能,则需要设置WITH RECOMPILE选项或使用OPTION (OPTIMIZE FOR UNKNOWN)

进行测试

基本上,在where子句中,您希望排除不在组中的每个项目,或者它在组中,并且存在过滤器并且过滤器匹配。

DECLARE @Attributes TABLE(AttributeGroupID INT,AttributeID INT)
INSERT @Attributes SELECT 1,104--COLOR
INSERT @Attributes SELECT 1,114--COLOR
INSERT @Attributes SELECT 2,125--LENGTH
INSERT @Attributes SELECT 2,140--LENGTH
INSERT @Attributes SELECT 3,215--MATERIAL
INSERT @Attributes SELECT 3,317--MATERIAL

DECLARE @AttributeGroup TABLE(AttributeGroupID INT)
INSERT @AttributeGroup SELECT 1--COLOR
INSERT @AttributeGroup SELECT 2--LENGTH
INSERT @AttributeGroup SELECT 3--MATERIAL

DECLARE @Product TABLE(ProductID INT)
INSERT @Product SELECT 1 
INSERT @Product SELECT 2


DECLARE @ProductAttribute TABLE(ProductID INT,AttributeID INT)
INSERT @ProductAttribute SELECT 1,104
INSERT @ProductAttribute SELECT 1,125
INSERT @ProductAttribute SELECT 1,317
INSERT @ProductAttribute SELECT 2,114
INSERT @ProductAttribute SELECT 2,125
INSERT @ProductAttribute SELECT 2,215


DECLARE @Filter TABLE(AttributeGroupID INT,AttributeID INT)
INSERT @Filter SELECT 1,104
INSERT @Filter SELECT 2,125
--INSERT @Filter SELECT 3,317

SELECT
    P.ProductID,
    MatchingAttrinuteCount=COUNT(*)
FROM
    @Product P
    INNER JOIN @ProductAttribute PA ON PA.ProductID=P.ProductID
    INNER JOIN @Attributes A ON A.AttributeID=PA.AttributeID
WHERE
    (A.AttributeGroupID<>1 OR(A.AttributeGroupID=1 AND ( (NOT(EXISTS(SELECT * FROM @Filter WHERE AttributeGroupID=1))) OR PA.AttributeID IN(SELECT F.AttributeID FROM @Filter F WHERE F.AttributeGroupID=1))))
    AND
    (A.AttributeGroupID<>2 OR(A.AttributeGroupID=2 AND ( (NOT(EXISTS(SELECT * FROM @Filter WHERE AttributeGroupID=2))) OR PA.AttributeID IN(SELECT F.AttributeID FROM @Filter F WHERE F.AttributeGroupID=2))))
    AND
    (A.AttributeGroupID<>3 OR(A.AttributeGroupID=3 AND ( (NOT(EXISTS(SELECT * FROM @Filter WHERE AttributeGroupID=3))) OR PA.AttributeID IN(SELECT F.AttributeID FROM @Filter F WHERE F.AttributeGroupID=3))))
GROUP BY
    P.ProductID

上面的查询将产生:

ProductID   MatchingAttrinuteCount
1           3
2           2

第二种可能更高效的方法......

回避游标通常是一个好主意,但是,这是游标证明有用的时代之一。注意:您将把满足遇到的第一个条件的所有产品加载到内存中作为最大集合。如果你认为这个值不会很大,那么下面的光标逻辑应该更加紧凑和高效。

DECLARE @Filter TABLE(AttributeGroupID INT,AttributeID INT)
INSERT @Filter SELECT 1,104
INSERT @Filter SELECT 2,125
INSERT @Filter SELECT 1,114
SET NOCOUNT ON

DECLARE @Matches TABLE(ProductID INT,AttributeID INT)
DECLARE @AttributeID INT, @AttributeGroupID INT, @HasLooped BIT=0
--GROUPS TO LOOP FOR 
DECLARE ATTRIBUTE_GROUP_CURSOR CURSOR FOR SELECT DISTINCT AttributeGroupID FROM @Filter
OPEN ATTRIBUTE_GROUP_CURSOR
FETCH NEXT FROM ATTRIBUTE_GROUP_CURSOR INTO @AttributeGroupID

--FOR EACH GROUP [COLOR..MATERIAL] IN THE FILTER 
WHILE(@@FETCH_STATUS=0)BEGIN

    INSERT @Matches 
    --INSERT ALL PRODUCTS WITH MATCHING ATTRIBUTES IN THIS GROUP
    --IF FIRST LOOP EXAMINE ALL PRODUCTS IF NOT EXAMINE ONLY PRODUCTS ALREADY IN MATCH SET
    SELECT P.ProductID, PA.AttributeID 
    FROM @Product P
    INNER JOIN @ProductAttribute PA ON PA.ProductID=P.ProductID 
    WHERE 
        AttributeID IN (SELECT AttributeID FROM @Filter F WHERE F.AttributeGroupID=@AttributeGroupID ) 
        AND 
        (@HasLooped=0 OR P.ProductID IN (SELECT ProductID FROM @Matches)) --Only match products if one filter has been applied

    SET @HasLooped=1

    FETCH NEXT FROM ATTRIBUTE_GROUP_CURSOR INTO @AttributeGroupID

END

CLOSE ATTRIBUTE_GROUP_CURSOR
DEALLOCATE ATTRIBUTE_GROUP_CURSOR

SELECT * FROM @Matches

...屈服

ProductID   AttributeID
1           104
2           114
1           125
2           125