Выберите 10 лучших записей для каждой категории

Я хочу вернуть 10 лучших записей из каждого раздела одним запросом. Кто-нибудь может помочь как это сделать? Раздел - это одна из колонок в таблице.

База данных - это SQL Server 2005. Я хочу вернуть первые 10 по дате ввода. Разделы бывают деловые, местные и тематические. Для одной конкретной даты мне нужны только верхние (10) бизнес-строк (самая последняя запись), верхние (10) локальные строки и верхние (10) функции.

Сработал ли для вас какой-либо из этих ответов?

Kyle Delaney 01.12.2017 23:55

Думаю, мы никогда не узнаем ...

Denny 12.04.2019 16:05

Прошло 12 лет, и мы не знаем, сработало ли что-нибудь из этого.

aroma 24.03.2020 15:34
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
229
3
512 699
14

Ответы 14

Может ли оператор СОЮЗ работать на вас? Сделайте по одному SELECT для каждого раздела, затем СОЕДИНИТЕ их вместе. Думаю, это сработает только для фиксированного количества разделов.

Это работает на SQL Server 2005 (отредактировано, чтобы отразить ваше разъяснение):

select *
from Things t
where t.ThingID in (
    select top 10 ThingID
    from Things tt
    where tt.Section = t.Section and tt.ThingDate = @Date
    order by tt.DateEntered desc
    )
    and t.ThingDate = @Date
order by Section, DateEntered desc

Однако это не работает для строк, в которых Section имеет значение null. Вам нужно будет сказать "where (tt.Section имеет значение null, а t.Section равно null) или tt.Section = t.Section"

Matt Hamilton 07.10.2008 06:11

Если вы знаете, что это за разделы, вы можете:

select top 10 * from table where section=1
union
select top 10 * from table where section=2
union
select top 10 * from table where section=3

Это был бы самый простой способ сделать это.

Hector Sosa Jr 07.10.2008 06:15

Но это было бы неэффективно, если у вас их 150 или если категории меняются по дням, неделям и т. д.

Rafa Barragan 08.09.2017 18:26

Конечно, но процитирую OP: «Разделы бывают деловые, местные и тематические». Если у вас есть три статические категории, это лучший способ сделать это.

Blorgbeard 08.09.2017 18:31

Если вы используете SQL 2005, вы можете сделать что-то вроде этого ...

SELECT rs.Field1,rs.Field2 
    FROM (
        SELECT Field1,Field2, Rank() 
          over (Partition BY Section
                ORDER BY RankCriteria DESC ) AS Rank
        FROM table
        ) rs WHERE Rank <= 10

Если у вашего RankCriteria есть связи, вы можете вернуть более 10 строк, и решение Мэтта может быть лучше для вас.

Если вам действительно нужен только топ-10, измените его на RowNumber () вместо Rank (). Тогда никаких связей.

Mike L 07.10.2008 07:26

Это работает, но имейте в виду, что rank (), скорее всего, будет преобразован планировщиком запросов в полную сортировку таблицы, если нет индекса, ключ первый которого является RankCriteria. В этом случае вы можете увеличить пробег, выбирая отдельные разделы и перекрестно применяя их, чтобы выбрать 10 лучших, упорядоченных по RankCriteria desc.

Joe Kearney 13.02.2013 13:42

Отличный ответ! Получил практически то, что мне было нужно. Я остановился на DENSE_RANK, в котором нет пробелов в нумерации. +1

Michael Stramel 22.03.2013 03:33

"FROM table) rs WHERE Rank <= 10)" что это за "rs"

Facbed 04.05.2014 19:41

@Facbed Это просто псевдоним на столе.

Darrel Miller 04.05.2014 21:04

Для всех, кто использует Sql Server, функция RowNumber (), упомянутая Майком L, - это ROW_NUMBER ().

randomraccoon 07.07.2016 02:07

Я так делаю:

SELECT a.* FROM articles AS a
  LEFT JOIN articles AS a2 
    ON a.section = a2.section AND a.article_date <= a2.article_date
GROUP BY a.article_id
HAVING COUNT(*) <= 10;

Обновить: Этот пример GROUP BY работает только в MySQL и SQLite, потому что эти базы данных более разрешительны, чем стандартный SQL в отношении GROUP BY. Большинство реализаций SQL требуют, чтобы все столбцы в списке выбора, которые не являются частью агрегатного выражения, также находились в GROUP BY.

Это работает? Я почти уверен, что вы "a.somecolumn недопустимы в списке выбора, поскольку он не содержится в агрегатной функции или предложении group by" для каждого столбца в статьях, кроме article_id ..

Blorgbeard 07.10.2008 14:39

Вы должны иметь возможность включать другие столбцы, которые функционально зависят от столбца (столбцов), указанного в GROUP BY. Столбцы, которые не являются функционально зависимыми, неоднозначны. Но вы правы, в зависимости от реализации СУБД. Он работает в MySQL, но IIRC не работает в InterBase / Firebird.

Bill Karwin 08.10.2008 01:03

Сработает ли это в случае, если все одиннадцать лучших записей для раздела имеют одну и ту же дату? Все они будут иметь счет по 11, и результатом будет пустой набор.

Arth 05.11.2015 20:13

Нет, вам нужно каким-то образом разорвать отношения, если у всех одна и та же дата. См. Пример stackoverflow.com/questions/121387/….

Bill Karwin 05.11.2015 21:27

@BillKarwin, спасибо! Знаете ли вы, есть ли способ сделать это, если статья принадлежит более чем одному разделу? Например, ссылаясь на исходный вопрос, статья о местном предприятии может быть отнесена как к разделу «местный», так и «бизнес».

carlosgg 16.03.2017 23:20

@carlosgg, если статьи имеют отношение «многие ко многим» с разделами, тогда вам понадобится таблица пересечений, чтобы сопоставить статьи с их разделами. Затем ваш запрос должен быть присоединен к таблице пересечений для отношения m2m и сгруппирован по article_id и section. Это должно помочь вам начать работу, но я не собираюсь описывать все решение в комментариях.

Bill Karwin 17.03.2017 02:42

Я знаю, что эта ветка немного устарела, но я только что столкнулся с аналогичной проблемой (выберите новейшую статью из каждой категории), и это решение, которое я придумал:

WITH [TopCategoryArticles] AS (
    SELECT 
        [ArticleID],
        ROW_NUMBER() OVER (
            PARTITION BY [ArticleCategoryID]
            ORDER BY [ArticleDate] DESC
        ) AS [Order]
    FROM [dbo].[Articles]
)
SELECT [Articles].* 
FROM 
    [TopCategoryArticles] LEFT JOIN 
    [dbo].[Articles] ON
        [TopCategoryArticles].[ArticleID] = [Articles].[ArticleID]
WHERE [TopCategoryArticles].[Order] = 1

Это очень похоже на решение Даррела, но преодолевает проблему RANK, которая может возвращать больше строк, чем предполагалось.

Зачем использовать CTE, сэр? Это снижает потребление памяти?

toha 13.09.2016 13:22

@toha, потому что CTE проще и понятнее

Reversed Engineer 04.12.2019 21:23

Отличный ответ !! Его можно оптимизировать, используя внутренний JOIN вместо LEFT JOIN, поскольку никогда не будет записи для TopCategoryArticles без соответствующей записи Article.

Reversed Engineer 04.12.2019 21:36

Q) Поиск TOP X записей из каждой группы (Oracle)

SQL> select * from emp e 
  2  where e.empno in (select d.empno from emp d 
  3  where d.deptno=e.deptno and rownum<3)
  4  order by deptno
  5  ;

 EMPNO ENAME      JOB              MGR HIREDATE         SAL       COMM     DEPTNO

  7782 CLARK      MANAGER         7839 09-JUN-81       2450                    10
  7839 KING       PRESIDENT            17-NOV-81       5000                    10
  7369 SMITH      CLERK           7902 17-DEC-80        800                    20
  7566 JONES      MANAGER         7839 02-APR-81       2975                    20
  7499 ALLEN      SALESMAN        7698 20-FEB-81       1600        300         30
  7521 WARD       SALESMAN        7698 22-FEB-81       1250        500         30

Выбрано 6 рядов.


Вопрос касался SQL Server, а не Oracle.

Craig 18.07.2016 18:21

SELECT r.*
FROM
(
    SELECT
        r.*,
        ROW_NUMBER() OVER(PARTITION BY r.[SectionID]
                          ORDER BY r.[DateEntered] DESC) rn
    FROM [Records] r
) r
WHERE r.rn <= 10
ORDER BY r.[DateEntered] DESC

Что такое таблица с псевдонимом "м"?

Chalky 08.08.2016 06:32

@Chalky это опечатка, должно быть r. фиксированный.

lorond 08.08.2016 11:36

Работал как шарм. Спасибо!

Ron Nuni 25.05.2017 10:08

Что делать, если в DateEntered есть ничья?

Yiping 12.10.2020 04:07

@Yiping Не определено. Любая строка могла выиграть. Это зависит от многих обстоятельств и может быть разным. Вы можете сделать порядок более конкретным, добавив больше столбцов, например ORDER BY r.DateEntered, r.ID, но это все зависит от вашей конкретной задачи.

lorond 13.10.2020 21:46

Спасибо, я думаю, что использование RANK () в вашем ответе заставит его работать.

Yiping 14.10.2020 11:42

Вопрос @Yiping касался 10 первых строк из каждой категории, RANK () мог дать больше. Но если вам нужно 10 лучших результатов, RANK () определенно будет лучшим вариантом.

lorond 14.10.2020 15:45

rank () и density_rank () были плохой идеей, в моем случае использование rank или density_rank возвращало 15 строк, а не 10. но когда я использовал row_number (), он всегда возвращал только до 10 строк - правильно. у вас есть мой голос за этот лучший ответ для моих нужд, аналогичный OP

hamish 09.02.2021 15:07

В T-SQL я бы сделал:

WITH TOPTEN AS (
    SELECT *, ROW_NUMBER() 
    over (
        PARTITION BY [group_by_field] 
        order by [prioritise_field]
    ) AS RowNo 
    FROM [table_name]
)
SELECT * FROM TOPTEN WHERE RowNo <= 10

: Пожалуйста, опишите свое решение более подробно. См .: Как ответить

askmish 20.10.2012 05:20

Может ли запрос выбора в CTE содержать предложение where?

toha 13.09.2016 13:37

@toha Да, может

KindaTechy 11.05.2017 17:05

Хотя вы говорите «В T-SQL», это работает для любой базы данных, реализующей функцию ROW_NUMBER. Например, я использовал это решение в SQLite.

Tony 06.10.2019 12:57

Он также работает для postgres sql. Мне просто пришлось использовать "упорядочить по [Prioritise_field] desc"

Phun 11.05.2020 08:58

Если вы хотите создать вывод, сгруппированный по разделам, отображая только верхние записи п из каждого раздела, примерно так:

SECTION     SUBSECTION

deer        American Elk/Wapiti
deer        Chinese Water Deer
dog         Cocker Spaniel
dog         German Shephard
horse       Appaloosa
horse       Morgan

... тогда следующее должно работать в общем со всеми базами данных SQL. Если вы хотите получить первые 10, просто измените 2 на 10 ближе к концу запроса.

select
    x1.section
    , x1.subsection
from example x1
where
    (
    select count(*)
    from example x2
    where x2.section = x1.section
    and x2.subsection <= x1.subsection
    ) <= 2
order by section, subsection;

Установить:

create table example ( id int, section varchar(25), subsection varchar(25) );

insert into example select 0, 'dog', 'Labrador Retriever';
insert into example select 1, 'deer', 'Whitetail';
insert into example select 2, 'horse', 'Morgan';
insert into example select 3, 'horse', 'Tarpan';
insert into example select 4, 'deer', 'Row';
insert into example select 5, 'horse', 'Appaloosa';
insert into example select 6, 'dog', 'German Shephard';
insert into example select 7, 'horse', 'Thoroughbred';
insert into example select 8, 'dog', 'Mutt';
insert into example select 9, 'horse', 'Welara Pony';
insert into example select 10, 'dog', 'Cocker Spaniel';
insert into example select 11, 'deer', 'American Elk/Wapiti';
insert into example select 12, 'horse', 'Shetland Pony';
insert into example select 13, 'deer', 'Chinese Water Deer';
insert into example select 14, 'deer', 'Fallow';

Это не работает, когда мне нужна только первая запись для каждого раздела. Он удаляет все группы разделов, которые имеют более одной записи. Я попытался заменить <= 2 на <= 1

nils 18.07.2016 17:52

@nils Значений разделов всего три: олень, собака и лошадь. Если вы измените запрос на <= 1, вы получите по одному подразделу для каждого раздела: американский лось / вапити для оленей, кокер-спаниель для собак и аппалуза для лошадей. Это также первые значения в каждом разделе по алфавиту. Запрос - имел в виду, чтобы исключить все остальные значения.

Craig 18.07.2016 18:17

Но когда я пытаюсь выполнить ваш запрос, он удаляет все, потому что счетчик> = 1 для всего. Он не сохраняет 1-й подраздел для каждого раздела. Можете ли вы попробовать выполнить свой запрос для <= 1 и сообщить мне, если вы получите первый подраздел для каждого раздела?

nils 19.07.2016 19:28

@nils Привет, я воссоздал эту небольшую тестовую базу данных из сценариев и выполнил запрос, используя <= 1, и он вернул значение первого подраздела из каждого раздела. Какой сервер базы данных вы используете? Всегда есть шанс, что это связано с выбранной вами базой данных. Я просто запустил это в MySQL, потому что он был удобен и вел себя так, как ожидалось. Я почти уверен, что когда я сделал это в первый раз (я хотел убедиться, что то, что я опубликовал, действительно работает без отладки), я почти уверен, что делал это с помощью Sybase SQL Anywhere или MS SQL Server.

Craig 21.07.2016 01:38

у меня он отлично работал в mysql. Я немного изменил запрос, не зная, почему он использовал <= для поля varchar в подразделе .. Я изменил его на и x2.subsection = x1.subsection

Mahen Nakar 30.01.2020 07:50

Если мы используем SQL Server> = 2005, то мы можем решить задачу только с одним Выбрать:

declare @t table (
    Id      int ,
    Section int,
    Moment  date
);

insert into @t values
(   1   ,   1   , '2014-01-01'),
(   2   ,   1   , '2014-01-02'),
(   3   ,   1   , '2014-01-03'),
(   4   ,   1   , '2014-01-04'),
(   5   ,   1   , '2014-01-05'),

(   6   ,   2   , '2014-02-06'),
(   7   ,   2   , '2014-02-07'),
(   8   ,   2   , '2014-02-08'),
(   9   ,   2   , '2014-02-09'),
(   10  ,   2   , '2014-02-10'),

(   11  ,   3   , '2014-03-11'),
(   12  ,   3   , '2014-03-12'),
(   13  ,   3   , '2014-03-13'),
(   14  ,   3   , '2014-03-14'),
(   15  ,   3   , '2014-03-15');


-- TWO earliest records in each Section

select top 1 with ties
    Id, Section, Moment 
from
    @t
order by 
    case 
        when row_number() over(partition by Section order by Moment) <= 2 
        then 0 
        else 1 
    end;


-- THREE earliest records in each Section

select top 1 with ties
    Id, Section, Moment 
from
    @t
order by 
    case 
        when row_number() over(partition by Section order by Moment) <= 3 
        then 0 
        else 1 
    end;


-- three LATEST records in each Section

select top 1 with ties
    Id, Section, Moment 
from
    @t
order by 
    case 
        when row_number() over(partition by Section order by Moment desc) <= 3 
        then 0 
        else 1 
    end;

+1 Мне нравится это решение за его простоту, но не могли бы вы объяснить, как использование top 1 работает с оператором case в предложении order by, возвращающим 0 или 1?

Ceres 19.06.2015 17:40

Здесь TOP 1 работает с WITH TIES. WITH TIES означает, что когда ORDER BY = 0, тогда SELECT берет эту запись (из-за TOP 1) и все остальные, которые имеют ORDER BY = 0 (из-за WITH TIES)

Vadim Loboda 19.06.2015 18:24

Вы можете попробовать этот подход. Этот запрос возвращает 10 самых густонаселенных городов для каждой страны.

   SELECT city, country, population
   FROM
   (SELECT city, country, population, 
   @country_rank := IF(@current_country = country, @country_rank + 1, 1) AS country_rank,
   @current_country := country 
   FROM cities
   ORDER BY country, population DESC
   ) ranked
   WHERE country_rank <= 10;

Это решение не проходит тестовый пример, когда у нас есть таблица с записью одной страны с 9 одинаковым населением, например, оно возвращает null вместо возврата всех 9 доступных записей по порядку. Есть предложения по исправлению этой проблемы?

Mojgan Mazouchi 24.07.2018 02:31

Пробовал следующее, и это тоже сработало со связями.

SELECT rs.Field1,rs.Field2 
FROM (
    SELECT Field1,Field2, ROW_NUMBER() 
      OVER (Partition BY Section
            ORDER BY RankCriteria DESC ) AS Rank
    FROM table
    ) rs WHERE Rank <= 10

Здравствуйте, у меня запрос работает нормально, пока я не добавлю в представление агрегированную меру. SELECT rs.Field1, rs.Field2, rs.measure FROM (SELECT Field1, Field2, sum (приведение (измерение как INT)) по (разделение по разделу) как agg_measure, ROW_NUMBER () OVER (Разделение по разделам ORDER BY agg_measure DESC) AS Rank FROM table) rs WHERE Rank <= 10 Не могли бы вы помочь мне, где я ошибаюсь.

Harish Mahamure 22.10.2020 18:43

Хотя вопрос касался SQL Server 2005, большинство людей пошли дальше, и если они все же найдут этот вопрос, то какой ответ в других ситуациях может быть предпочтительным - один с использованием CROSS APPLY, как показано в этом сообщении в блоге.

SELECT *
FROM t
CROSS APPLY (
  SELECT TOP 10 u.*
  FROM u
  WHERE u.t_id = t.t_id
  ORDER BY u.something DESC
) u

Этот запрос включает 2 таблицы. Запрос OP включает только 1 таблицу, в случае которой решение на основе оконных функций может быть более эффективным.

Другие вопросы по теме