如何 select 特定月份的最频繁值并显示该值及其出现次数?

How do I select the most frequent value for a specific month and display this value as well as the amount of times it occurs?

我正在为一个 TSQL 查询而苦苦挣扎,而且我已经没有谷歌搜索了,所以我自然而然地想我不妨问问 SO。

请记住,几周前我才开始尝试学习 SQL,我不太确定有哪些规则以及您可以和不可以编写查询/子查询的方式.

这是我目前拥有的:

编辑:使用 DDL 进行了更新,应该有助于创建示例,还注释掉了不必要的 "Client"-列。

CREATE TABLE NumberTable
(
Number varchar(20),
Date date
);

INSERT INTO NumberTable (Number, Date)
VALUES
('55512345', '2015-01-01'),
('55512345', '2015-01-01'),
('55512345', '2015-01-01'),
('55545678', '2015-01-01'),
('55512345', '2015-02-01'),
('55523456', '2015-02-01'),
('55523456', '2015-02-01'),
('55534567', '2015-03-01'),
('55534567', '2015-03-01'),
('55534567', '2015-03-01'),
('55534567', '2015-03-01'),
('55545678', '2015-03-01'),
('55545678', '2015-04-01')

DECLARE
    --@ClientNr AS int,
    @FromDate AS date,
    @ToDate AS date

--SET @ClientNr = 11111
SET @FromDate = '2015-01-01'
SET @ToDate = DATEADD(yy, 1, @FromDate)

SELECT
    YEAR(Date) AS [Year],
    MONTH(Date) AS [Month],
    COUNT(Number) AS [Total Count]
FROM
    NumberTable
WHERE
    --Client = @ClientNr
    Date BETWEEN @FromDate AND @ToDate
    AND Number IS NOT NULL
    AND Number NOT IN ('888', '144')
GROUP BY MONTH(Date), YEAR(Date)
ORDER BY [Year], [Month]

有了这个,我得到了年、月和总计数。

我很高兴每个月只获得前 1 个最常被呼叫的号码和计数,但最好显示前 5 个。

这是我希望 table 最后看起来的示例(将月份格式设置为 JAN、FEB 等而不是数字并不是很重要,但会是一个很好的奖励):

╔══════╦═══════╦═════════════╦═══════════╦══════════╦═══════════╦══════════╗
║ Year ║ Month ║ Total Count ║ #1 Called ║ #1 Count ║ #2 Called ║ #2 Count ║
╠══════╬═══════╬═════════════╬═══════════╬══════════╬═══════════╬══════════╣
║ 2016 ║ JAN   ║       80431 ║ 555-12345 ║    45442 ║ 555-94564 ║    17866 ║
╚══════╩═══════╩═════════════╩═══════════╩══════════╩═══════════╩══════════╝

有人告诉我这是 "easily" 通过子查询完成的,但我不太确定...

此查询对您有帮助:

IF OBJECT_ID('tempdb..#Test','U') IS NOT NULL DROP TABLE #Test;

CREATE TABLE #Test(Number INT NOT NULL)

INSERT INTO #Test(Number)
VALUES(1),(2),(3),(1)

SELECT TOP 1 WITH TIES
    Number
FROM (
SELECT DISTINCT
    Number
    , COUNT(*) OVER(PARTITION BY Number) AS cnt
FROM #Test) AS T
ORDER BY cnt DESC

当多个值存在最大计数时,我使用了 TOP 1 WITH TIES。

这个很有趣,我相信你可以用 CTE 和 PIVOT 来做到这一点,但这超出了我的想象...这可能无法一字不差地工作

WITH Rollup_CTE
AS
(
    SELECT Client,MONTH(Date) as Month, YEAR(Date) as Year, Number, Count(0) as Calls, ROW_NUMBER() OVER (PARTITION BY Client,MONTH(Date) as SqNo, YEAR(Date), Number ORDER BY COUNT(0) DESC)
    from NumberTable 
        WHERE Number IS NOT NULL AND Number NOT IN ('888', '144')
    GROUP BY Client,MONTH(Date), YEAR(Date), Number
)
SELECT * FROM Rollup_CTE Where SqNo <=5

然后您可以根据需要使用 PIVOT

旋转数据

试试这个,不一定是 CTE,但我用它来填充数据,你可以扩展它以包括第 3、4 等。

;with data AS
(select '2016-01-01' as called, '111' as number
union all select '2016-01-01', '111'
union all select '2016-01-01', '111'
union all select '2016-01-01', '222'
union all select '2016-01-01', '222')
, ordered AS (
select called
, number
, count(*) cnt
, ROW_NUMBER() OVER (ORDER BY COUNT(*) DESC) rnk
from data
group by called, number)
SELECT distinct *
FROM (SELECT DATENAME(month, called) mnth FROM ordered) AS mnth,
(SELECT number MostCalledNumber FROM ordered WHERE rnk = 1) AS MostCalledNumber,
(SELECT cnt MostCalledTimes FROM ordered WHERE rnk = 1) AS MostCalledTimes,
(SELECT number SecondMostCalledNumber FROM ordered WHERE rnk = 2) AS SecondMostCalledNumber,
(SELECT cnt SecondMostCalledTimes FROM ordered WHERE rnk = 2) AS SecondMostCalledTimes

artm 的查询已更正(分区)并简化了最后一步(旋转)。

with data AS
(select '2016-01-01' as called, '111' as number
union all select '2016-01-01', '111'
union all select '2016-01-01', '111'
union all select '2016-01-01', '222'
union all select '2016-01-01', '222'
union all select '2016-01-05', '111'
union all select '2016-01-05', '222'
union all select '2016-01-05', '222')
, ordered AS (
select called
, number
, count(*) cnt
, ROW_NUMBER() OVER (PARTITION BY called ORDER BY COUNT(*) DESC) rnk
from data
group by called, number)
select called, total = sum(cnt)
, n1= max(case rnk when 1 then number end)
, cnt1=max(case rnk when 1 then cnt end)
, n2= max(case rnk when 2 then number end)
, cnt2=max(case rnk when 2 then cnt end)
 from ordered 
 group by called

编辑 使用 OP

提供的设置
WITH ordered AS(
   -- compute order
   SELECT 
    [Year] = YEAR(Date) 
    , [Month] = MONTH(Date)  
    , number
    , COUNT(*) cnt
    , ROW_NUMBER() OVER (PARTITION BY YEAR(Date), MONTH(Date) ORDER BY COUNT(*) DESC) rnk
    FROM NumberTable
    WHERE Date BETWEEN @FromDate AND @ToDate
        AND Number IS NOT NULL
        AND Number NOT IN ('888', '144')
    GROUP BY YEAR(Date), MONTH(Date), number
)
-- pivot by order
SELECT [Year], [Month] 
    , total = sum(cnt)
    , n1 = MAX(case rnk when 1 then number end)
    , cnt1 = MAX(case rnk when 1 then cnt end)
    , n2 = MAX(case rnk when 2 then number end)
    , cnt2 = MAX(case rnk when 2 then cnt end)
    -- n3, cnt3, .... 
FROM ordered 
GROUP BY [Year], [Month];