我有一个活动表,一个用户表和第三个表,这些表使用外键将用户链接到活动。
我要做的是创建一个结果表,其中将活动作为列,将用户作为行,其中单元格是用户参与的该类型活动的数量。
例如,列将是
User | Activity A | Activity B | Activity C
进行过每项活动3次的用户将得到一行
John Doe | 3 | 3 | 3
现在,如果我为数据库中的每个活动手动添加count()调用,就可以轻松地做到这一点,例如:
select
u.name,
(select count(*)
from userActivity ua
where ua.userID = user.userID and ua.activityID = 1),
(select count(*)
from userActivity ua
where ua.userID = user.userID and ua.activityID = 2),
(select count(*)
from userActivity ua
where ua.userID = user.userID and ua.activityID = 3)
from
user u
但是,如果明天有人将活动D输入系统,这对我没有帮助。该报告不会显示。如何将“活动”表的行用作列?
答案 0 :(得分:1)
我做了一个快速查询,可能会有所帮助。这使用了前面提到的Pivot函数。
您可以运行整个过程,也可以跳到最底部!
-- Temp tables
IF OBJECT_ID('tempdb.dbo.#_tmp') IS NOT NULL DROP TABLE #_tmp
IF OBJECT_ID('tempdb.dbo.#_user') IS NOT NULL DROP TABLE #_user
IF OBJECT_ID('tempdb.dbo.#_activity') IS NOT NULL DROP TABLE #_activity
IF OBJECT_ID('tempdb.dbo.#_useractivity') IS NOT NULL DROP TABLE #_useractivity
-- User table
CREATE TABLE #_user (
[USER_ID] INT IDENTITY(1,1) NOT NULL,
[FIRST_NAME] NVARCHAR(50)
)
INSERT INTO #_user ([FIRST_NAME])
VALUES ('John'), ('Peter'), ('Paul')
-- Activity table
CREATE TABLE #_activity (
[ACTIVITY_ID] INT IDENTITY(1,1) NOT NULL,
[ACTIVITY_NAME] NVARCHAR(255)
)
INSERT INTO #_activity ([ACTIVITY_NAME])
VALUES ('Sailing'), ('Bowling'), ('Hiking')
-- Composite table
CREATE TABLE #_useractivity (
[LOG_ID] INT IDENTITY(1,1) NOT NULL,
[USER_ID] INT,
[ACTIVITY_ID] INT
)
INSERT INTO #_useractivity ([USER_ID], [ACTIVITY_ID])
VALUES (1,1),(1,2),(1,3),(1,3),(2,2),(2,3),(3,1), (3,2),(1,2),(2,1)
-- Main data table.
SELECT USR.FIRST_NAME
, A.ACTIVITY_NAME
INTO #_tmp
FROM #_useractivity AS UA
INNER JOIN #_user AS USR ON USR.USER_ID = UA.USER_ID
INNER JOIN #_activity AS A ON A.ACTIVITY_ID = UA.ACTIVITY_ID
SELECT * FROM #_tmp
-- Use pivot function to get desired results.
DECLARE @_cols AS NVARCHAR(MAX)
DECLARE @_sql AS NVARCHAR(MAX)
SET @_cols = STUFF((SELECT ',' + QUOTENAME(T.ACTIVITY_NAME)
FROM #_tmp AS T
GROUP BY T.ACTIVITY_NAME
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'),1,1,'')
-- Trick is to add 1 "counter" before pivoting.
set @_sql = '
SELECT Name, ' + @_cols + '
FROM (
SELECT FIRST_NAME AS Name, ACTIVITY_NAME, 1 AS COUNT
FROM #_tmp
) AS SRC
PIVOT (
SUM(COUNT) FOR ACTIVITY_NAME IN (' + @_cols + ')
) p'
EXEC(@_sql)
主数据表:
FIRST_NAME ACTIVITY_NAME
John Sailing
John Bowling
John Hiking
John Hiking
Peter Bowling
Peter Hiking
Paul Sailing
Paul Bowling
John Bowling
Peter Sailing
输出:
Name Bowling Hiking Sailing
John 2 2 1
Paul 1 NULL 1
Peter 1 1 1
答案 1 :(得分:0)
您似乎想要条件聚合:
select u.name,
sum(case when ua.activityID = 1 then 1 else 0 end) as cnt_1,
sum(case when ua.activityID = 2 then 1 else 0 end) as cnt_2,
sum(case when ua.activityID = 3 then 1 else 0 end) as cnt_3
from user u left join
userActivity ua
on ua.userID = u.userID
group by u.name;