【问题标题】:How do I create a Pivot query for this simple case?如何为这个简单的案例创建一个 Pivot 查询?
【发布时间】:2010-06-01 14:34:25
【问题描述】:

我有一个非常简单的查询需要进行透视,但我不知道如何根据我在文献中阅读的内容将其转换为透视表。我读过的所有内容都涉及硬编码的列或过于复杂。它必须更简单,否则我无法掌握 CTE。

查询是用户名和他们有权访问的客户端的列表。所以我有类似的东西:

user  client
1 a
1 b
1 c
2 a
2 d
3 a
3 d
3 e
3 f

客户总数可能在 20 左右。一些用户可以访问所有客户端,而其他用户只能访问一两个。我想看到的是:

user a b c d e f
1 x x x
2 x   x
3 x   x x x

这涉及三个表:一个用户表、一个客户表和一个将用户与其客户联合起来的交叉引用表(权限)。

我已经尝试过类似以下的方法,但它显然不起作用(甚至无法编译)。

with usersclients(user_name, clients, client_code)
as
(
 select users.user_name
 from clients cli
  left join
  (select u.user_id, user_name, c.client_id, client_code, client_name 
   from permissions p inner join clients c on p.client_id = c.client_id
   inner join users u on u.user_id = p.user_id
  ) user
  on user.client_id = cli.client_id
)
select *
from usersclients
pivot (max(client_code) for client_code in (select client_code from clients)) as P

最感谢的任何线索!

【问题讨论】:

    标签: sql-server sql-server-2005 common-table-expression


    【解决方案1】:

    我相信 SQL Server 要求您明确命名数据透视表中的每一列。因此,您的原始查询中的以下 sn-p 是不允许的:

    for client_code in (select client_code from clients)

    您必须明确命名每个客户端代码。

    编辑:这是适合您的示例的示例枢轴:

    WITH Permit (Usr, Client) AS
    (
        SELECT 1, 'a' UNION ALL
        SELECT 1, 'b' UNION ALL
        SELECT 1, 'c' UNION ALL
        SELECT 2, 'a' UNION ALL
        SELECT 2, 'd' UNION ALL
        SELECT 3, 'a' UNION ALL
        SELECT 3, 'd' UNION ALL
        SELECT 3, 'e' UNION ALL
        SELECT 3, 'f'
    )
    SELECT p.*
    FROM Permit
        PIVOT (MAX(Client) FOR Client IN (a, b, c, d, e, f)) p
    

    编辑:这是一个动态 SQL 选项;你可以把它放在 TVF 中:

    --source table
    CREATE TABLE #Permit (Usr int, Client char(1));
    INSERT INTO #Permit (Usr, Client)
    SELECT 1, 'a' UNION ALL
    SELECT 1, 'b' UNION ALL
    SELECT 1, 'c' UNION ALL
    SELECT 2, 'a' UNION ALL
    SELECT 2, 'd' UNION ALL
    SELECT 3, 'a' UNION ALL
    SELECT 3, 'd' UNION ALL
    SELECT 3, 'e' UNION ALL
    SELECT 3, 'f';
    
    
    DECLARE @Command nvarchar(max);
    SET @Command = '';
    
    --prepare the list of columns
    SELECT @Command = @Command + ', ' + CONVERT(nvarchar(10), Client)
    FROM (SELECT DISTINCT Client FROM #Permit) x;
    
    --chop the extra leading comma off
    SET @Command = SUBSTRING(@Command, 3, LEN(@Command));
    
    --prepare the rest of the pivot command
    SET @Command = N'
    SELECT p.*
    FROM #Permit
        PIVOT (MAX(Client) FOR Client IN (' + @Command + ')) p';
    
    --execute the command
    EXEC sp_executesql @Command;
    
    
    DROP TABLE #Permit;
    

    【讨论】:

    • 感谢 Michael - 这就是为什么所有样本都明确命名列的原因!当您可以拥有数十个客户时,这才是真正的 PITA……
    • 同意。如果您有很多客户,您可以将查询形成为字符串并使用sp_executesql。我会尽快更新我的答案。
    • 那么唯一的办法就是使用动态sql?我已经看到了一些类似的例子。我尝试使用您的上述方法,但不幸的是,我有成千上万的用户!这将需要一段时间来编码。也许另一个答案是将用户/客户列表导入 Excel,然后让 Excel 创建数据透视表。 Excel 似乎更擅长这种事情。
    • 我不知道为什么你有成千上万的用户很重要,因为每个用户只是枢轴中的一行。您也可以使用 Excel;我想这取决于您需要多久查询一次数据。
    • 感谢十亿迈克尔!我能够将您的答案翻译成我的真实案例,但需要注意几点。首先,如果我必须全部输入用户数量(大约有 33000 行),那么用户数量很重要,但如果我使用动态 sql 则不重要。其次,我的客户选择之一是“ALL”,由于某种莫名其妙的原因会引发错误。如果我从客户端选项中删除 ALL,它会很好地工作。奇怪的。最后一个问题,如果他们有权访问该客户端,是否可以仅用 X 替换值字段中的客户端名称,或者空白而不是 NULL? “不”是可以接受的答案...
    猜你喜欢
    • 1970-01-01
    • 2012-03-25
    • 2020-01-13
    • 1970-01-01
    • 1970-01-01
    • 2021-12-21
    • 2021-10-27
    相关资源
    最近更新 更多