Seleziona solo Superset


10

Ho due tabelle (insieme a un indice non cluster) che possono essere create con i comandi seguenti:

CREATE TABLE GroupTable
(
  GroupKey int NOT NULL PRIMARY KEY, 
  RecordCount int NOT NULL,
  GroupScore float NOT NULL
);

CREATE TABLE RecordTable
(
  RecordKey varchar(10) NOT NULL, 
  GroupKey int NOT NULL,
  PRIMARY KEY(RecordKey, GroupKey)
);

CREATE UNIQUE INDEX ixGroupRecord ON RecordTable(GroupKey, RecordKey);

Mentre tecnicamente i miei tavoli sono leggermente diversi e mi sto unendo ad alcuni altri tavoli, questo è un proxy adatto alla mia situazione.

  • Vorrei selezionare tutto GroupKeysciò che non è un sottoinsieme di un altro GroupKey.
  • Per un determinato superset, vorrei prendere il massimo GroupScoredi tutti i suoi sottoinsiemi (incluso se stesso).
  • Nel caso in cui a GroupKeycontenga esattamente lo stesso RecordKeysdi un altro GroupKey(s), solo uno di questi GroupKeysviene catturato (non importa quale).
  • Chiunque GroupKeyabbia lo stesso identico RecordKeysa un altro GroupKey(s)avrà anche lo stesso GroupScore.
  • Anche i non correlati GroupKeyspossono avere lo stesso punteggio.

Di seguito è riportato un esempio per illustrare ciò che sto chiedendo:

              GroupTable                          RecordTable

GroupKey    RecordCount   GroupScore         RecordKey    GroupKey
------------------------------------         ---------------------
  1              3            6.2                A          1
  29             2            9.8                A          29
  95             3            6.2                A          95
  192            4            7.1                A          192
                                                 B          1
                                                 B          29
                                                 B          95
                                                 B          192
                                                 C          1
                                                 C          95
                                                 D          192
                                                 E          192

Vorrei che l'output fosse il seguente:

GroupKey    RecordCount    GroupScore
-------------------------------------
  1              3             9.8
  192            4             9.8

GroupTableha circa 75 milioni di righe e RecordTablecirca 115 milioni di righe; tuttavia, dopo i join e il WHEREpredicato, in un determinato giorno tende ad esserci circa 20k righe.

Mi scuso se questa domanda è banale, ma per qualche ragione sto davvero lottando con essa.

Risposte:


7

Vorrei che l'output fosse il seguente:

 GroupKey    RecordCount    GroupScore
 -------------------------------------
   1              3             9.8
   192            4             7.1

L'uso di subquery correlate è un modo per ottenere l'output desiderato.

  • Nell'istanza in cui un GroupKey contiene gli stessi RecordKey esatti di un altro GroupKey, viene catturato solo uno di quei GroupKey (non importa quale).

Sto restituendo il gruppo con il GroupKey più basso quando c'è una partita, ma è arbitrario come dici tu non importa.

dati di test:

INSERT INTO RecordTable(RecordKey,GroupKey)
VALUES ('A',1)
     , ('A',29)
     , ('A',95)
     , ('A',192)
     , ('B',1)
     , ('B',29)
     , ('B',95)
     , ('B',192)
     , ('C',1)
     , ('C',95)
     , ('D',192)
     , ('E',192);

INSERT INTO GroupTable(GroupKey,RecordCount,GroupScore)
VALUES (1,3,6.2)     -- ABC
     , (29,2,9.8)    -- AB
     , (95,3,6.2)    -- ABC
     , (192,4,7.1);  -- ABDE
GO

query:

SELECT GroupKey
     , RecordCount
     , GroupScore = ( SELECT max(GroupScore)
                      FROM GroupTable g2 
                      WHERE ( SELECT count(*)
                              FROM ( SELECT RecordKey
                                     FROM RecordTable
                                     WHERE GroupKey=g1.GroupKey
                                     UNION
                                     SELECT RecordKey
                                     FROM RecordTable
                                     WHERE GroupKey=g2.GroupKey ) z
                            )=g1.RecordCount )
FROM GroupTable g1
WHERE NOT EXISTS ( SELECT *
                   FROM GroupTable g3
                   WHERE ( SELECT count(*)
                           FROM ( SELECT RecordKey
                                  FROM RecordTable 
                                  WHERE GroupKey=g1.GroupKey 
                                  UNION
                                  SELECT RecordKey 
                                  FROM RecordTable 
                                  WHERE GroupKey=g3.GroupKey ) z )=g3.RecordCount
                         AND ( g3.RecordCount>g1.RecordCount 
                               OR ( g3.RecordCount=g1.RecordCount 
                                    AND g3.GroupKey<g1.GroupKey ) ) );
GO

La sottoquery in SELECT ottiene il massimo GroupScoresolo dai gruppi che sono sottoinsiemi di questo gruppo ('g1'). Ciò si ottiene contando l'UNIONE del RecordKey's per il set' g1 'e ogni set' g2 '. Se UNION è più grande del set 'g1', deve essercene almeno uno RecordKeynel set 'g2' senza un corrispondente RecordKeyper il set 'g1', quindi il set 'g2' non è un sottoinsieme e non dovrebbe essere preso in considerazione per questa riga.

Nella clausola WHERE, ci sono due casi da considerare per il filtraggio. In entrambi i casi, il set 'g1' viene filtrato solo se tutti i 'g1' RecordKeysono presenti anche nel set 'g3'; e questo controllo si ottiene contando nuovamente l'unione (secondo la clausola SELECT).

I due casi sono: ① il set 'g1' ha meno RecordKeys ( g3.RecordCount>g1.RecordCount; nel qual caso filtriamo), e ② il set 'g1' è identico al set 'g3' ( g3.RecordCount=g1.RecordCount; nel qual caso scegliamo arbitrariamente il set con il inferiore GroupKey)

produzione:

/*
|GroupKey|RecordCount|GroupScore|
|-------:|----------:|---------:|
|       1|          3|       9.8|
|     192|          4|       9.8|
*/

dbfiddle qui


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.