我有以下表格:
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
子句,那么我也会得到同样的错误,你能告诉我为什么会出现这个错误,我该怎么办?任何帮助表示赞赏。
答案 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);
答案 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);