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


Может ли оператор СОЮЗ работать на вас? Сделайте по одному 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"
Если вы знаете, что это за разделы, вы можете:
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
Это был бы самый простой способ сделать это.
Но это было бы неэффективно, если у вас их 150 или если категории меняются по дням, неделям и т. д.
Конечно, но процитирую OP: «Разделы бывают деловые, местные и тематические». Если у вас есть три статические категории, это лучший способ сделать это.
Если вы используете 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 (). Тогда никаких связей.
Это работает, но имейте в виду, что rank (), скорее всего, будет преобразован планировщиком запросов в полную сортировку таблицы, если нет индекса, ключ первый которого является RankCriteria. В этом случае вы можете увеличить пробег, выбирая отдельные разделы и перекрестно применяя их, чтобы выбрать 10 лучших, упорядоченных по RankCriteria desc.
Отличный ответ! Получил практически то, что мне было нужно. Я остановился на DENSE_RANK, в котором нет пробелов в нумерации. +1
"FROM table) rs WHERE Rank <= 10)" что это за "rs"
@Facbed Это просто псевдоним на столе.
Для всех, кто использует Sql Server, функция RowNumber (), упомянутая Майком L, - это ROW_NUMBER ().
Я так делаю:
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 ..
Вы должны иметь возможность включать другие столбцы, которые функционально зависят от столбца (столбцов), указанного в GROUP BY. Столбцы, которые не являются функционально зависимыми, неоднозначны. Но вы правы, в зависимости от реализации СУБД. Он работает в MySQL, но IIRC не работает в InterBase / Firebird.
Сработает ли это в случае, если все одиннадцать лучших записей для раздела имеют одну и ту же дату? Все они будут иметь счет по 11, и результатом будет пустой набор.
Нет, вам нужно каким-то образом разорвать отношения, если у всех одна и та же дата. См. Пример stackoverflow.com/questions/121387/….
@BillKarwin, спасибо! Знаете ли вы, есть ли способ сделать это, если статья принадлежит более чем одному разделу? Например, ссылаясь на исходный вопрос, статья о местном предприятии может быть отнесена как к разделу «местный», так и «бизнес».
@carlosgg, если статьи имеют отношение «многие ко многим» с разделами, тогда вам понадобится таблица пересечений, чтобы сопоставить статьи с их разделами. Затем ваш запрос должен быть присоединен к таблице пересечений для отношения m2m и сгруппирован по article_id и section. Это должно помочь вам начать работу, но я не собираюсь описывать все решение в комментариях.
Я знаю, что эта ветка немного устарела, но я только что столкнулся с аналогичной проблемой (выберите новейшую статью из каждой категории), и это решение, которое я придумал:
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, потому что CTE проще и понятнее
Отличный ответ !! Его можно оптимизировать, используя внутренний JOIN вместо LEFT JOIN, поскольку никогда не будет записи для TopCategoryArticles без соответствующей записи Article.
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.
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 это опечатка, должно быть r. фиксированный.
Работал как шарм. Спасибо!
Что делать, если в DateEntered есть ничья?
@Yiping Не определено. Любая строка могла выиграть. Это зависит от многих обстоятельств и может быть разным. Вы можете сделать порядок более конкретным, добавив больше столбцов, например ORDER BY r.DateEntered, r.ID, но это все зависит от вашей конкретной задачи.
Спасибо, я думаю, что использование RANK () в вашем ответе заставит его работать.
Вопрос @Yiping касался 10 первых строк из каждой категории, RANK () мог дать больше. Но если вам нужно 10 лучших результатов, RANK () определенно будет лучшим вариантом.
rank () и density_rank () были плохой идеей, в моем случае использование rank или density_rank возвращало 15 строк, а не 10. но когда я использовал row_number (), он всегда возвращал только до 10 строк - правильно. у вас есть мой голос за этот лучший ответ для моих нужд, аналогичный OP
В 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
: Пожалуйста, опишите свое решение более подробно. См .: Как ответить
Может ли запрос выбора в CTE содержать предложение where?
@toha Да, может
Хотя вы говорите «В T-SQL», это работает для любой базы данных, реализующей функцию ROW_NUMBER. Например, я использовал это решение в SQLite.
Он также работает для postgres sql. Мне просто пришлось использовать "упорядочить по [Prioritise_field] desc"
Если вы хотите создать вывод, сгруппированный по разделам, отображая только верхние записи п из каждого раздела, примерно так:
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 Значений разделов всего три: олень, собака и лошадь. Если вы измените запрос на <= 1, вы получите по одному подразделу для каждого раздела: американский лось / вапити для оленей, кокер-спаниель для собак и аппалуза для лошадей. Это также первые значения в каждом разделе по алфавиту. Запрос - имел в виду, чтобы исключить все остальные значения.
Но когда я пытаюсь выполнить ваш запрос, он удаляет все, потому что счетчик> = 1 для всего. Он не сохраняет 1-й подраздел для каждого раздела. Можете ли вы попробовать выполнить свой запрос для <= 1 и сообщить мне, если вы получите первый подраздел для каждого раздела?
@nils Привет, я воссоздал эту небольшую тестовую базу данных из сценариев и выполнил запрос, используя <= 1, и он вернул значение первого подраздела из каждого раздела. Какой сервер базы данных вы используете? Всегда есть шанс, что это связано с выбранной вами базой данных. Я просто запустил это в MySQL, потому что он был удобен и вел себя так, как ожидалось. Я почти уверен, что когда я сделал это в первый раз (я хотел убедиться, что то, что я опубликовал, действительно работает без отладки), я почти уверен, что делал это с помощью Sybase SQL Anywhere или MS SQL Server.
у меня он отлично работал в mysql. Я немного изменил запрос, не зная, почему он использовал <= для поля varchar в подразделе .. Я изменил его на и x2.subsection = x1.subsection
Если мы используем 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?
Здесь TOP 1 работает с WITH TIES. WITH TIES означает, что когда ORDER BY = 0, тогда SELECT берет эту запись (из-за TOP 1) и все остальные, которые имеют ORDER BY = 0 (из-за WITH TIES)
Вы можете попробовать этот подход. Этот запрос возвращает 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 доступных записей по порядку. Есть предложения по исправлению этой проблемы?
Пробовал следующее, и это тоже сработало со связями.
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 Не могли бы вы помочь мне, где я ошибаюсь.
Хотя вопрос касался 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 таблицу, в случае которой решение на основе оконных функций может быть более эффективным.
Сработал ли для вас какой-либо из этих ответов?