как использовать строки таблицы SQL в качестве столбцов для другой таблицы - PullRequest
0 голосов
/ 16 марта 2019

У меня есть одна таблица действий, одна таблица пользователей и третья таблица, связывающая пользователей с действиями, использующими внешние ключи.

То, что я пытаюсь сделать, - это создать таблицу результатов, в которой действия будут представлены в виде столбцов, а пользователи - в виде строк, а ячейками будет количество действий такого типа, в котором участвовал пользователь.

Например, столбцы будут

User | Activity A | Activity B | Activity C

А пользователь, который выполнил каждое действие три раза, выдает строку

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 в систему завтра. Отчет не показал бы это. Как я могу использовать строки таблицы Activity в качестве столбцов?

Ответы [ 2 ]

1 голос
/ 16 марта 2019

Я сделал быстрый запрос, который может помочь.При этом используется функция 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
0 голосов
/ 16 марта 2019

Кажется, вы хотите условное агрегирование:

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;
...