Speichern mehrerer Tags in der Analysedatenbank

7

Ich möchte benutzerdefinierte Tags für Benutzerkäufe bei jeder Transaktion speichern. Wenn Benutzer beispielsweise Schuhe gekauft haben, sind dies Tags "SPORTS", "NIKE", SHOES, COLOUR_BLACK, SIZE_12,..

Diese Tags sind diejenigen Verkäufer, die daran interessiert sind, eine Anfrage zu stellen, um die Verkäufe zu verstehen.

Meine Idee ist, wann immer ein neues Tag hereinkommt, neuen Code (so etwas wie Hashcode, aber sequentiell) für dieses Tag zu erstellen, und der Code beginnt mit "a-z"26 Buchstaben und "aa, ab, ac...zz"geht dann weiter. Behalten Sie nun alle in einer Transaktion angegebenen Tags in der einen Spalte, die tag (varchar)durch Trennen mit aufgerufen wird "|".

Nehmen wir an, Mapping ist (auf Anwendungsebene)

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

Wenn Sie also die oben genannte Kauftransaktion speichern, tag="|a|z|ay|bc|cq|"sieht das Tag wie folgt aus. Jetzt kann der Verkäufer die Anzahl der verkauften SCHUHE suchen, indem er die WHEREBedingung hinzufügt tag LIKE %|ay|%. Jetzt ist das Problem, dass ich keinen Index (Sortierschlüssel in Rotverschiebungsdatenbank) für "LIKE beginnt mit%" verwenden kann. Wie kann man dieses Problem lösen, da ich möglicherweise 100 Millionen Datensätze habe? Ich möchte keinen vollständigen Tabellenscan.

Gibt es eine Lösung, um dies zu beheben?

Update_1: Ich habe das bridge tableKonzept (Querverweistabelle) nicht befolgt, da ich nach dem Durchsuchen der angegebenen Tags eine Gruppierung der Ergebnisse durchführen möchte. Meine Lösung gibt nur eine Zeile an, wenn zwei Tags in einer einzigen Transaktion übereinstimmen, aber die Brückentabelle gibt mir zwei Zeilen? dann wird meine Summe () verdoppelt.

Ich habe einen Vorschlag wie unten

EXISTS (SELECT 1 FROM transaction_tag WHERE tag_id = 'zz' und trans_id = tr.trans_id) in der WHERE-Klausel einmal für jedes Tag (Hinweis: Es wird davon ausgegangen, dass tr ein Alias ​​für die Transaktionstabelle in der umgebenden Abfrage ist).

Ich bin dem nicht gefolgt; da ich AND- und OR-Bedingungen für die Tags ausführen muss, Beispiel ("SPORTS" UND "ADIDAS") ---- "SHOE" AND ("NIKE" ODER "ADIDAS")

Update_2: Ich bin dem Bitfeld nicht gefolgt, da ich nicht weiß, dass Redshift diese Unterstützung bietet. Ich gehe auch davon aus, dass mein System mindestens 3500 Tags haben wird, und ordne jedem ein Bit zu. Dies ergibt 437 Bytes für jede Transaktion, obwohl für eine Transaktion nur maximal 5 Tags angegeben werden können. Irgendeine Optimierung hier?

Lösung_1:

Ich habe darüber nachgedacht, min (SMALL_INT) und max value (SMALL_INT) zusammen mit der Tags-Spalte hinzuzufügen und darauf einen Index anzuwenden.

so etwas wie das

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

Meine Spaltenwerte sind also

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

Und Abfrage für die Suche Schuh (ay = 51) ist

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

Und die Abfrage für die Suche nach Schuh (ay = 51) UND SIZE_12 (cq = 95) ist

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

Wird dies einen Nutzen bringen? Bitte schlagen Sie Alternativen vor.

Kanagavelu Sugumar
quelle
2
Haben Sie eine transaction_tagTabelle betrachtet, die verknüpft ist transactionund tagin einer Viele-zu-Viele-Beziehung steht? In Bezug auf die Leistung ist es in der Regel eine schlechte Idee, mehrere Werte als einfach begrenzten Text in einer einzelnen Spalte zu speichern.
RDFozz
@RDFozz Ich möchte nach dem Durchsuchen der angegebenen Tags eine Gruppierung nach den Ergebnissen durchführen. Meine Lösung gibt nur eine Zeile an, wenn zwei Tags in einer einzigen Transaktion übereinstimmen, aber die Brückentabelle gibt mir zwei Zeilen? dann wird meine Summe () verdoppelt. Gibt es eine Lösung, um dies zu beheben?
Kanagavelu Sugumar
@RDFozz Auch ich habe keinen echten Wert gespeichert, nur einen codierten Wert (max. 2 Zeichen) bis zu 5 Tags -> also 10 Zeichen + 6 Zeichen (Begrenzer). Ich denke, es ist eine einfache Spalte mit varchar (16) und das wird gegen den LIKE-Operator validiert. Aber Brückentabelle wird Suche nach "5 mal * Anzahl der tatsächlichen Zeilen" machen, ich denke hier mehr RAM nach dem Join erforderlich.
Kanagavelu Sugumar
@RDFozz Wenn Sie beschäftigt sind, entfernen Sie bitte Ihre Ablehnung, damit ich einige Antworten von anderen bekomme.
Kanagavelu Sugumar
Kann entweder durch Betrogenen beseitigen INNER JOINzu transaction_tageinmal für jeden Tag angefordert oder unter Verwendung von EXISTS (SELECT 1 FROM transaction_tag WHERE tag_id = 'zz' and trans_id = tr.trans_id)in der WHEREeinmal Klausel für jeden Tag (Hinweis: nimmt an tr ein Alias für das ist transactionTabelle in der umgebenden Abfrage).
RDFozz

Antworten:

5

Ich bin immer noch davon überzeugt, dass die Verwendung einer Viele-zu-Viele-Nachschlagetabelle (einer Brückentabelle) hier immer noch die beste Option ist. Ihre Bedenken hinsichtlich des Abgleichs mehrerer Zeilen können durch ein ordnungsgemäßes Abfragedesign behoben werden. Angenommen, Ihre Tabellen sind:

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

Für jeden Kauf können mehrere Tags festgelegt werden (ohne Limit). Aus Spaß habe ich die Möglichkeit hinzugefügt, die Tags nach TagType zu kategorisieren. Vielleicht enthält sie Dinge wie "ProductType", "Marke", "Farbe", "Sport", so dass Sie eine Möglichkeit haben , dass die „Schuhe“ zu sagen , ist ein „Product“ tag „Nike“ ist eine Marke - Tag, und „Fußball“ ist ein Sport - Tag.

Wenn Sie dann abfragen möchten (und nur einzelne Zeilen zurückgeben möchten), gehen Sie einfach wie folgt vor:

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

Wenn Sie ausgefallenere Kombinationssuchen durchführen müssen (Kauf von Nike-Schuhen oder Adidas-Schuhen finden) , muss Ihre Anfrage auch ausgefallener sein:

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

Auch hier wird für jeden Einkauf eine einzelne Zeile zurückgegeben, die Ihrer gewünschten Tag-Kombination entspricht.

BradC
quelle
Plus eins. Vielen Dank! Lassen Sie mich das versuchen und werden sich bei Ihnen melden !!
Kanagavelu Sugumar
Ich habe diese Antwort nicht akzeptiert, da IN-Abfragen mit vielen IDs Auswirkungen auf die Leistung hatten.
Kanagavelu Sugumar
2

Der übliche Weg, um ein solches Problem zu lösen, ist die Verwendung eines Bitfelds .

Sie würden also eine Tags-Tabelle erstellen und diese über eine n: m-Tabelle mit den Verkaufszahlen oder Produkten verknüpfen. Dann würden Sie in der Tags-Tabelle für jedes Tag einen eindeutigen Bitwert als Potenz von 2 zuweisen, z. B. 1, 2, 4, 8, ..., 1024, 2048, ...für sports, tennis, cricket, ...und so weiter.

Mit können bit_orSie diese Werte dann zu einem einzigen numerischen Wert zusammenfassen und neben den Produkt- oder Verkaufszahlen speichern. Zum Beispiel werden die Tags "Sport" und "Cricket" auf einem Produkt zu 5.

Wenn die Bitgröße des verfügbaren numerischen Typs nicht ausreicht, um alle Ihre Tags zu speichern, verwenden Sie mehrere dieser Felder und speichern Sie die Nummer oder den Spaltennamen des Felds und den Bitwert mit dem Tag.

Verwenden Sie dann zum Abfragen Klauseln des Formulars:

flags & 1024 = 1024 oder flags & 1024 <> 0= 10. Flag gesetzt

Sie können jetzt einen beliebigen booleschen Ausdruck für die Flags ausführen. Wenn Sie ein einzelnes Feld für alle Farben festlegen, können Sie auch andere Tricks ausführen, z. B. die Abfrage von Produkten mit einem Color-Tag: colorflags <> 0und so weiter.

Da Sie sich in einer spaltenorientierten Datenbank befinden (Rotverschiebung), &wird diese nur einmal pro eindeutigem Wert in der Spalte ausgeführt. Abhängig von der Implementierung wird die Datenbank dies weiter reduzieren, indem sie die &-klausel analysiert und Größenbeschränkungen durch Sortierreihenfolge der Spaltenwerte verwendet (kostenlos).

Und wenn Sie das letzte Stück Leistung benötigen, können Sie Tricks ausführen, indem Sie Statistiken zu den Flags und Abfragen sammeln und diese intelligent gruppieren. Ich gehe davon aus, dass in dem von Ihnen beschriebenen Anwendungsfall (Summengruppe nach Filterung durchführen) die Leistung, die Sie dadurch erzielen könnten, im Vergleich zu den Berechnungskosten vernachlässigbar ist.

Grimaldi
quelle
1

Tabellen

brand 
ID
Name  

size 
ID
name  

color 
ID 
name  

customer 
ID 
Name  

purchase 
ID
customerID
date 

purchaseDetail 
purchaseID  
brandID 
sizeID 
colorID

Sie benötigen purchaseDetail, um alle Tags dem Artikel zuzuordnen

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' 
Paparazzo
quelle
Plus eins. Vielen Dank! Ich werde es ausprobieren und mich bei Ihnen melden!
Kanagavelu Sugumar