Frage

Ich habe eine Tabelle, die voll von Artikeln aus verschiedenen Quellen. Einige der Quellen könnten die gleiche Position haben (in meinem Beispiel würde verschiedener BBC-Nachrichten-Feeds verschiedene Quellen, aber sie kommen alle aus der BBC). Jedes Element hat eine „eindeutige“ ID, die verwendet werden kann es unter anderem von der gleichen Stelle zu identifizieren. Dies bedeutet, dass Elemente auf einer Website auf die gleiche Nachrichtengeschichte beziehen, aber unter verschiedenen Feeds veröffentlichen die gleiche „unique ID“ haben, aber das ist nicht unbedingt weltweit einzigartig.

Das Problem ist, dass ich will bei Anzeigezeit Duplikate beseitigen, so dass (je nachdem, welche Feeds Sie sehen) erhalten Sie nur höchstens eine Version jeder Geschichte, auch wenn zwei oder drei Ihrer Feeds können Links enthalten es.

Ich habe eine sources Tabelle mit Informationen über jede Quelle und location_id und location_precedence Felder aus. Ich habe dann eine items Tabelle, die jedes Element enthält, dessen unique_id, source_id und content. Elemente mit dem gleichen unique_id und Quelle location_id höchstens einmal vorkommen sollen, mit der höchsten Quelle location_precedence zu gewinnen.

Ich habe gedacht, dass so etwas wie:

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

würde den Trick tun, aber das scheint die Lage Prioritätsfeld zu ignorieren. Was habe ich verpasst?


Beispieldaten:

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

Erwartete Inhalt nach Abfrage:

  • Die Geschichte über Thema One
  • Techy Güte
  • Foo, Bar und Quux vor Gericht Schlacht
  • Anti-Fubar Proteste verursachen Unruhe
  • Microsoft Bleh trifft lauwarmen Empfang
  • jemals daran gedacht, es in VB tun?
  • Celebrity hat 'neuen Freund'
  • Microsoft startet Bleh weltweit
  • Tech Round-up
  • Estates 'alte Nachrichten' sagen Regierung
  • iranischer Arzt vergiftet wurde "
  • Apple-Fans überglücklich durch iBlah

Ich habe versucht, eine Variante der Lösung von Andomar mit einigem Erfolg:

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

Mit AND s.location_priority = filter.prio Dingen fast arbeiten, wie ich will. Da ein Element aus mehreren Quellen mit der gleichen Priorität kommen kann, können Einzelteile wiederholt werden. In diesem Fall wird ein zusätzlicher GROUP BY i.unique_id auf der äußeren Abfrage macht den Job, und ich nehme an, es ist egal, welche Quelle „gewinnt“, wenn die Prioritäten gleich sind.

Ich hatte mit AND i.source_id = filter.source_id versucht stattdessen, die fast arbeitet (das heißt entfällt die zusätzliche GROUP BY), aber keine Ergebnisse aus den richtigen Quellen geben. In dem obigen Beispiel, es gibt mir „Weitere Updates auf Foo Fall“ (Quelle „BBC News England“) statt „Foo, Bar und Quux vor Gericht Schlacht“ (Quelle „BBC Technology News“. Auf die Ergebnisse der inneren Blick Abfrage, die ich erhalten:

unique_id: 'ghi'
source_id: 2
prio: 15

Beachten Sie, dass die Quell-ID nicht korrekt ist (erwartet: 3).

War es hilfreich?

Lösung

Order by lediglich die Zeilen anordnet, ist es nicht unter ihnen auswählen.

Eine der Möglichkeiten, um herauszufiltern Reihen mit einem niedrigen location_priority ist einen inner join als Filter zu verwenden:

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;

Eine Alternative ist ein where ... in <subquery> Klausel, zum Beispiel:

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

Dieses Problem ist auch bekannt als „Auswählen von Datensatz einen konzernweite maximale Halt.“ Quassnoi hat ein schöner Artikel auf sie.

EDIT: Eine Möglichkeit, Verbindungen mit mehreren Quellen mit der gleichen Priorität zu brechen ist eine WHERE Klausel mit einer Unterabfrage. Dieses Beispiel bricht Beziehungen auf 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 hat auch einen Artikel über Auswählen von Datensätzen Haltegruppenweise Maximum (Lösung Bindungen) :)

Andere Tipps

trete ein Selbst zu einer abgeleiteten Tabelle wie

select max(location_priority) from table where ...
  

Was habe ich verpasst?

Die ORDER BY passieren, nachdem die bereits GROUP BY jede Gruppe eine einzelne Reihe reduziert hat. Paul gibt eine Auflösung.

Was das Problem mit der Abfrage:

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 ist weder ein Aggregat noch gruppiert. Als Ergebnis, das Sie erhalten Wert unbestimmt ist.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top