SQL - Выберите рекордный максимум для каждого дня недели - PullRequest
0 голосов
/ 29 апреля 2011

Возвращает все строки, отсортированные по CountOfRecords DESC. Мне бы хотелось, чтобы здесь было всего семь строк, показывающих самое высокое значение за указанный день. Поэтому каждый день недели должен происходить один раз и только один раз. Это возможно без большой боли?

SELECT 
    Count(*) As CountOfRecords, 
    CAST(FLOOR(CAST([visit].[datetimeentered] AS float)) AS smalldatetime) AS DateEntered, 
    DatePart(dw, visit.datetimeentered) As DayOfTheWeek
FROM visit
INNER JOIN useragent ON useragent.useragentid = visit.useragentid
WHERE useragent.isbot = 0
GROUP BY CAST(FLOOR(CAST([visit].[datetimeentered] AS float)) AS smalldatetime), 
    DatePart(dw, visit.datetimeentered)
ORDER BY CountOfRecords DESC

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

Msg 207, Level 16, State 1, Line 29
Invalid column name 'RowNum'.
Msg 207, Level 16, State 1, Line 25
Invalid column name 'CountOfRecords'.
Msg 207, Level 16, State 1, Line 26
Invalid column name 'DateEntered'.
Msg 207, Level 16, State 1, Line 27
Invalid column name 'DayOfTheWeek'.

Отдельное спасибо marc_s за указание на простой способ получить единственную часть даты из даты и времени.

Ответы [ 2 ]

1 голос
/ 29 апреля 2011

По вашему запросу:

WITH groupedByDay AS (
  SELECT 
    Count(*) As CountOfRecords, 
    CAST(FLOOR(CAST([visit].[datetimeentered] AS float)) AS smalldatetime) AS DateEntered
  FROM visit
  INNER JOIN useragent ON useragent.useragentid = visit.useragentid
  WHERE useragent.isbot = 0
  GROUP BY CAST(FLOOR(CAST([visit].[datetimeentered] AS float)) AS smalldatetime)
),
ranked AS (
  SELECT
    CountOfRecords,
    DateEntered,
    DOW = DATENAME(dw, DateEntered),
    rank = ROW_NUMBER() OVER (PARTITION BY DatePart(dw, DateEntered)
                              ORDER BY CountOfRecords DESC)
  FROM groupedByDay
)
SELECT
  CountOfRecords,
  DateEntered,
  DOW
FROM ranked
WHERE rank = 1
ORDER BY CountOfRecords DESC

Это вернет одну строку для каждого дня недели, представленного в таблице. Если среди максимальных значений может быть дубликат на CountOfRecords и вы хотите вернуть их все, используйте RANK() вместо ROW_NUMBER().

1 голос
/ 29 апреля 2011

В SQL Server 2005 и новее вы можете использовать CTE (Common Table Expression) с функцией ранжирования ROW_NUMBER() и предложением PARTITION BY - что-то вроде этого:

;WITH DataByDayOfWeek AS
(
   SELECT 
      Count(*) As CountOfRecords,
      CAST(FLOOR(CAST(v.[datetimeentered] AS float)) AS smalldatetime) AS DateEntered, 
      DatePart(dw, v.datetimeentered) As DayOfTheWeek
   FROM dbo.visit v
   INNER JOIN dbo.useragent u ON u.useragentid = v.useragentid
   WHERE u.isbot = 0
   GROUP BY 
       CAST(FLOOR(CAST(v.[datetimeentered] AS float)) AS smalldatetime), 
       DatePart(dw, v.datetimeentered)
),
HighestValues AS
(
    SELECT 
       CountOfRecords,
       DateEntered,
       DayOfTheWeek,
       ROW_NUMBER() OVER(PARTITION BY DayOfTheWeek 
                         ORDER BY CountOfRecords DESC) 'RowNum'
    FROM DataByDayOfWeek
)
SELECT 
   CountOfRecords,
   DateEntered,
   DayOfTheWeek
WHERE
   RowNum = 1

Позвольте мне объяснить:

  • первый CTE выполняет подсчет записей, в основном - поэтому вы получите одну запись для каждого DateEntered с количеством записей и днем ​​недели
  • второй CTE основан на этом первом CTE и «разделяет» ваши данные на DayOfTheWeek - поэтому для каждого отдельного дня недели вы получаете счетчик, начинающийся с 1. Данные для каждого дня недели сортируются по записисчитать в порядке убывания, поэтому самое высокое значение имеет RowNum, равное 1

. Таким образом, выбор всех строк из второго CTE, имеющих RowNum = 1, дает вам наибольшее значение для каждого днянеделю.

В качестве примечания: я верю, что в SQL Server 2008 вы могли бы намного проще преобразовать дату и время в строгое значение DATE, которое имеет только DATE - без времени - как это:

CAST(v.DateTimeEntered AS DATE)

Больше никаких хлопот спреобразовать в число с плавающей точкой, а затем в smalldatetime - попробуйте!

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