在分析数据库上存储多个标签

Kan*_*mar 7 performance index index-tuning redshift

我想在每笔交易中存储用户购买的自定义标签,例如,如果用户购买了鞋子,那么标签是 "SPORTS", "NIKE", SHOES, COLOUR_BLACK, SIZE_12,..

这些标签是有兴趣查询以了解销售情况的卖家。

我的想法是,当新标签出现时,为该标签创建新代码(类似于哈希码但顺序),代码从"a-z"26 个字母开始,然后"aa, ab, ac...zz"继续。现在,将一笔交易中给出的所有标签保存在tag (varchar)用 分隔的一列中"|"

让我们假设映射是(在应用程序级别)

"SPORTS" = a
"TENNIS" = b
"CRICKET" = c
...
...
"NIKE"  = z        //Brands company
"ADIDAS" = aa
"WOODLAND" = ab
...
...
SHOES   = ay
...
...
COLOUR_BLACK = bc
COLOUR_RED = bd
COLOUR_BLUE = be
...
SIZE_12 = cq
...
Run Code Online (Sandbox Code Playgroud)

所以存储上面的购买交易,标签会像tag="|a|z|ay|bc|cq|"现在允许卖家通过添加WHERE条件来搜索售出的鞋子数量tag LIKE %|ay|%。现在的问题是我不能将索引(redshift db 中的排序键)用于“LIKE 以 % 开头”。那么如何解决这个问题,因为我可能有 1 亿条记录?不想全表扫描..

任何解决方案来解决这个问题?

Update_1:我没有遵循bridge table概念(交叉引用表),因为我想在搜索指定标签后对结果执行分组。当两个标签在单个事务中匹配时,我的解决方案将只给出一行,但桥接表会给我两行?那么我的 sum() 将翻倍。

我得到了如下建议

EXISTS (SELECT 1 FROM transaction_tag WHERE tag_id = 'zz' and trans_id = tr.trans_id) 在 WHERE 子句中为每个标签一次(注意:假设 tr 是周围查询中事务表的别名)

我没有遵循这个;因为我必须对标签执行 AND 和 OR 条件,例如 ("SPORTS" AND "ADIDAS") ---- "SHOE" AND ("NIKE" OR "ADIDAS")

Update_2:我没有关注位域,因为不知道 redshift 是否也有这种支持,我假设我的系统是否将拥有最少 3500 个标签,并为每个标签分配一位;这导致每笔交易有 437 个字节,但最多只能为一笔交易提供 5 个标签。这里有什么优化吗?

解决方案_1:

我想过添加最小值(SMALL_INT)和最大值(SMALL_INT)以及标签列,并对其应用索引。

所以像这样

"SPORTS" = a = 1
"TENNIS" = b = 2
"CRICKET" = c = 3
...
...
"NIKE"  = z  = 26
"ADIDAS" = aa = 27
Run Code Online (Sandbox Code Playgroud)

所以我的列值是

`tag="|a|z|ay|bc|cq|"` //sorted?
`minTag=1`
`maxTag=95` //for cq
Run Code Online (Sandbox Code Playgroud)

而搜索鞋(ay=51)的查询是

maxTag <= 51 AND tag LIKE %|ay|%

而搜索鞋(ay=51)AND SIZE_12(cq=95)的查询是

minTag >= 51 AND maxTag <= 95 AND tag LIKE %|ay|%|cq|%

这会带来任何好处吗?请提出任何替代方案。

Bra*_*adC 5

我仍然坚信使用多对多查找表(桥接表)仍然是您最好的选择。您对匹配多行的担忧可以通过适当的查询设计来解决。假设您的表是:

CREATE TABLE purchases(PurchaseID,CustomerID,PurchaseDate,...)
CREATE TABLE tags(TagID,TagType,TagName)
CREATE TABLE purchasetags(PurchaseID,TagID)
Run Code Online (Sandbox Code Playgroud)

因此,每次购买都可以设置多个标签(无限制),为了好玩,我添加了按TagType对标签进行分类的功能,可能包含“ProductType”、“Brand”、“Color”、“Sport”等内容,所以你有办法告诉“鞋子”是“产品类型”标签,“耐克”是品牌标签,“足球”是一项运动标签。

然后,如果您想查询(并只返回单行),只需执行以下操作:

SELECT *
FROM purchases 
WHERE PurchaseID IN (SELECT pt.PurchaseID 
                     FROM purchasetag pt
                     INNER JOIN tags t ON pt.TagID=t.TagID
                     WHERE t.TagName IN ('Adidas','Nike'))
GROUP BY whatever...
Run Code Online (Sandbox Code Playgroud)

如果您需要进行更高级的组合搜索(查找购买的耐克鞋阿迪达斯鞋,您的查询也必须更高级:

SELECT *
FROM purchases 
WHERE PurchaseID IN (SELECT pt.PurchaseID 
                     FROM purchasetag pt
                     INNER JOIN tags t ON pt.TagID=t.TagID
                     WHERE t.TagName = 'Shoes')
AND   PurchaseID IN (SELECT pt.PurchaseID 
                     FROM purchasetag pt
                     INNER JOIN tags t ON pt.TagID=t.TagID
                     WHERE t.TagName IN ('Adidas','Nike'))
Run Code Online (Sandbox Code Playgroud)

同样,对于与您想要的标签组合相匹配的每次购买,这仍然返回一行。


Gri*_*ldi 2

解决此类问题的通常方法是使用位字段

因此,您将创建一个标签表,并通过 n:m 表将其链接到销售数据或产品。然后在标签表中,您将为每个标签分配一个唯一的位值(2 的幂),例如1, 2, 4, 8, ..., 1024, 2048, ...forsports, tennis, cricket, ...等。

然后,您可以使用bit_or这些值将其压缩为单个数值,并将其与产品或销售数据一起存储。例如,一件产品上的“运动”和“板球”标签变为 5 个。

如果可用的数字类型的位大小不足以存储所有标签,请使用多个这些字段并存储字段的数字或列名称以及标签的位值。

然后为了查询使用以下形式的子句:

flags & 1024 = 1024flags & 1024 <> 0= 第 10 个标志设置

您现在可以对标志执行任何布尔表达式。如果您为所有颜色指定一个字段,您还可以执行其他技巧,例如查询具有颜色标签的产品:colorflags <> 0等等。

由于您处于面向列的数据库(redshift)中,&因此仅对列中的每个唯一值执行一次。根据实现,数据库将通过分析 -&子句并通过列值的排序顺序使用大小限制(免费)来进一步减少这种情况。

如果您需要最后一点性能,您可以通过收集有关标志和查询的统计信息并将它们智能地分组在一起来实现一些技巧。我希望在您描述的用例中(在过滤后执行 sum ... group by ),与计算成本相比,您可以通过此获得的性能可以忽略不计。