列在选择列表中无效,因为它不包含在聚合函数或Group By子句中

时间:2014-09-22 21:20:37

标签: sql sql-server group-by

我有以下表格:

Create Table Country(CountryID int primary key, CountryName nvarchar(100) not null)
Create Table DeviceType(DeviceTypeID int primary key, DeviceTypeName nvarchar(100) not null)
Create Table UserStat
(
LocalID int primary key identity(1,1),
TimePeriod datetime not null,
CountryID int not null,
DeviceTypeID int not null,
UserCount int not null,
CONSTRAINT [FK_UserStat_Country] FOREIGN KEY (CountryID) REFERENCES [dbo].[Country] (CountryID),
CONSTRAINT [FK_UserStat_DeviceType] FOREIGN KEY (DeviceTypeID) REFERENCES [dbo].[DeviceType] (DeviceTypeID))

Insert into Country values (1, 'India')
Insert into Country values (2, 'USA')

Insert Into DeviceType values (1, 'Mobile')
Insert Into DeviceType values (2, 'Desktop')

Insert into UserStat values (CAST(DATEFROMPARTS(2014,9,1) AS datetime),1,1,9999)
Insert into UserStat values (CAST(DATEFROMPARTS(2014,9,1) AS datetime),1,2,10000)
Insert into UserStat values (CAST(DATEFROMPARTS(2014,9,1) AS datetime),2,1,20000)
Insert into UserStat values (CAST(DATEFROMPARTS(2014,9,1) AS datetime),2,2,19999)
Insert into UserStat values (CAST(DATEFROMPARTS(2014,8,1) AS datetime),1,1,50000)
Insert into UserStat values (CAST(DATEFROMPARTS(2014,8,1) AS datetime),1,2,60000)
Insert into UserStat values (CAST(DATEFROMPARTS(2014,8,1) AS datetime),2,1,70000)
Insert into UserStat values (CAST(DATEFROMPARTS(2014,8,1) AS datetime),2,2,80000)

现在,我需要根据特定月份和年份的位置和设备类型获得总用户数。我在下面尝试查询:

Declare @region nvarchar(50)='Both'
Declare @calendarYear int = 2014

SELECT  YEAR(U.TimePeriod) AS [Year],
        MONTH(U.TimePeriod) AS [Month],
        CASE 
            WHEN @region = 'India' THEN (SELECT SUM(U.UserCount) WHERE C.CountryName = 'India' AND D.DeviceTypeName = 'Mobile' GROUP BY C.CountryName, D.DeviceTypeName)
            WHEN @region = 'USA' THEN (SELECT SUM(U.UserCount) WHERE C.CountryName = 'USA' AND D.DeviceTypeName = 'Mobile' GROUP BY C.CountryName, D.DeviceTypeName)
            WHEN @region = 'Both' THEN (SELECT SUM(U.UserCount) WHERE C.CountryName IN ('India', 'USA') AND D.DeviceTypeName = 'Mobile' GROUP BY C.CountryName, D.DeviceTypeName)
            ELSE 0
        END AS [MobileUsers],
        CASE 
        WHEN @region = 'India' THEN (SELECT SUM(U.UserCount) WHERE C.CountryName = 'India' AND D.DeviceTypeName = 'Desktop' GROUP BY C.CountryName, D.DeviceTypeName)
            WHEN @region = 'USA' THEN (SELECT SUM(U.UserCount) WHERE C.CountryName = 'USA' AND D.DeviceTypeName = 'Desktop' GROUP BY C.CountryName, D.DeviceTypeName)
            WHEN @region = 'Both' THEN (SELECT SUM(U.UserCount) WHERE C.CountryName IN ('India', 'USA') AND D.DeviceTypeName = 'Desktop' GROUP BY C.CountryName, D.DeviceTypeName)
            ELSE 0
        END AS [DesktopUsers]
FROM dbo.UserStat AS U WITH (NOLOCK)
Join dbo.Country AS C WITH (NOLOCK) ON C.CountryID=U.CountryID 
Join dbo.DeviceType AS D WITH (NOLOCK) ON D.DeviceTypeID=U.DeviceTypeID 
WHERE YEAR(U.TimePeriod) = @calendarYear
GROUP BY YEAR(U.TimePeriod), MONTH(U.TimePeriod);

当我尝试运行此操作时,我遇到了以下错误:

Column 'dbo.Country.CountryName' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
Column 'dbo.DeviceType.DeviceTypeName' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.

如果我在上面的CASE语句中没有使用Group By子句,那么我也会得到同样的错误,你能告诉我为什么会出现这个错误,我该怎么办?任何帮助表示赞赏。

4 个答案:

答案 0 :(得分:1)

我认为这会让你想要你想要的东西:

DECLARE @region varchar(10);
DECLARE @calendarYear int;
SET @calendarYear = 2014
SET @region = 'USA'

;WITH Data AS (
SELECT U.*, C.CountryName, D.DeviceTypeName
FROM dbo.UserStat AS U WITH (NOLOCK)
INNER JOIN dbo.Country AS C WITH (NOLOCK) ON C.CountryID=U.CountryID 
INNER JOIN dbo.DeviceType AS D WITH (NOLOCK) ON D.DeviceTypeID=U.DeviceTypeID 
) 
SELECT YEAR(d1.TimePeriod) AS [Year],
       MONTH(d1.TimePeriod) AS [Month],
       CASE 
           WHEN @region = 'India' THEN (SELECT SUM(d2.UserCount) FROM Data d2 
                                         WHERE YEAR(d2.TimePeriod) = YEAR(d1.TimePeriod) AND MONTH(d2.TimePeriod) = MONTH(d1.TimePeriod) 
                                           AND d2.CountryName = 'India' AND d2.DeviceTypeName = 'Mobile' GROUP BY YEAR(d2.TimePeriod))
           WHEN @region = 'USA' THEN (SELECT SUM(d2.UserCount) FROM Data d2 
                                       WHERE YEAR(d2.TimePeriod) = YEAR(d1.TimePeriod) AND MONTH(d2.TimePeriod) = MONTH(d1.TimePeriod) 
                                         AND d2.CountryName = 'USA' AND d2.DeviceTypeName = 'Mobile' GROUP BY YEAR(d2.TimePeriod))
           WHEN @region = 'Both' THEN (SELECT SUM(d2.UserCount) FROM Data d2 
                                        WHERE YEAR(d2.TimePeriod) = YEAR(d1.TimePeriod) AND MONTH(d2.TimePeriod) = MONTH(d1.TimePeriod) 
                                          AND d2.CountryName IN ('India', 'USA') AND d2.DeviceTypeName = 'Mobile' GROUP BY YEAR(d2.TimePeriod))
           ELSE 0
       END AS [MobileUsers],
       CASE 
       WHEN @region = 'India' THEN (SELECT SUM(d2.UserCount) FROM Data d2 
                                     WHERE YEAR(d2.TimePeriod) = YEAR(d1.TimePeriod) AND MONTH(d2.TimePeriod) = MONTH(d1.TimePeriod) 
                                       AND d2.CountryName = 'India' AND d2.DeviceTypeName = 'Desktop' GROUP BY d2.CountryName, d2.DeviceTypeName)
           WHEN @region = 'USA' THEN (SELECT SUM(d2.UserCount) FROM Data d2 
                                       WHERE YEAR(d2.TimePeriod) = YEAR(d1.TimePeriod) AND MONTH(d2.TimePeriod) = MONTH(d1.TimePeriod) 
                                         AND d2.CountryName = 'USA' AND d2.DeviceTypeName = 'Desktop' GROUP BY d2.CountryName, d2.DeviceTypeName)
           WHEN @region = 'Both' THEN (SELECT SUM(d2.UserCount) FROM Data d2 
                                        WHERE YEAR(d2.TimePeriod) = YEAR(d1.TimePeriod) AND MONTH(d2.TimePeriod) = MONTH(d1.TimePeriod) 
                                          AND d2.CountryName IN ('India', 'USA') AND d2.DeviceTypeName = 'Desktop' GROUP BY d2.DeviceTypeName)
           ELSE 0
       END AS [DesktopUsers]
  FROM Data d1
WHERE YEAR(d1.TimePeriod) = @calendarYear
GROUP BY YEAR(d1.TimePeriod), MONTH(d1.TimePeriod);

答案 1 :(得分:1)

看起来你可以摆脱case语句中的group by,而是总结外部查询。

select
    year(u.TimePeriod) AS [Year],
    month(u.TimePeriod) AS [Month],
    sum(case
        when d.DeviceTypeName = 'Mobile' and ((
                @region in ('India', 'Both') and 
                c.CountryName = 'India'
            ) or (
                @region in ('USA', 'Both') and
                c.CountryName = 'USA'
            )) then u.UserCount
        else
            0
        end) as [MobileUsers],
    sum(case 
            when d.DeviceTypeName = 'Desktop' and ((
                @region in ('India', 'Both') and 
                c.CountryName = 'India'
            ) or (
                @region in ('USA', 'Both') and
                c.CountryName = 'USA'
            )) then u.UserCount
        else
            0
        end) as [DesktopUsers]
from
    dbo.UserStat as u with (nolock)
        inner Join 
    dbo.Country as c with (nolock) 
        on c.CountryID = u.CountryID
        inner Join 
    dbo.DeviceType as d with (nolock) 
        on d.DeviceTypeID = u.DeviceTypeID 
where
    year(u.TimePeriod) = @calendarYear
group by
    year(u.TimePeriod),
    month(u.TimePeriod);

Example SQLFiddle

答案 2 :(得分:1)

Declare @region nvarchar(50)='BOTH'
Declare @calendarYear int = 2014

SELECT   YEAR(U.TimePeriod)  AS [Year]
        ,MONTH(U.TimePeriod) AS [Month]
        ,SUM(CASE WHEN D.DeviceTypeName = 'Mobile'  THEN U.UserCount ELSE NULL END) AS [MobileUsers]
        ,SUM(CASE WHEN D.DeviceTypeName = 'Desktop' THEN U.UserCount  ELSE NULL END) AS [DesktopUsers]

FROM dbo.UserStat   AS U WITH (NOLOCK)
Join dbo.Country    AS C WITH (NOLOCK) ON C.CountryID    = U.CountryID 
Join dbo.DeviceType AS D WITH (NOLOCK) ON D.DeviceTypeID = U.DeviceTypeID 
WHERE YEAR(U.TimePeriod) = @calendarYear
 AND (  
        (C.CountryName = @region AND @region <> 'Both')
        OR
        (@region = 'Both' AND C.CountryName IN ('India','USA'))
      )
GROUP BY YEAR(U.TimePeriod), MONTH(U.TimePeriod);

SQL FIDDLE

答案 3 :(得分:1)

对于您想要做的事情,您的查询无可救药地复杂化。我认为条件聚合更接近你真正想要的东西:

SELECT  YEAR(U.TimePeriod) AS [Year],
        MONTH(U.TimePeriod) AS [Month],
        SUM(CASE WHEN D.DeviceTypeName = 'Mobile' THEN U.UserCount ELSE 0 END) as MobileUsers,
        SUM(CASE WHEN D.DeviceTypeName = 'DeskTop' THEN U.UserCount ELSE 0 END) as DeskTopUsers
FROM dbo.UserStat AS U WITH (NOLOCK) Join
     dbo.Country AS C WITH (NOLOCK)
     ON C.CountryID = U.CountryID Join
     dbo.DeviceType AS D WITH (NOLOCK)
     ON D.DeviceTypeID = U.DeviceTypeID 
WHERE YEAR(U.TimePeriod) = @calendarYear AND
      (@region = 'Both' or @region = C.CountryName)
GROUP BY YEAR(U.TimePeriod), MONTH(U.TimePeriod);