Виберіть лише суперсети


10

У мене є дві таблиці (разом з некластеризованим індексом), які можна створити за допомогою наведених нижче команд:

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

Хоча технічно мої таблиці трохи відрізняються, і я приєднуюся до кількох інших таблиць, це підходящий проксі для моєї ситуації.

  • Я хотів би вибрати все, GroupKeysщо не є підмножинами іншого GroupKey.
  • Для даного суперсети я хотів би захопити максимум GroupScoreусіх його підмножин (включаючи себе).
  • У випадку, коли a GroupKeyмістить те саме, що RecordKeysі інше GroupKey(s), хапається лише один із них GroupKeys(не має значення, який з них).
  • Будь-який, GroupKeyхто має те саме, що RecordKeysй інший GroupKey(s), також матиме те саме GroupScore.
  • Неспоріднені GroupKeysможуть мати таку ж оцінку.

Нижче наводиться приклад для ілюстрації того, що я прошу:

              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

Я хотів би, щоб результат був таким:

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

GroupTableмає близько 75М рядків і RecordTableмає близько 115М рядків; однак, після приєднання та WHEREприсудка, зазвичай, в цей день буде близько 20 тис. рядків.

Прошу вибачення, якщо це питання є тривіальним, але я чомусь справді борюся з цим.

Відповіді:


7

Я хотів би, щоб результат був таким:

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

Використання корельованих підзапитів - один із способів отримати потрібний результат.

  • У випадку, коли GroupKey містить такі самі точні записи RecordKeys, як і інші GroupKey, тоді схоплено лише один з цих GroupKeys (не важливо, який саме).

Я повертаю Групу з найнижчим GroupKey, коли є матч, але це довільно, як ви кажете, це не має значення.

дані тесту:

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

запит:

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

Підзапит у SELECT отримує найвищий рівень GroupScoreлише з тих груп, які є підмножинами цієї групи ('g1'). Це досягається шляхом підрахунку UNION RecordKey's для' g1 'безлічі та кожного' g2 '. Якщо UNION більше, ніж набір 'g1', у наборі RecordKey'g2' має бути принаймні один без відповідного RecordKeyнабору 'g1', тому набір 'g2' не є підмножиною і не повинен вважатися для цей ряд.

У пункті WHERE є два випадки, які слід розглянути для фільтрації. В будь-якому випадку набір 'g1' фільтрується лише у тому випадку, якщо всі 'g1' RecordKeyтакож є у наборі 'g3'; і ця перевірка досягається шляхом підрахунку об'єднання ще раз (відповідно до пункту SELECT).

Два випадки: set набір 'g1' має менше RecordKeys ( g3.RecordCount>g1.RecordCount; у цьому випадку ми фільтруємо), і set набір 'g1' ідентичний набору 'g3' ( g3.RecordCount=g1.RecordCount; у такому випадку ми довільно обираємо набір із нижній GroupKey)

вихід:

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

dbfiddle тут


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.