使用优先级聚合SQL行

时间:2009-12-06 12:46:44

标签: sql mysql group-by aggregation

我有一张桌子,里面装满了不同来源的物品。一些来源可能具有相同的位置(在我的示例中,不同的BBC新闻源将是不同的来源,但它们都来自BBC)。每个项目都有一个“唯一”ID,可用于从同一位置识别它。这意味着与网站上相同新闻报道相关但在不同Feed下发布的相关项目将具有相同的“唯一ID”,但这不一定是全球唯一的。

问题是我想在显示时删除重复项,以便(根据您看到的哪些Feed)您最多只能获得每个故事的一个版本,即使您的两个或三个Feed可能包含指向它

我有一个sources表,其中包含有关每个来源以及location_idlocation_precedence字段的信息。然后,我有一个items表,其中包含每个项目,unique_idsource_idcontent。具有相同unique_id和来源location_id的项目最多只能出现一次,获得最高来源location_precedence

我会想到这样的事情:

SELECT `sources`.`name` AS `source`,
       `items`.`content`,
       `items`.`published`
FROM `items` INNER JOIN `sources`
  ON `items`.`source_id` = `sources`.`id` AND `sources`.`active` = 1
GROUP BY `items`.`unique_id`, `sources`.`location_id`
ORDER BY `sources`.`location_priority` DESC

会做到这一点,但这似乎忽略了位置优先级字段。我错过了什么?


示例数据:

CREATE TABLE IF NOT EXISTS `sources` (
  `id` int(10) unsigned NOT NULL auto_increment,
  `location_id` int(10) unsigned NOT NULL,
  `location_priority` int(11) NOT NULL,
  `active` tinyint(1) unsigned NOT NULL default '1',
  `name` varchar(150) NOT NULL,
  `url` text NOT NULL,
  PRIMARY KEY  (`id`),
  KEY `active` (`active`)
);

INSERT INTO `sources` (`id`, `location_id`, `location_priority`, `active`, `name`, `url`) VALUES
(1, 1, 25, 1, 'BBC News Front Page', 'http://newsrss.bbc.co.uk/rss/newsonline_uk_edition/front_page/rss.xml'),
(2, 1, 10, 1, 'BBC News England', 'http://newsrss.bbc.co.uk/rss/newsonline_uk_edition/england/rss.xml'),
(3, 1, 15, 1, 'BBC Technology News', 'http://newsrss.bbc.co.uk/rss/newsonline_uk_edition/technology/rss.xml'),
(4, 2, 0, 1, 'Slashdot', 'http://rss.slashdot.org/Slashdot/slashdot'),
(5, 3, 0, 1, 'The Daily WTF', 'http://syndication.thedailywtf.com/TheDailyWtf');

CREATE TABLE IF NOT EXISTS `items` (
  `id` bigint(20) unsigned NOT NULL auto_increment,
  `source_id` int(10) unsigned NOT NULL,
  `published` datetime NOT NULL,
  `content` text NOT NULL,
  `unique_id` varchar(255) NOT NULL,
  PRIMARY KEY  (`id`),
  UNIQUE KEY `unique_id` (`unique_id`,`source_id`),
  KEY `published` (`published`),
  KEY `source_id` (`source_id`)
);

INSERT INTO `items` (`id`, `source_id`, `published`, `content`, `unique_id`) VALUES
(1,  1, '2009-12-01 16:25:53', 'Story about Subject One',                     'abc'),
(2,  2, '2009-12-01 16:21:31', 'Subject One in story',                        'abc'),
(3,  3, '2009-12-01 16:17:20', 'Techy goodness',                              'def'),
(4,  2, '2009-12-01 16:05:57', 'Further updates on Foo case',                 'ghi'),
(5,  3, '2009-12-01 15:53:39', 'Foo, Bar and Quux in court battle',           'ghi'),
(6,  2, '2009-12-01 15:52:02', 'Anti-Fubar protests cause disquiet',          'mno'),
(7,  4, '2009-12-01 15:39:00', 'Microsoft Bleh meets lukewarm reception',     'pqr'),
(8,  5, '2009-12-01 15:13:45', 'Ever thought about doing it in VB?',          'pqr'),
(9,  1, '2009-12-01 15:13:15', 'Celebrity has 'new friend'',        'pqr'),
(10, 1, '2009-12-01 15:09:57', 'Microsoft launches Bleh worldwide',           'stu'),
(11, 2, '2009-12-01 14:57:22', 'Microsoft launches Bleh in UK',               'stu'),
(12, 3, '2009-12-01 14:57:22', 'Microsoft launches Bleh',                     'stu'),
(13, 3, '2009-12-01 14:42:15', 'Tech round-up',                               'vwx'),
(14, 2, '2009-12-01 14:36:26', 'Estates 'old news' say government', 'yza'),
(15, 1, '2009-12-01 14:15:21', 'Iranian doctor 'was poisoned'',     'bcd'),
(16, 4, '2009-12-01 14:14:02', 'Apple fans overjoyed by iBlah',               'axf');

查询后的预期内容:

  • 关于主题一的故事
  • 狡猾善良
  • Foo,Bar和Quux在法庭上的战斗
  • 抗Fubar抗议导致不安
  • Microsoft Bleh遇到不冷不热的接待
  • 曾经想过在VB中这样做吗?
  • 名人有'新朋友'
  • Microsoft在全球范围内推出Bleh
  • Tech round-up
  • 庄园的旧闻'说政府
  • 伊朗医生'中毒了'
  • 苹果粉丝喜欢iBlah

我尝试了Andomar对解决方案的一些修改,取得了一些成功:

SELECT      s.`name` AS `source`,
            i.`content`,
            i.`published`
FROM        `items` i
INNER JOIN  `sources` s
ON          i.`source_id` = s.`id`
AND         s.`active` = 1
INNER JOIN (
  SELECT `unique_id`, `source_id`, MAX(`location_priority`) AS `prio` 
  FROM `items` i
  INNER JOIN `sources` s ON s.`id` = i.`source_id` AND s.`active` = 1
  GROUP BY `location_id`, `unique_id`
) `filter`
ON          i.`unique_id` = `filter`.`unique_id`
AND         s.`location_priority` = `filter`.`prio`
ORDER BY    i.`published` DESC
LIMIT 50

随着AND s.location_priority = filter.prio事情几乎按照我的意愿运作。由于项目可以来自具有相同优先级的多个来源,因此可以重复项目。在这种情况下,外部查询的额外GROUP BY i.unique_id执行该作业,并且我认为如果优先级相等,哪个源“获胜”无关紧要。

我尝试使用AND i.source_id = filter.source_id,这几乎可以正常工作(即消除了额外的GROUP BY),但没有给出正确来源的结果。在上面的例子中,它给了我“Foo案例的进一步更新”(来源“英国广播公司新闻英格兰”),而不是“法庭战中的Foo,Bar和Quux”(来源“BBC技术新闻”。看看内心的结果查询,我得到:

unique_id: 'ghi'
source_id: 2
prio: 15

请注意,源ID不正确(预期:3)。

3 个答案:

答案 0 :(得分:5)

Order by只是命令行,它不会在它们中间选择。

过滤掉location_priority较低行的方法之一是使用inner join作为过滤器:

SELECT     s.name, i.content, i.published
FROM       items i 
INNER JOIN sources s
ON         i.source_id = s.id
AND        s.active = 1
INNER JOIN (
    SELECT unique_id, max(location_priority) as prio
    FROM items i
    INNER JOIN sources s ON s.id = i.source_id AND s.active = 1
    GROUP BY unique_id) filter
ON         i.unique_id = filter.unique_id
AND        s.location_priority = filter.prio;

替代方案是where ... in <subquery>子句,例如:

SELECT     s.name, i.content, i.published
FROM       items i 
INNER JOIN sources s
ON         i.source_id = s.id
AND        s.active = 1
WHERE      (i.unique_id, s.location_priority) IN (
    SELECT unique_id, max(location_priority)
    FROM items i
    INNER JOIN sources s ON s.id = i.source_id AND s.active = 1
    GROUP BY unique_id
);

此问题也称为“选择包含组范围最大值的记录”。 Quassnoi在其上写了nice article

编辑:以相同优先级打破与多个源的关系的一种方法是带有子查询的WHERE子句。此示例打破了i.id DESC上的关联:

SELECT     s.name, i.unique_id, i.content, i.published
FROM       (
           SELECT unique_id, min(location_priority) as prio
           FROM items i
           INNER JOIN sources s ON s.id = i.source_id AND s.active = 1
           GROUP BY unique_id
           ) filter
JOIN       items i
JOIN       sources s
ON         s.id = i.source_id 
           AND s.active = 1
WHERE      i.id =
           (
           SELECT   i.id
           FROM     items i
           JOIN     sources s 
           ON       s.id = i.source_id 
                    AND s.active = 1
           WHERE    i.unique_id = filter.unique_id
           AND      s.location_priority = filter.prio
           ORDER BY i.id DESC
           LIMIT 1
           )

Quassnoi还有一篇关于selecting records holding group-wise maximum (resolving ties)的文章:)

答案 1 :(得分:1)

自我加入到派生表,如

select max(location_priority) from table where ...

答案 2 :(得分:0)

  

我错过了什么?

ORDER BYGROUP BY已将每个组缩减为一行后发生。保罗给出了一个决议。

至于查询的问题:

SELECT `unique_id`, `source_id`, MAX(`location_priority`) AS `prio` 
FROM `items` i
INNER JOIN `sources` s ON s.`id` = i.`source_id` AND s.`active` = 1
GROUP BY `location_id`, `unique_id`

source_id既不是聚合也不是分组。结果,你获得的价值是不确定的。