【问题标题】:Query with columns calculated at runtime使用在运行时计算的列进行查询
【发布时间】:2020-08-03 05:51:53
【问题描述】:

我需要一些有关 SQL Server 2012 查询的帮助。

我的客户有一个(某种)内部 wiki,他的员工可以在其中保存文档;这些文档存储在数据库的一个表中(我们称之为Documentation,列有IDTitleTextTags)并通过Web 前端访问。

到目前为止,每个员工都可以访问所有文档,现在我的客户希望他的员工只能访问特定程序。没有特定的标准,例如角色或用户类型,他想根据个人情况决定谁看到什么(从现在开始,John 将看到文件 1,2 和 4,而 Janet 将只看到文件 3,4 和 5,依此类推)。不要问我为什么……

我的任务是在前端准备一个大表,其中每一行都是一个文档,员工在列中;对于每个文档,每个员工都有一个复选框,用于指示该用户是否可以访问该文档,类似于以下内容:

问题是文件数量不固定,员工人数也不固定。我需要在不知道列数的情况下找到一个查询来提取这些数据,它必须在运行时动态完成。

我当然可以访问用户表,所以我知道员工的号码和姓名。至于授权,我认为我可以使用一个只有 3 列的新表:IDID_DocumentID_User

我用谷歌搜索并搜索,但找不到合适的答案。我尝试使用数据透视表,但对我来说似乎不合适,我不必对数据进行任何聚合。

谁能帮帮我?

【问题讨论】:

  • PIVOT 中的聚合没有任何意义。 mssqltips.com/sqlservertip/2783/…
  • 这个解决方案的可扩展性不是很好。您正在寻找用于显示的动态数据透视表,但更新基础表并不容易。
  • 那么您的数据库中是否有规范化的表格,其中包含用户、文档、标签的表格(因为看起来一个文档有很多),然后是链接它们的表格?您的数据实际上是什么样的?
  • @Larnu 涉及 3 个表:一张用于文档,一张用于用户,一张用于授权
  • 我同意@KeithL,但表格文档和用户已经存在,并且“大表格”页面似乎对客户是强制性的

标签: sql-server dynamic


【解决方案1】:

我要我们假设您总共有 5 张桌子。我将调用这些DocumentTagEmployee,然后是DocumentTagDocumentEmployee。因此,要获得解决方案,您需要 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);

如果不需要动态数据透视,那么您的 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;

但是,由于您需要在添加员工时扩展数据集,因此您需要动态 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

【讨论】:

  • 谢谢@Larnu,我的帖子似乎并不清楚:我有 3 个表,用户 - 文档 - 授权。还是谢谢你的回复
  • @SCdev 不幸的是,没有样本数据,我假设您有一个完全规范化的数据库。但是,您应该可以轻松更改此设置,以便为您的非规范化标签;因为您只需要将列添加到 SELECTGROUP BY 并删除适当的 FOR XML PATH 表达式。这涵盖了所有场景,但是,如果您不需要字符串聚合,您实际上可以使用稍微简单的 SQL。
  • 这仍然适用。你只是没有标准化你的标签。
  • 这是一个关于如何呈现数据的好例子,但这不会导致可编辑的前端。我仍然认为您需要显示它,然后有一个编辑按钮,允许您使用 1 个文档。
  • 不,OP 需要在其前端处理正确的INSERT/DELETE 语句,例如@KeithL .A DELETE,就像:DELETE DE FROM dbo.DocumentEmployee DE JOIN dbo.Employee E ON DE.EmployeeID = E.EmployeeID WHERE DE.DocumentID = @DocumentID AND E.EmployeeName = @EmployeeName; (当然,这假设员工的姓名是唯一的,这是一个愚蠢的假设*,但 OP 的要求也是如此。)
猜你喜欢
  • 2017-08-21
  • 2019-04-24
  • 2021-10-23
  • 2021-12-23
  • 1970-01-01
  • 2017-06-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多