Только выбрать суперсеты


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для набора «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.