web-dev-qa-db-ja.com

分析データベースに複数のタグを保存する

ユーザーが購入したカスタムタグをトランザクションごとに保存したいと思います。たとえば、ユーザーが靴を購入した場合、タグは_"SPORTS", "NIKE", SHOES, COLOUR_BLACK, SIZE_12,.._になります。

これらのタグは、売り手に問い合わせて売り上げを理解することに関心のある売り手です。

私の考えは、新しいタグが入ってくると、そのタグの新しいコード(ハッシュコードのようなものですがシーケンシャル)を作成し、コードは_"a-z"_ 26文字から始まり、次に_"aa, ab, ac...zz"_が続きます。 _"|"_で区切って、tag (varchar)と呼ばれる1つの列に1つのトランザクションで指定されたすべてのタグを保持します。

マッピングが(アプリケーションレベルで)であると仮定しましょう

_"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
...
_

したがって、上記の購入トランザクションを保存すると、タグは_tag="|a|z|ay|bc|cq|"_のようになります。これで、WHERE条件_tag LIKE %|ay|%_を追加して、販売者が販売した靴の数を検索できるようになります。ここでの問題は、「LIKEが%で始まる」のインデックス(redshift dbのソートキー)を使用できないことです。では、1億のレコードがある可能性があるため、この問題を解決するにはどうすればよいですか?全表スキャンを望まない.

これを修正する解決策はありますか?

Update_1:指定したタグを検索した後、結果に対してグループ化を実行したいので、_bridge table_の概念(相互参照テーブル)を使用していません。 1つのトランザクションで2つのタグが一致した場合、私のソリューションでは1行しか表示されませんが、ブリッジテーブルでは2行表示されますか?その後、私のsum()は2倍になります。

以下のような提案を受けました

タグごとに1回、WHERE句でEXISTS(SELECT 1 FROM transaction_tag WHERE tag_id = 'zz' and trans_id = tr.trans_id)(注:trは周囲のクエリのトランザクションテーブルのエイリアスであると想定)

私はこれに従っていません。 ANDとOR条件をタグで実行する必要があるため、例( "SPORTS" AND "ADIDAS")---- "SHOE" AND( "NIKE" OR "ADIDAS")

Update_2:私はビットフィールドをフォローしていません。Redshiftがこのサポートを持っていることを知らないため、システムに最低3500のタグがあり、それぞれに1ビットを割り当てると想定しています。これにより、トランザクションごとに437バイトになりますが、トランザクションには最大5つのタグしか指定できません。ここで何か最適化しますか?

Solution_1:

タグ列とともに最小値(SMALL_INT)と最大値(SMALL_INT)を追加し、それにインデックスを適用することを考えました。

こんな感じ

_"SPORTS" = a = 1
"TENNIS" = b = 2
"CRICKET" = c = 3
...
...
"NIKE"  = z  = 26
"ADIDAS" = aa = 27
_

だから私の列の値は

_`tag="|a|z|ay|bc|cq|"` //sorted?
`minTag=1`
`maxTag=95` //for cq
_

そして、shoe(ay = 51)を検索するクエリは

_maxTag <= 51 AND tag LIKE %|ay|%_

そして、shoe(ay = 51)AND SIZE_12(cq = 95)を検索するクエリは

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

これは何かメリットがありますか?親切に代替案を提案してください。

7

ここでも、多対多のルックアップテーブル(ブリッジテーブル)を使用するのが最善の方法であると確信しています。複数の行の一致に関する懸念は、適切なクエリ設計によって修正できます。テーブルが次のとおりだとします。

CREATE TABLE purchases(PurchaseID,CustomerID,PurchaseDate,...)
CREATE TABLE tags(TagID,TagType,TagName)
CREATE TABLE purchasetags(PurchaseID,TagID)

したがって、各購入には複数のタグセット(制限なし)を含めることができ、お楽しみのために、タグをTagTypeで分類する機能を追加しました。 Color」、「Sport」、「shoes」は「ProductType」タグ、「Nike」はブランドタグ、「soccer」は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...

より洗練されたコンボ検索(Nikeシューズまたはアディダスシューズの購入を見つける)を行う必要がある場合、クエリもより洗練されている必要があります。

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'))

この場合も、目的のタグの組み合わせに一致する購入ごとに1行が返されます。

5
BradC

このような問題を解決する通常の方法は、 bitfield を使用することです。

したがって、タグテーブルを作成し、それをn:mテーブルを介して売上高または製品にリンクします。次に、タグテーブルで、タグごとに2の累乗として一意のビット値を割り当てます。 1, 2, 4, 8, ..., 1024, 2048, ... for sports, tennis, cricket, ...など。

bit_orを使用すると、これらの値を単一の数値に圧縮し、これを製品または売上の数値と一緒に保存できます。たとえば、1つの製品のタグ「sports」と「cricket」は5になります。

使用可能な数値タイプのビットサイズがすべてのタグを格納するのに十分でない場合は、これらのフィールドの複数を使用して、フィールドの番号または列名とタグ付きのビット値を格納します。

次に、次の形式の使用句をクエリします。

flags & 1024 = 1024またはflags & 1024 <> 0 = 10番目のフラグセット

これで、フラグに対してブール式を実行できます。すべての色に単一のフィールドを指定すると、Colorタグのある製品のクエリなど、colorflags <> 0などの他のトリックも実行できます。

列指向のデータベース(redshift)を使用しているため、&は列の一意の値ごとに1回だけ実行されます。実装に応じて、データベースは&節を分析し、列値のソート順(無料)を通じてサイズの制約を使用することにより、これをさらに削減します。

そして、最後のパフォーマンスが必要な場合は、フラグとクエリに関する統計を収集し、それらをインテリジェントにグループ化することで、トリックを実行できます。私はあなたが説明しているユースケースでは(フィルタリングの後に合計を実行する...グループ化する)、これによって得られる可能性のあるパフォーマンスは計算のコストと比較して無視できるでしょう。

2
Grimaldi

テーブル

brand 
ID
Name  

size 
ID
name  

color 
ID 
name  

customer 
ID 
Name  

purchase 
ID
customerID
date 

purchaseDetail 
purchaseID  
brandID 
sizeID 
colorID

すべてのタグをアイテムに関連付けるには、purchaseDetailが必要です

select * 
from purchase 
join purchaseDetail 
  on purchase.ID = purchaseDetail.purchaseID 
join brand 
  on brand.ID    = purchaseDetail.brandID 
 and brand.Name in ('Nike', 'Adidas')
join size 
  on size.ID     = purchaseDetail.sizeID
join color 
  on color.ID    = purchaseDetail.colorID 
 and color.Name = 'black' 
1
paparazzo