如何将SQL表行用作另一个表的列

时间:2019-03-15 21:20:26

标签: sql select foreign-keys row

我有一个活动表,一个用户表和第三个表,这些表使用外键将用户链接到活动。

我要做的是创建一个结果表,其中将活动作为列,将用户作为行,其中单元格是用户参与的该类型活动的数量。

例如,列将是

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输入系统,这对我没有帮助。该报告不会显示。如何将“活动”表的行用作列?

2 个答案:

答案 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;