views:

45

answers:

2

I have a very simple query that I need pivoted, but I haven't got a clue how to turn it into a pivot table from what I've read in the literature. Everything I've read involves hard-coded columns or is too complex. It has to be simpler, or I just can't grasp CTE's.

The query is a list of user names and clients they have access to. So I have something like:

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

The total number of clients could be around 20 or so. Some users can access all clients, others only one or two. What I want to see is:

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

This involves three tables: a user table, a client table, and a cross-reference table (permissions) that unites users with their clients.

I've tried something like the following, but it obviously doesn't work (or even compile).

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

Any clues most appreciated!

A: 

This doesn't format very well. How do you format the question properly?

Larry
Highlight the code and press ctrl+k, or click the '01010' button. I've gone through it for you now anyway :)
Blorgbeard
Also, this answer should really have been a comment or edit to your question.
Blorgbeard
Sorry, the comment textbox wasn't there before - I swear! Thanks for your patience :)
Larry
by the way, StackOverflow is becoming one of my favorite answer boards, and I recommend it to everyone. This is the first time I posted on it though.
Larry
+2  A: 

I believe SQL server requires you to explicitly name each column in your pivot. So, the following snippet from your original query is not allowed:

for client_code in (select client_code from clients)

You would have to name each client code explicitly.

Edit: Here is a sample pivot to fit your example:

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

Edit: Here is a dynamic SQL option; you might put this in a 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 Petito
Thanks Michael - that must be why all the samples name the columns explicitly! That's a real PITA when you can have dozens of clients...
Larry
Agreed. You could form the query as a string and use `sp_executesql` if you have many clients. I'll update my answer with that shortly.
Michael Petito
So the only way is using dynamic sql? I've seen a few examples along those lines. I tried employing your above method, but unfortunately, I have thousands of users! That would take awhile to code.Maybe another answer is to just import the user/client list into Excel, and let Excel create the pivot table. Excel seems to be better at that kind of thing.
Larry
I'm not sure why it matters that you have thousands of users, since each user is just one row in the pivot. You could also use Excel; I guess it depends on how often you need to query the data.
Michael Petito
Thanks a billion Michael! I was able to translate your answer to my real world case with a couple of caveats. First, the number of users matters if I have to type them all in (there are some 33000 rows), but not if I use dynamic sql. Second, one of my client choices is "ALL", which for some inexplicable reason throws an error. If I remove ALL from the client options, it works beautifully. Weird. One last question, is it possible to replace the client names in the value fields with just X if they have access to that client, or blank instead of NULL? "No" is an acceptable answer...
Larry
@Larry `ALL` is a reserved word in SQL. Use ` ([' + @Command + '])`
Martin Smith
Ahhhh - thanks! lol
Larry
And replacing MAX by COUNT gives me just what I want. Thanks all!
Larry