Запрос, чтобы найти первое и второе наибольшее значение из группы - PullRequest
6 голосов
/ 14 декабря 2010

У меня такой запрос:

SELECT
 DATEPART(year,some_date),
 DATEPART(month,some_date),
 MAX(some_value) max_value
FROM
 some_table
GROUP BY
    DATEPART(year,some_date),
    DATEPART(month,some_date)

Возвращает таблицу с: год, месяц, наибольшее значение за месяц .

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

Мне кажется, что хорошо известные решения, такие как "ТОП 2", "НЕ В ТОП 1" или подпункте, здесь не сработают.

(Если быть точным - я использую SQL Server 2008.)

Любая помощь приветствуется, спасибо.

Ответы [ 6 ]

3 голосов
/ 14 декабря 2010

Мне кажется, что вопрос требует запроса, который вернул бы лучший и второй лучший в той же строке для каждого месяца и года, например:

month, year, best, second best
...
...

, а не две строки для одного и того же месяца и года, содержащие наилучшее и второе наилучшее значение.

Это решение, которое я придумал, поэтому, если у кого-то есть более простой способ достижения этого, я бы хотел знать.

with ranks as (
    select 
        year(entrydate) as [year], 
        month(entrydate) as [month], 
        views, 
        rank() over (partition by year(entrydate), month(entrydate) order by views desc) as [rank]
    from product
)
select 
    t1.year, 
    t1.month, 
    t1.views as [best], 
    t2.views as [second best]
from ranks t1
    inner join ranks t2
        on t1.year = t2.year
        and t1.month = t2.month
        and t1.rank = 1
        and t2.rank = 2

РЕДАКТИРОВАТЬ: просто из любопытства я провел немного больше испытаний и в итоге получил более простой вариант ответа Стефани Пейдж *1000*, в котором не используется дополнительный подзапрос. И я изменил функцию rank () на row_number (), так как она не работает, когда два максимальных значения одинаковы.

with ranks as (
    select 
        year(entrydate) as [year], 
        month(entrydate) as [month], 
        views, 
        row_number() over (partition by year(entrydate), month(entrydate) order by views desc) as [rank]
    from product
)
select 
    t1.year, 
    t1.month, 
    max(case when t1.rank = 1 then t1.views else 0 end) as [best], 
    max(case when t1.rank = 2 then t1.views else 0 end) as [second best]
from 
    ranks t1
where
    t1.rank in (1,2)
group by
    t1.year, t1.month
3 голосов
/ 14 декабря 2010

RANK() это, может быть, то, что вы ищете ...

http://msdn.microsoft.com/en-us/library/ms176102.aspx

2 голосов
/ 14 декабря 2010

чтобы сделать это без объединений (я покажу Oracle ... вы просто будете использовать CASE вместо DECODES)

with ranks as (
        select 
            year(entrydate) as [year], 
            month(entrydate) as [month], 
            views, 
            rank() over (partition by year(entrydate), month(entrydate) order by views desc) as [rank]
        from product
    )
SELECT [year], [month], Max([best]), Max([second best])
 FROM
    ( select 
        t1.year, 
        t1.month, 
        Decode([rank],1,t1.views,0) as [best], 
        Decode([rank],2,t1.views,0)  as [second best]
    from ranks t1
    where t1.rank <= 2 ) x
GROUP BY [year], [month]
1 голос
/ 14 декабря 2010

Вы можете использовать CTE с функциями ранжирования в SQL Server 2005 и выше:

;WITH TopValues AS
(
   SELECT
     YEAR(some_date) AS 'Year',
     MONTH(some_date) AS 'Month',
     Some_Value,
     ROW_NUMBER() OVER(PARTITION BY YEAR(some_date),MONTH(some_date) 
                       ORDER BY Some_Value DESC) AS 'RowNumber'
  FROM
     dbo.some_table
)
SELECT
    Year, Month, Some_Value
FROM
    TopValues
WHERE
    RowNumber <= 2

Это «разделит» (то есть сгруппирует) ваши данные по месяцам / годам, упорядочит внутри каждой группы по убыванию Some_Value (сначала по величине), а затем вы сможете выбрать первые две из каждой группы из этого CTE.

RANK() также работает (я чаще всего использую ROW_NUMBER) - он дает несколько иные результаты - в действительности зависит от ваших потребностей.

1 голос
/ 14 декабря 2010

Это немного старая школа, но TOP и подзапрос будут работать, если вы используете ORDER BY. Попробуйте это:

SELECT TOP 2
 DATEPART(year,some_date),
 DATEPART(month,some_date),
 (SELECT MAX(st1.some_value) FROM some_table AS st1 
  WHERE DATEPART(month,some_date) = DATEPART(month,st1.some_date)) AS max_value
FROM
 some_table
GROUP BY
    DATEPART(year,some_date),
    DATEPART(month,some_date)
ORDER BY DATEPART(month,some_date) DESC

Это даст вам две строки с «самыми высокими» значениями месяца, а добавленный подвыбор должен дать вам максимум от каждой группировки.

0 голосов
/ 14 декабря 2010

Хммм, это своего рода установка, но вы можете сделать это с подзапросами ... вместо того, чтобы использовать этот максимум, я бы выбрал some_values, у которых совпадают год и месяц, row_number () = 1 / row_number () = 2соответственно и порядок по some_value DESC.

Невозможность использовать OFFSET / LIMIT, как вы можете в SQLite, - одна из моих неприятностей в SQL Server.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...