Как я могу получить лучшие n записей каждой категории - PullRequest
1 голос
/ 01 сентября 2010

Я здесь, чтобы получать записи по категориям.

Моя таблица foo имеет поля [id, name, class] . мои записи могут быть такими:

1, ram, 10
2, hari, 9
3, sita, 10
4, gita, 9
5, rita, 5
6, tina, 7
8, nita, 8
9, bita, 5
10,seta, 7

... и не только ...

Теперь я хотел бы получить результат с каждой записью из другого класса .. т.е. что-то вроде

1, ram, 10
2, hari, 9
5, rita, 5
6, tina, 7
8, nita, 8

то есть просто top 1 записей по классу

Ответы [ 5 ]

6 голосов
/ 01 сентября 2010

Для SQL Server 2005+ и Oracle 9i + используйте аналитические функции:

WITH summary AS (
  SELECT f.id,
         f.name,
         f.class,
         ROW_NUMBER() OVER (PARTITION BY f.class
                                ORDER BY f.name) AS rank
    FROM FOO f)
SELECT s.id,
       s.name,
       s.class
  FROM summary s
 WHERE s.rank = 1

Здесь также используется общее табличное выражение (CTE), известное как факторинг подзапроса в Oracle ...

MySQL не имеет поддержки аналитических функций, поэтому вы должны использовать:

SELECT x.id,
       x.name,
       x.class
  FROM (SELECT f.id,
               f.name,
               f.class,
               CASE 
                 WHEN @class = f.class THEN @rownum := @rownum + 1 
                 ELSE @rownum := 1
               END AS rank,
               @class := f.class
          FROM FOO f
          JOIN (SELECT @rownum := 0, @class := '') r
      ORDER BY f.class, f.name) x
 WHERE x.rank = 1
4 голосов
/ 01 сентября 2010

Это должен быть самый простой способ, который не включает никаких специфичных для базы данных параметров:

select * 
  from foo 
 where id in (select min(id) 
                from foo 
               group by class);

upd: да, конечно, это будет работать, только если вам нужна только одна запись из каждого класса.

upd2: просто для интереса придумайте запрос, который покажет вам TOP N и не требует аналитики.выглядит немного беспорядочно, но, кажется, работает:)

select newfoo.id, newfoo.name, newfoo.class
  from (select class, max(r) top, min(r) bottom
          from (select f.*, rownum r
                  from (select id, name, class from foo order by class, id asc) f)
         group by class) minmax,
       (select id, name, class, r
          from (select f.*, rownum r
                  from (select id, name, class from foo order by class, id asc) f)) newfoo
 where newfoo.class = minmax.class
   and newfoo.r between minmax.bottom and
       least(minmax.bottom + (TOP_N-1), minmax.top);

, где TOP_N - количество записей, которое вам нужно получить.

3 голосов
/ 01 сентября 2010

Я тестировал в SQL 2008 это и работает для меня, надеюсь, это поможет вам в некотором роде.

DECLARE @Class TABLE
(
    id INT
    ,Name NVARCHAR(120)
    ,Class INT

    PRIMARY KEY (id)
)

INSERT INTO @Class values (1, 'ram', 10)
INSERT INTO @Class values (2, 'hari', 9)
INSERT INTO @Class values (3, 'sita', 10)
INSERT INTO @Class values (4, 'gita', 9)
INSERT INTO @Class values (5, 'rita', 5)
INSERT INTO @Class values (6, 'tina', 7)
INSERT INTO @Class values (8, 'nita', 8)
INSERT INTO @Class values (9, 'bita', 5)
INSERT INTO @Class values (10, 'seta', 7)

SELECT A.id, A.Name, A.Class
FROM
(
    SELECT ROW_NUMBER() OVER (PARTITION BY Class ORDER BY ID) as Num, ID, Name, Class
    FROM @Class
) A
WHERE A.Num = 1
ORDER BY id
1 голос
/ 01 сентября 2010

В случае SQL Server или Oracle (или любого другого механизма, реализующего эту часть стандарта, включая, например, PostgreSQL среди бесплатных), «оконные функции» в предложении OVER (например, см. здесь для MS документы о них) сделать это легко; например, в этом SO вопросе см. ответ @ Darrel (он выбирает 10 лучших в каждой категории, вам нужна только 1 верхняя, изменения должны быть очевидны; -).

В MySql или другом движке, не соответствующем стандарту, касающемуся условия OVER, вы можете использовать ответ @ Bill (хорошо для MySql, но не для других) или @ Matt (может потребоваться небольшая адаптация, поскольку он отвечает за SQL Server). и поэтому используя SELECT TOP 10 ... - в MySql это будет SELECT ... LIMIT 10! -).

0 голосов
/ 01 сентября 2010

Вот еще один способ

    DECLARE @foo TABLE(ID INT,Name VARCHAR(20),Class INT)
INSERT INTO @foo
SELECT 1,'ram', 10 UNION ALL
SELECT 2, 'hari', 9 UNION ALL 
SELECT 3, 'sita', 10  UNION ALL
SELECT 4, 'gita', 9  UNION ALL
SELECT 5, 'rita', 5  UNION ALL
SELECT 6, 'tina', 7  UNION ALL
SELECT 8, 'nita', 8  UNION ALL
SELECT 9, 'bita', 5  UNION ALL
SELECT 10,'seta', 7

SELECT DISTINCT X.*
FROM @foo f
CROSS APPLY(SELECT TOP 1 * FROM @foo WHERE Class = f.Class) AS X
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...