SQL Server: Selecting using count and Case - sql-server

Hello a client of ours has requested a "count(values) as NAME"
now on it's own that would be nice and simple but what they are asking for is
3 seperate columns where the defining factor is an "ID"
with a count for values for instance:
Count(CC where CC in ('A','B','C')
adding up to something like this
select case((select count(CC where CC in ('A','B','C') AB,
count(CC where CC in ('D','E') DE and id = 1234),
case((select count(CC where CC in ('Z','X','Y') XY,
count(CC where CC in ('W','G') WG and id = 1235)
Can anyone think of anyway to make this possible?
Sample Data
ID, CC
1234 A
1234 B
1234 C
1234 A
1235 B
1235 B
1234 A
1235 C
1234 A
1234 B
1235 C
1234 A
1234 B
1235 B
1234 A
1235 C
Expected Output
CC ID:1234 ID:1235
A 6 0
B 3 3
C 1 1
( sorry to say Dynamic SQL is out the window on this one, the software the client uses to pull the information does not allow for temporary tables, updates, inserts or deletes)

Use dynamic SQL:
CREATE TABLE TestTbl (ID INT, CC VARCHAR(10));
INSERT INTO TestTbl VALUES
(1234,'A')
,(1234,'B')
,(1234,'C')
,(1234,'A')
,(1235,'B')
,(1235,'B')
,(1234,'A')
,(1235,'C')
,(1234,'A')
,(1234,'B')
,(1235,'C')
,(1234,'A')
,(1234,'B')
,(1235,'B')
,(1234,'A')
,(1235,'C');
DECLARE #PivotColumns VARCHAR(MAX)=STUFF(
(
SELECT DISTINCT ',[ID:' + CAST(ID AS VARCHAR(MAX)) + ']'
FROM TestTbl
FOR XML PATH('')
),1,1,'');
DECLARE #SqlCmd VARCHAR(MAX)=
'
SELECT p.*
FROM
(
SELECT DISTINCT CC
,COUNT(CC) OVER(PARTITION BY ID,CC) AS CountCC
,''ID:'' + CAST(ID AS VARCHAR(10)) AS ColumnName
FROM TestTbl AS tbl
) AS x
PIVOT
(
MIN(CountCC) FOR ColumnName IN(' + #PivotColumns + ')
) AS p
';
EXEC(#SqlCmd);
DROP TABLE TestTbl;
The Result:
CC ID:1234 ID:1235
A 6 NULL
B 3 3
C 1 3

I suspect I may have misunderstood your requirement, so please accept my apologies if this isn't the answer you are looking for for.
Is this just a case of conditionally counting rows, based on a combination of CC and ID?
Example
DECLARE #Sample TABLE
(
ID INT,
CC VARCHAR(1)
)
;
INSERT INTO #Sample
(
ID,
CC
)
VALUES
(1234, 'A'),
(1234, 'B'),
(1234, 'C'),
(1234, 'A'),
(1235, 'B'),
(1235, 'B'),
(1234, 'A'),
(1235, 'C'),
(1234, 'A'),
(1234, 'B'),
(1235, 'C'),
(1234, 'A'),
(1234, 'B'),
(1235, 'B'),
(1234, 'A'),
(1235, 'C')
;
SELECT
CC,
SUM(CASE WHEN ID = 1234 THEN 1 ELSE 0 END) AS [ID:1234],
SUM(CASE WHEN ID = 1235 THEN 1 ELSE 0 END) AS [ID:1235]
FROM
#Sample
GROUP BY
CC
;
Returned
CC ID:1234 ID:1235
A 6 0
B 3 3
C 1 3
The last record contains a different value in ID:1235 to your example. If you can explain why this should 1 I'll take another look at my query.

Related

How to replace all 11 consecutive number in a nvarchar field with asterisk in SQL Server

I have a textarea on the view which saves the input in an nvarchar(max) field. The users wrote an 11 number digit which is sensitive information on the textarea which they should not have. I was asked to replace the 11 consecutive number with ***********.
Sample Dataset
Id Details
1 Id: 03948518231
ddk asd
2 ed 99 93482019393 ex
3 ehhdg g#3 85291293841 ldd
Result after update:
Id Details
1 Id: ***********
ddk asd
2 ed 99 *********** ex
3 ehhdg g#3 *********** ldd
Something like this:
Update InfoTable SET Details=DetailsWithout11ConsecutiveNumbers
You can use stuff and patindex. But be aware that it will replace first occurrence of 11 consecutive numbers
declare #InfoTable table (
Details varchar(40)
)
insert into #InfoTable
values ('Id: 03948518231 ')
, ('ed 99 93482019393 ex')
, ('ehhdg g#3 85291293841 ldd')
update #InfoTable
set Details = stuff(Details, patindex('%[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]%', Details), 11, '***********')
where
patindex('%[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]%', Details) > 0
select * from #InfoTable
You can use PATINDEX but you also need to handle the case where you no sensitive values
DECLARE #Mytable table (ID tinyint NOT NULL, Details nvarchar(100) NOT NULL)
INSERT #Mytable VALUES
(1, 'Id: 03948518231 ddk asd'),
(2, 'ed 99 93482019393 ex'),
(3, 'ehhdg g#3 85291293841 ldd'),
(4, 'nothing secret here');
SELECT
ID,
CASE WHEN X.SensitiveStart > 0 THEN STUFF(Details, X.SensitiveStart, 11, '***********') ELSE X.Details END
FROM
(
SELECT
ID, Details,
SensitiveStart = PATINDEX('%[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]%', Details)
FROM
#Mytable
) X
If you have multiple sensitive values
DECLARE #Mytable table (ID tinyint NOT NULL, Details nvarchar(100) NOT NULL)
INSERT #Mytable VALUES
(1, 'Id: 03948518231 ddk asd'),
(2, 'ed 99 93482019393 ex'),
(3, 'ehhdg g#3 85291293841 ldd'),
(4, 'nothing secret here'),
(5, 'two 93482019393 sensitive 85291293841 values');
WITH MultipleSecretValue AS
(
SELECT
ID,
CASE WHEN X.SensitiveStart > 0 THEN STUFF(Details, X.SensitiveStart, 11, '***********') ELSE X.Details END AS Details,
0 AS Level
FROM
(
SELECT
ID, Details,
SensitiveStart = PATINDEX('%[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]%', Details)
FROM
#Mytable
) X
UNION ALL
SELECT
ID,
STUFF(Details, X.SensitiveStart, 11, '***********'),
Level + 1
FROM
(
SELECT
ID, Details,
SensitiveStart = PATINDEX('%[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]%', Details),
Level
FROM
MultipleSecretValue
) X
WHERE
SensitiveStart > 0
)
SELECT
X.ID, X.Details
FROM
(
SELECT
rn = ROW_NUMBER() OVER (PARTITION BY m.ID ORDER BY m.Level DESC),
m.ID,
m.Details
FROM
MultipleSecretValue m
) X
WHERE
X.rn = 1

Search child rows for values

I have something like this:
Transaction Customer
1 Cust1
2 Cust2
3 Cust3
4 Cust4
TransID Code
2 A
2 B
2 D
3 A
4 B
4 C
If I want to be able to do something like "IF Customer 'Cust1' Has code 'A'", how should I best build a view? I want to end up being able to query something like "Select Customer from View where Code in [Some list of codes]" OR "Select Cust1 from View Having Codes in [Some list of codes]"
While I can do something like
Customer | Codes
Cust1 | A, B, D
Etc.
SELECT Transaction from Tbl where Codes like 'A'
This seems to me to be an impractical way to do it.
Here's how I'd do it
;with xact_cust (xact, cust) as
(
select 1, 'cust1' union all
select 2, 'cust2' union all
select 3, 'cust3' union all
select 4, 'cust4'
), xact_code (xact, code) as
(
select 2, 'A' union all
select 2, 'B' union all
select 2, 'D' union all
select 3, 'A' union all
select 4, 'B' union all
select 4, 'C'
)
select Cust, Code
from xact_cust cust
inner join xact_code code
on cust.xact = code.xact
where exists (select 1
from xact_code i
where i.xact = code.xact
and i.code = 'A')
If you NEED the codes serialized into a delimited list, take a look at this article: What this query does to create comma delimited list SQL Server?
Here's another option...
IF OBJECT_ID('tempdb..#CustomerTransaction', 'U') IS NOT NULL
DROP TABLE #CustomerTransaction;
CREATE TABLE #CustomerTransaction (
TransactionID INT NOT NULL PRIMARY KEY,
Customer CHAR(5) NOT NULL
);
INSERT #CustomerTransaction (TransactionID, Customer) VALUES
(1, 'Cust1'), (2, 'Cust2'), (3, 'Cust3'),
(4, 'Cust4'), (5, 'Cust5');
IF OBJECT_ID('tempdb..#TransactionCode', 'U') IS NOT NULL
DROP TABLE #TransactionCode;
CREATE TABLE #TransactionCode (
TransactionID INT NOT NULL,
Code CHAR(1) NOT NULL
);
INSERT #TransactionCode (TransactionID, Code) VALUES
(2, 'A'), (2, 'B'), (2, 'D'), (3, 'A'), (4, 'B'), (4, 'C');
--SELECT * FROM #CustomerTransaction ct;
--SELECT * FROM #TransactionCode tc;
--=============================================================
SELECT
ct.TransactionID,
ct.Customer,
CodeList = STUFF(tcx.CodeList, 1, 1, '')
FROM
#CustomerTransaction ct
CROSS APPLY (
SELECT
', ' + tc.Code
FROM
#TransactionCode tc
WHERE
ct.TransactionID = tc.TransactionID
ORDER BY
tc.Code ASC
FOR XML PATH('')
) tcx (CodeList);
Results...
TransactionID Customer CodeList
------------- -------- -----------
1 Cust1 NULL
2 Cust2 A, B, D
3 Cust3 A
4 Cust4 B, C
5 Cust5 NULL

SQL Server : show rows as columns / Pivot [duplicate]

This question already has answers here:
SQL Server Pivot Table with multiple column aggregates
(3 answers)
Closed 6 years ago.
I have a SQL Server 2008 Express table like this:
rno gid uid dat origamt disamt
-----------------------------------------------
1 AA a 12-05-2016 200 210
2 AA b 12-05-2016 300 305
3 AA c 12-05-2016 150 116
4 BB a 12-05-2016 120 125
5 BB c 12-05-2016 130 136
6 CC a 12-05-2016 112 115
7 CC b 12-05-2016 135 136
and so on for different dates
I want to show it like this:
sno dat gid a_orig a_dis b_orig b_dis c_orig c_dis .....
1 12-05-2016 AA 200 210 300 305 150 116
2 12-05-2016 BB 120 125 0 0 130 136
3 12-05-2016 CC 112 115 135 136 0 0
NOTE: the values of uid are not fixed, they may vary dynamically, so, a_orig, a_dis, b_orig, b_dis, etc cannot be hardcoded into SQL.
NOTE: around 300 rows are expected on each date due to the cartesian product of gid and uid. and I will search datewise by implementing the LIKE clause since datatype of dat column is varchar(50).
Note: I would prefer datatype of origamt and disamt to be varchar(50) instead of Decimal(18, 0) but it is not compulsion.
I have tried to use PIVOT by taking reference from several articles posted here on stackoverflow and other website but couldn't get the work done completely.
Here is what I tried and got almost fine results with fixed uid and only fetched origamt:
select *
from
(
select gid, uid, dat, origamt
from vamounts
) as src
pivot
(
sum(origamt)
for uid IN ( a, b )
) as piv;
Kindly help me with the least bulky possible solution for this problem. I will prefer least lines of code and least complexity.
Errr, no. You can't generate your desired table using SQL. This isn't a valid pivot table.
"the values of uid are not fixed, they may vary dynamically, so,
a_orig, a_dis, b_orig, b_dis, etc cannot be hardcoded into SQL."
Sorry, this is also not possible. You must specify the exact values to be placed as the column headers. Whenever you write a SELECT statement, you must specify the names of the columns (fields) which you'll be returning. There's no way around this.
However, below are the steps required to create a "valid" SQL Server pivot table from your data:
I've got to admit, when I recently had to write my first PIVOT in SQL Server, I also Googled like mad, but didn't understand how to write it.
However, I eventually worked out what you need to do, so here's the step-by-step guide that you won't find anywhere else..!
(Readers can easily adapt these instructions, to use with your own data !)
1. Create your sample data
If you expect readers to reply to your Question, you should at least give them the SQL to create your sample data, so they have something to work off.
So, here's how I would create the data shown in your question:
CREATE TABLE tblSomething
(
[gid] nvarchar(100),
[uid] nvarchar(100),
[dat] datetime,
[origamt] int,
[disamt] int
)
GO
INSERT INTO tblSomething VALUES ('AA', 'a', '2016-05-12', 200, 210)
INSERT INTO tblSomething VALUES ('AA', 'b', '2016-05-12', 300, 305)
INSERT INTO tblSomething VALUES ('AA', 'c', '2016-05-12', 150, 116)
INSERT INTO tblSomething VALUES ('BB', 'a', '2016-05-12', 120, 125)
INSERT INTO tblSomething VALUES ('BB', 'c', '2016-05-12', 130, 136)
INSERT INTO tblSomething VALUES ('CC', 'a', '2016-05-12', 112, 115)
INSERT INTO tblSomething VALUES ('CC', 'b', '2016-05-12', 135, 136)
GO
2. Write a SQL Query which returns exactly three columns
The first column will contain the values which will appear in your PIVOT table's left-hand column.
The second column will contain the list of values which will appear on the top row.
The values in the third column will be positioned within your PIVOT table, based on the row/column headers.
Okay, here's the SQL to do this:
SELECT [gid], [uid], [origamt]
FROM tblSomething
This is the key to using a PIVOT. Your database structure can be as horribly complicated as you like, but when using a PIVOT, you can only work with exactly three values. No more, no less.
So, here's what that SQL will return. Our aim is to create a PIVOT table containing (just) these values:
3. Find a list of distinct values for the header row
Notice how, in the pivot table I'm aiming to create, I have three columns (fields) called a, b and c. These are the three unique values in your [uid] column.
So, to get a comma-concatenated list of these unique values, I can use this SQL:
DECLARE #LongString nvarchar(4000)
SELECT #LongString = COALESCE(#LongString + ', ', '') + '[' + [uid] + ']'
FROM [tblSomething]
GROUP BY [uid]
SELECT #LongString AS 'Subquery'
When I run this against your data, here's what I get:
Now, cut'n'paste this value: we'll need to place it twice in our overall SQL SELECT command to create the pivot table.
4. Put it all together
This is the tricky bit.
You need to combine your SQL command from Step 2 and the result from Step 3, into a single SELECT command.
Here's what your SQL would look like:
SELECT [gid],
-- Here's the "Subquery" from part 3
[a], [b], [c]
FROM (
-- Here's the original SQL "SELECT" statement from part 2
SELECT [gid], [uid], [origamt]
FROM tblSomething
) tmp ([gid], [uid], [origamt])
pivot (
MAX([origamt]) for [uid] in (
-- Here's the "Subquery" from part 3 again
[a], [b], [c]
)
) p
... and here's a confusing image, which shows where the components come from, and the results of running this command.
As you can see, the key to this is that SELECT statement in Step 2, and putting your three chosen fields in the correct place in this command.
And, as I said earlier, the columns (fields) in your pivot table come from the values obtained in step 3:
[a], [b], [c]
You could, of course, use a subset of these values. Perhaps you just want to see the PIVOT values for [a], [b] and ignore [c].
Phew !
So, that's how to create a pivot table out of your data.
I will prefer least lines of code and least complexity.
Yeah, good luck on that one..!!!
5. Merging two pivot tables
If you really wanted to, you could merge the contents of two such PIVOT tables to get the exact results you're looking for.
This is easy enough SQL for Shobhit to write himself.
You need dynamic SQL for this stuff.
At first create table with your data:
CREATE TABLE #temp (
rno int,
gid nvarchar(10),
[uid] nvarchar(10),
dat date,
origamt int,
disamt int
)
INSERT INTO #temp VALUES
(1, 'AA', 'a', '12-05-2016', 200, 210),
(2, 'AA', 'b', '12-05-2016', 300, 305),
(3, 'AA', 'c', '12-05-2016', 150, 116),
(4, 'BB', 'a', '12-05-2016', 120, 125),
(5, 'BB', 'c', '12-05-2016', 130, 136),
(6, 'CC', 'a', '12-05-2016', 112, 115),
(7, 'CC', 'b', '12-05-2016', 135, 136)
And then declare variables with columns:
DECLARE #columns nvarchar(max), #sql nvarchar(max), #columns1 nvarchar(max), #columnsN nvarchar(max)
--Here simple columns like [a],[b],[c] etc
SELECT #columns =STUFF((SELECT DISTINCT ','+QUOTENAME([uid]) FROM #temp FOR XML PATH('')),1,1,'')
--Here with ISNULL operation ISNULL([a],0) as [a],ISNULL([b],0) as [b],ISNULL([c],0) as [c]
SELECT #columnsN = STUFF((SELECT DISTINCT ',ISNULL('+QUOTENAME([uid])+',0) as '+QUOTENAME([uid]) FROM #temp FOR XML PATH('')),1,1,'')
--Here columns for final table orig.a as a_orig, dis.a as a_dis,orig.b as b_orig, dis.b as b_dis,orig.c as c_orig, dis.c as c_dis
SELECT #columns1 = STUFF((SELECT DISTINCT ',orig.'+[uid] + ' as ' +[uid]+ '_orig, dis.'+[uid] + ' as ' +[uid]+ '_dis' FROM #temp FOR XML PATH('')),1,1,'')
And main query:
SELECT #sql = '
SELECT orig.gid,
orig.dat,
'+#columns1+'
FROM (
SELECT gid, dat, '+#columnsN+'
FROM (
SELECT gid, [uid], LEFT(dat,10) as dat, origamt
FROM #temp
) as p
PIVOT (
SUM(origamt) FOR [uid] in ('+#columns+')
) as pvt
) as orig
LEFT JOIN (
SELECT gid, dat, '+#columnsN+'
FROM (
SELECT gid, [uid], LEFT(dat,10) as dat, disamt
FROM #temp
) as p
PIVOT (
SUM(disamt) FOR [uid] in ('+#columns+')
) as pvt
) as dis
ON dis.gid = orig.gid and dis.dat = orig.dat'
EXEC(#sql)
Output:
gid dat a_orig a_dis b_orig b_dis c_orig c_dis
AA 2016-12-05 200 210 300 305 150 116
BB 2016-12-05 120 125 0 0 130 136
CC 2016-12-05 112 115 135 136 0 0
A join might help
declare #t table (rno int, gid varchar(2), uid varchar(1), dat varchar(10), origamt int, disamt int)
insert into #t
values
(1, 'AA', 'a', '12-05-2016', 200, 210),
(2 , 'AA', 'b', '12-05-2016', 300, 305),
(3 , 'AA', 'c', '12-05-2016', 150, 116),
(4 , 'BB', 'a', '12-05-2016', 120, 125),
(5 , 'BB', 'c', '12-05-2016', 130, 136),
(6 , 'CC', 'a', '12-05-2016', 112, 115),
(7 , 'CC', 'b', '12-05-2016', 135, 136)
select -- piv.*,piv2.*
piv.gid,piv.dat
,piv.a as a_org
,piv2.a as a_dis
,piv.b as b_org
,piv2.b as b_dis
,piv.c as c_org
,piv2.c as c_dis
from
(
select gid, uid, dat, origamt
from #t
) as src
pivot
(
sum(origamt)
for uid IN ([a],[b],[c] )
) as piv
join
(select piv2.*
from
(
select gid, uid, dat, disamt
from #t
) as src
pivot
(
sum(disamt)
for uid IN ([a],[b],[c] )
) as piv2
) piv2
on piv2.gid = piv.gid and piv2.dat = piv.dat
This is a POC you would have to use dynamic sql to deal with the variable number of uids. Let me know if you don't know how to use dynamic SQL and I'll work up an example for you.

Compare when one field is empty but concatenation is to be compared

I have two tables.
Tbl1_Students
Name Class Section
Joe 8 A
Pin 8 B
Tea 8 C
Mia 9 A
Ton 9 B
Mon 10
Tim 7 A
Tbl_Exclusion
Class Section
7 A
8 B
9
The query should exclude all records where
class= 7 and Section =A and
Class= 8 and Section =B and
Class= 9 (all the sections of 9)
I have written below query :
SELECT * FROM TBL_STUDENTS WHERE
CLASS + ISNULL(SECTION, '') NOT IN (
SELECT Class + ISNULL(SECTION, '') FROM tbl_EXCLUSION)
But this wont work for case 3 where if alone class name is given then exclude all rows where class 9 is given ignoring section.
Use NOT EXISTS rather than NOT IN. It allows you to use more than one field for comparison when doing the exclusion:
SELECT *
FROM tbl_Students AS s
WHERE NOT EXISTS
( SELECT 1
FROM tbl_Exclusion AS e
WHERE e.Class = s.Class
AND (e.Section IS NULL OR e.Section = s.Section)
);
WORKING EXAMPLE
CREATE TABLE #tbl_Students (Name VARCHAR(10), Class INT, Section CHAR(1));
CREATE TABLE #tbl_Exclusion (Class INT, Section CHAR(1));
INSERT #tbl_Students (Name, Class, Section)
VALUES
('Joe', 8, 'A'), ('Pin', 8, 'B'), ('Tea', 8, 'C'), ('Mia', 9, 'A'),
('Ton', 9, 'B'), ('Mon', 10, NULL), ('Tim', 7, 'A ');
INSERT #tbl_Exclusion (Class, Section)
VALUES (7, 'A'), (8, 'B'), (9, NULL);
SELECT *
FROM #tbl_Students AS s
WHERE NOT EXISTS
( SELECT 1
FROM #tbl_Exclusion AS e
WHERE e.Class = s.Class
AND (e.Section IS NULL OR e.Section = s.Section)
);
DROP TABLE #tbl_Students, #tbl_Exclusion;
RESULTS
Name | Class | Section
-----+-------+---------
Joe | 8 | A
Tea | 8 | C
Mon | 10 | NULL
My query would run for 10,00,00,000 rows atleast. The solution GarethD gave was good, but I still felt below one was more clear to me.
Query would be:
SELECT *
FROM tbl_student s
WHERE
(
(
s.class + ISNULL(s.section, '') NOT IN
(
SELECT e.class + e.section
FROM tbl_exclusion e
WHERE e.section IS NOT NULL
)
)
AND
(
s.class NOT IN
(
SELECT e.class
FROM tbl_exclusion
WHERE e.section IS NULL
)
)
)

Left join get all data in both table

I have two tables Q and A,
records are A are
QID UserID Value
1 100 A
2 100 B
3 100 C
1 101 AA
2 101 BB
3 101 CC
1 102 AAA
2 102 BBB
As you can see, there is no record for user 102 for QID 3. There is this another table Q.
QID Value
1 Name
2 Email
3 Site
What I want is, for each user, weather they have answered a question or not (that is, weather a entry exits in A table or not) I want all questions for all users and their answers. Something like this.
QID QValue UserID Value
1 Name 100 A
2 Email 100 B
3 Site 100 C
1 Name 101 AA
2 Email 101 BB
3 Site 101 CC
1 Name 102 AAA
2 Email 102 BBB
What the problem is one row is missing from the desired output, and that is
3 Site 102 NULL
Because for user 102 there is no entry in A table. I tried LEFT JOIN, but obviously it won't give the desired result as all the left table are already there. And INNER JOIN doesn't works either.
It is also complete possible for answers table (table A) to have data like this
QID QValue UserID Value
1 Name 100 A
2 Email 101 BB
3 Site 102 CCC
Say, all users just have filled in one record, in this case desired output is something like this
QID QValue UserID Value
1 Name 100 A
2 Email 100 NULL
3 Site 100 NULL
1 Name 101 NULL
2 Email 101 BB
3 Site 101 NULL
1 Name 102 NULL
2 Email 102 NULL
3 Email 102 CCC
If I do a LEFT JOIN on QID it doesn't works. Please suggest what should be done.
Try this:
declare #A table(QID int, UserID int, Value varchar(10))
declare #Q table(QID int, Value varchar(10))
insert into #A values (1, 100, 'A')
insert into #A values (2, 100, 'B')
insert into #A values (3, 100, 'C')
insert into #A values (1, 101, 'AA')
insert into #A values (2, 101, 'BB')
insert into #A values (3, 101, 'CC')
insert into #A values (1, 102, 'AAA')
insert into #A values (2, 102, 'BBB')
insert into #Q values (1, 'Name')
insert into #Q values (2, 'Email')
insert into #Q values (3, 'Site')
select
U.UserID,
Q.QID,
Q.Value as QValue,
A.Value
from
(select distinct UserID from #A) U -- all Users
cross join #Q Q -- all Questions
left outer join #A A on A.UserID = U.UserID and A.QID = Q.QID
So basically you do a cross join between all questions and all users first to get all combinations. Then you take this result and do a left join with all the answers. Missing answers will have NULL values in the Value (the real answer) field.

Resources