Запрос со столбцами, рассчитанными во время выполнения - PullRequest
0 голосов
/ 20 апреля 2020

Мне нужна помощь с запросом в SQL Server 2012.

У моего клиента есть (вроде) внутренняя вики, где его сотрудники сохраняют документы; эти документы хранятся в таблице в базе данных (назовем ее Documentation, с колонками ID, Title, Text и Tags) и доступны через веб-интерфейс.

До сих пор каждый сотрудник имел доступ ко всем документам, теперь мой клиент теперь хочет, чтобы его сотрудники имели доступ только к определенным c процедурам. Не существует конкретных c критериев, таких как роль или тип пользователя, он хочет решить, кто что видит, на индивидуальной основе (отныне Джон будет видеть документы 1,2 и 4, в то время как J anet будет видеть только документы 3, 4 и 5 и тд). Не спрашивайте меня, почему ...

Моя задача - подготовить большую таблицу во внешнем интерфейсе, где каждая строка - это документ, а сотрудники - в столбце; для каждого документа есть флажок для каждого сотрудника, чтобы указать, может ли этот пользователь получить доступ к документу, что-то вроде этого:

enter image description here

Проблема заключается в том, что количество документов не фиксировано, ни количество работников. Мне нужно найти запрос для извлечения этих данных, не зная количества столбцов, он должен выполняться динамически во время выполнения.

Конечно, у меня есть доступ к таблице пользователей, поэтому я знаю номер и имя работники. Что касается разрешений, я думал, что смогу использовать новую таблицу, содержащую всего 3 столбца: ID, ID_Document и ID_User.

Я гуглил и искал, но не смог найти подходящий ответ. Я пытался использовать сводную таблицу, но она мне не кажется правильной, мне не нужно объединять данные.

Кто-нибудь может мне помочь?

1 Ответ

2 голосов
/ 20 апреля 2020

Я собираюсь, мы предположим, у вас всего 5 таблиц. Я собираюсь назвать эти Document, Tag, Employee, а затем DocumentTag и DocumentEmployee. Поэтому, чтобы получить решение, которое вам нужно, вам нужно 2 различных типа агрегации, агрегирование строк для тегов и поворот для сотрудников.

--Base tables
CREATE TABLE dbo.Document (DocumentID int, DocumentTitle nvarchar(50));

CREATE TABLE dbo.Employee (EmployeeID int, EmployeeName nvarchar(50));

CREATE TABLE dbo.Tag (TagID int, TagName nvarchar(50));

GO
--Link tables
CREATE TABLE dbo.DocumentTag (DocumentID int, TagID int);

CREATE TABLE dbo.DocumentEmployee (DocumentID int, EmployeeID int);

GO
--Sample data

INSERT INTO dbo.Document
VALUES(2,N'Important Doc'),(3,N'New Doc');

INSERT INTO dbo.Employee
VALUES(1,N'John'),
      (2,N'Mary'),
      (3,N'Patricia'),
      (4,N'Paul');

INSERT INTO dbo.Tag
VALUES(1,N'Classified'),
      (2,N'Finance'),
      (3,N'Warehouse');
GO

--Link Data

INSERT INTO dbo.DocumentTag
VALUES(1,1),(1,2),(2,3);

INSERT INTO dbo.DocumentEmployee
VALUES(1,1),(1,2),(1,3),(2,2),(2,4);

Если вам не нужен динамический свод c, то ваш SQL будет выглядеть примерно так:

SELECT D.DocumentID,
       D.DocumentTitle,
       STUFF((SELECT N' ' + T.TagName
              FROM dbo.DocumentTag DT
                   JOIN dbo.Tag T ON DT.TagID = T.TagID
              WHERE DT.DocumentID = D.DocumentID
              FOR XML PATH(N''),TYPE).value('.','nvarchar(MAX)'),1,1,N'') AS Tags,
       MAX(CASE E.EmployeeName WHEN N'John' THEN 1 ELSE 0 END) AS John,
       MAX(CASE E.EmployeeName WHEN N'Mary' THEN 1 ELSE 0 END) AS Mary,
       MAX(CASE E.EmployeeName WHEN N'Patricia' THEN 1 ELSE 0 END) AS Patricia,
       MAX(CASE E.EmployeeName WHEN N'Paul' THEN 1 ELSE 0 END) AS Paul
FROM dbo.Document D
     JOIN dbo.DocumentEmployee DE ON D.DocumentID = DE.EmployeeID
     JOIN dbo.Employee E ON DE.EmployeeID = E.EmployeeID
GROUP BY D.DocumentID,
         D.DocumentTitle;

Поскольку вам нужно масштабировать набор данных при добавлении сотрудников, вам понадобится динамический c SQL для этого. Таким образом, чтобы достичь этого, используя вышеуказанное решение, вы можете сделать что-то вроде этого:

DECLARE @SQL nvarchar(MAX),
        @CRLF nchar(2) = NCHAR(13) + NCHAR(10);

SET @SQL = N'SELECT D.DocumentID,' + @CRLF +
           N'       D.DocumentTitle,' + @CRLF +
           N'       STUFF((SELECT N'' '' + T.TagName' + @CRLF +
           N'              FROM dbo.DocumentTag DT' + @CRLF +
           N'                   JOIN dbo.Tag T ON DT.TagID = T.TagID' + @CRLF +
           N'              WHERE DT.DocumentID = D.DocumentID' + @CRLF +
           N'              FOR XML PATH(N''''),TYPE).value(''.'',''nvarchar(MAX)''),1,1,N'''') AS Tags,' + @CRLF +
           STUFF((SELECT N',' + @CRLF + 
                         N'       MAX(CASE E.EmployeeName WHEN N' + QUOTENAME(E.EmployeeName,'''') + N' THEN 1 ELSE 0 END) AS ' + QUOTENAME(E.EmployeeName)
                  FROM dbo.Employee E
                  ORDER BY E.EmployeeID ASC
                  FOR XML PATH(''),TYPE).value('.','nvarchar(MAX)'),1,3,N'') + @CRLF +
           N'FROM dbo.Document D' + @CRLF +
           N'     JOIN dbo.DocumentEmployee DE ON D.DocumentID = DE.EmployeeID' + @CRLF +
           N'     JOIN dbo.Employee E ON DE.EmployeeID = E.EmployeeID' + @CRLF +
           N'GROUP BY D.DocumentID,' + @CRLF +
           N'         D.DocumentTitle;';

--PRINT @SQL; --YOur debugging friend

EXEC sys.sp_executesql @SQL;

DB <> Fiddle

...