Using UNPIVOT and column names for field values - sql-server-2012

The context is online assessment responses. The source data I have to work with is one record per Participant ( test taker ) with over 140 columns representing the answer value for each question. The column name would correspond to the "ItemCode" of the specific question.
I want to move this data into the following table
ResponseItems
ParticipantID
Form ( 1 or 2 depending on ParticipantID )
ItemCode ( Name of the Column )
ItemDesc ( pulled from Codebook on ItemCode and Form
AnswerValue
I can attack this with a large Union statement but it does not scale to 140 columns wide.
But as an example, here is code that works for the 1st 3 Item Codes or Columns of answers:
Select ar.ParticipantID, ar.Form, c.ItemCode, c.ItemDesc, ar.SJT_01 as 'AnswerValue'
From dbo.Responses ar
Inner Join dbo.CodeBook c on c.ItemCode = 'SJT_01'
and c.Form = ar.Form
UNION ALL
Select ar.ParticipantID, ar.Form, c.ItemCode, c.ItemDesc, ar.SJT_02 as 'AnswerValue'
From dbo.Responses ar
Inner Join dbo.CodeBook c on c.ItemCode = 'SJT_02'
and c.Form = ar.Form
UNION ALL
Select ar.ParticipantID, ar.Form, c.ItemCode, c.ItemDesc, ar.SJT_03 as 'AnswerValue'
From dbo.Responses ar
Inner Join dbo.CodeBook c on c.ItemCode = 'SJT_03'
and c.Form = ar.Form
If I had 100 Test Takers and 3 Item Codes ( 3 columns with answers I want to capture, I am taking a 100 record table and creating 300 records.
I have studied the UNPIVOT command but I can't seem to get it to work.
Any suggestions would be greatly appreciated.
Source data with 10 test takers ... results desired are
ParticipantID, Form, ItemCode, AnswerValue
AICPAPSS003 1 SJT_01 5
AICPAPSS003 1 SJT_02 1
AICPAPSS003 1 SJT_03 3
AICPAPSS007 1 SJT_01 3
AICPAPSS007 1 SJT_02 1
AICPAPSS007 1 SJT_03 5
etc... ( a total of 30 records would be created )

Here you go - I think this is what you were after. If you have 140+ columns you might want to use some dynamic SQL to generate the final query. There are heaps of examples on SO.
Setup
IF(OBJECT_ID('Tempdb..#Test')) IS NOT NULL
DROP TABLE #Test;
SELECT * INTO #Test FROM (VALUES
('AICPAPSS003',1,5, 1, 3),
('AICPAPSS007',1,3, 1, 5),
('AICPAPSS012',1,2, 1, 4),
('AICPAPSS016',1,3, 2, 5),
('AICPAPSS019',1,1, 2, 5),
('AICPAPSS024',1,3, 2, 4),
('AICPAPSS025',1,1, 1, 4),
('AICPAPSS032',1,1, 2, 4),
('AICPAPSS033',1,3, 4, 5),
('AICPAPSS034',1,1, 2, 4)) A (ParticipantID, Form, SJT_01, SJT_02, SJT_03);
Query
SELECT ParticipantID, Form, ItemCode, Value
FROM #Test t
UNPIVOT
(
Value FOR ItemCode IN (SJT_01, SJT_02, SJT_03)
) u;
Results
ParticipantID Form ItemCode Value
------------- ----------- -------------------------------------------------------------------------------------------------------------------------------- -----------
AICPAPSS003 1 SJT_01 5
AICPAPSS003 1 SJT_02 1
AICPAPSS003 1 SJT_03 3
AICPAPSS007 1 SJT_01 3
AICPAPSS007 1 SJT_02 1
AICPAPSS007 1 SJT_03 5
AICPAPSS012 1 SJT_01 2
AICPAPSS012 1 SJT_02 1
AICPAPSS012 1 SJT_03 4
AICPAPSS016 1 SJT_01 3
AICPAPSS016 1 SJT_02 2
AICPAPSS016 1 SJT_03 5
AICPAPSS019 1 SJT_01 1
AICPAPSS019 1 SJT_02 2
AICPAPSS019 1 SJT_03 5
AICPAPSS024 1 SJT_01 3
AICPAPSS024 1 SJT_02 2
AICPAPSS024 1 SJT_03 4
AICPAPSS025 1 SJT_01 1
AICPAPSS025 1 SJT_02 1
AICPAPSS025 1 SJT_03 4
AICPAPSS032 1 SJT_01 1
AICPAPSS032 1 SJT_02 2
AICPAPSS032 1 SJT_03 4
AICPAPSS033 1 SJT_01 3
AICPAPSS033 1 SJT_02 4
AICPAPSS033 1 SJT_03 5
AICPAPSS034 1 SJT_01 1
AICPAPSS034 1 SJT_02 2
AICPAPSS034 1 SJT_03 4
(30 row(s) affected)

Related

case end / self-join postres sql

I am trying to process data within the same table.
Input:
Table
id sort value
1 1 1
2 1 8
3 2 0
4 1 2
What I want to achieve is obtain for each id, the first encountered value for all value equal to its sort, and this ordered by id.
Output
Table
id sort value new
1 1 1 1
2 1 8 1
3 2 0 0
4 1 2 1
I tried to self join the table, but I constantly get relation not found. I tried with a case statement but I don't see how can I connect to the same table, I get the same error, relation not found.
The beauty of SQL is that many requirements (yours included) can be verbosely described in very similar way they are finally coded:
with t(id, sort, value ) as (values
(1, 1, 1),
(2, 1, 8),
(3, 2, 0),
(4, 1, 2)
)
select t.*
, first_value(value) over (partition by sort order by id) as "new"
from t
order by id
id
sort
value
new
1
1
1
1
2
1
8
1
3
2
0
0
4
1
2
1
fiddle

Generate a sequence in SQL based on the column values

I need to generate a sequence in SQL Server 2016 database based on the following logic.
I have three fields each represents ID of Brand, Category and the Product. A brand could have multiple categories and each category could have multiple Products.
I want to generate a Sequence based on the values in these 3 fields
BrandNum
CategoryNum
ProductID
1
1
1
1
2
1
1
1
2
1
2
2
1
3
1
1
4
1
2
1
1
2
1
2
2
1
3
2
1
4
2
1
10
2
1
20
10
10
10
11
9
2
2
10
1
2
1
200
For example if Brand Number is 1, category is 1 and ItemID is 1 then I want 1100001. The first 1 from left represents the Brand Number, second number from left represents the category number and right most number 1 is the item number.
So for other example if the brand number is 4, category is 5 and ItemID is 100 then I need to generate 4500100.
it is working fine with the following logic (there could be a better way of writing it).
Select BrandNum*1000000+CategoryNum*100000+ItemID From Table
It works fine but this logic fails when the brand number is 10, category number is 10 (and any itemID (let's say 120)
The above code gets 1100120 but what I want is 101000120 (first 10 is brand number, second 10 is category number, and next 5 digits is for Item)
Could you please advice me what logic I should use to achieve my output?
It seems like what you ultimately want is a NVARCHAR with segments dedicated to your categories, for the first row:
010100001 because brand is 1 (01) category is 1 (01) and product is 1 (00001).
To get this done you could just cast to NVARCHAR and then pad the resulting string:
DECLARE #Product TABLE (Brand INT, Category INT, Product INT)
INSERT INTO #Product (Brand, Category, Product) VALUES
(1 , 1 , 1 ), (1 , 2 , 1 ), (1 , 1 , 2 ), (1 , 2 , 2 ),
(1 , 3 , 1 ), (1 , 4 , 1 ), (2 , 1 , 1 ), (2 , 1 , 2 ),
(2 , 1 , 3 ), (2 , 1 , 4 ), (2 , 1 , 10 ), (2 , 1 , 20 ),
(10, 10, 10 ), (11, 9 , 2 ), (2 , 10, 1 ), (2 , 1 , 200)
SELECT *, RIGHT('00'+CAST(Brand AS NVARCHAR(2)),2)+RIGHT('00'+CAST(Category AS NVARCHAR(2)),2)+RIGHT('00000'+CAST(Product AS NVARCHAR(5)),5) AS SKU
FROM #Product
Brand Category Product SKU
-------------------------------
1 1 1 010100001
1 2 1 010200001
1 1 2 010100002
1 2 2 010200002
1 3 1 010300001
1 4 1 010400001
2 1 1 020100001
2 1 2 020100002
2 1 3 020100003
2 1 4 020100004
2 1 10 020100010
2 1 20 020100020
10 10 10 101000010
11 9 2 110900002
2 10 1 021000001
2 1 200 020100200
You may use CONCAT and FORMAT functions as the following:
select BrandNum, CategoryNum, ProductID,
concat(BrandNum, CategoryNum, format(ProductID,'00000')) sequence
from table_name
See a demo.

SQL Query to get multiple resultant on single column

I have a table that looks something like this:
id name status
2 a 1
2 a 2
2 a 3
2 a 2
2 a 1
3 b 2
3 b 1
3 b 2
3 b 1
and the resultant i want is:
id name total count count(status3) count(status2) count(status1)
2 a 5 1 2 2
3 b 4 0 2 2
please help me get this result somehow, i can just get id, name or one of them at a time, don't know how to put a clause to get this table at once.
Here's a simple solution using group by and case when.
select id
,count(*) as 'total count'
,count(case status when 3 then 1 end) as 'count(status1)'
,count(case status when 2 then 1 end) as 'count(status3)'
,count(case status when 1 then 1 end) as 'count(status2)'
from t
group by id
id
total count
count(status3)
count(status2)
count(status1)
2
5
1
2
2
3
4
0
2
2
Fiddle
Here's a way to solve it using pivot.
select *
from (select status,id, count(*) over (partition by id) as "total count" from t) tmp
pivot (count(status) for status in ([1],[2],[3])) pvt
d
total count
1
2
3
3
4
2
2
0
2
5
2
2
1
Fiddle

Generate a serial number based on quantity column in sql

Hi Experts I have a table like this
T1
Order_no
Qty
1
3
2
5
3
1
4
3
I need to generate a column 'serial no' having values based on 'qty'
Output needed
OrderNo
Qty
SerailNo
1
3
1
1
3
2
1
3
3
2
5
1
2
5
2
2
5
3
2
5
4
2
5
5
3
1
1
4
3
1
4
3
2
4
3
3
Any suggestions?
Thanks in advance!!
You don't mention the specific database so I'll assume you are using PostgreSQL, aren't you?
You can use a Recursive CTE to expand the rows. For example:
with recursive
n as (
select order_no, qty, 1 as serial_no from t1
union all
select order_no, qty, serial_no + 1
from n
where serial_no < qty
)
select * from n order by order_no, serial_no
Result:
order_no qty serial_no
--------- ---- ---------
1 3 1
1 3 2
1 3 3
2 5 1
2 5 2
2 5 3
2 5 4
2 5 5
3 1 1
4 3 1
4 3 2
4 3 3
See running example at DB Fiddle.
EDIT FOR ORACLE
If you are using Oracle the query changes a bit to:
with
n (order_no, qty, serial_no) as (
select order_no, qty, 1 from t1
union all
select order_no, qty, serial_no + 1
from n
where serial_no < qty
)
select * from n order by order_no, serial_no
Result:
ORDER_NO QTY SERIAL_NO
--------- ---- ---------
1 3 1
1 3 2
1 3 3
2 5 1
2 5 2
2 5 3
2 5 4
2 5 5
3 1 1
4 3 1
4 3 2
4 3 3
See running example at db<>fiddle.
You should first provide the database you're using. Whether it's oracle, Sql Server, PostGreSQL will determine which procedural language to use. It's very likely that you'll need to do this in two steps:
1st: Duplicate the number of rows based on the column Qty using a decreasing loop
2nd: You'll need to create a sequential partionned column based on the Qty column

Select Query on Sql Server

The question may be very simple but i don't know how to fix it,
I have this table structure
sno left Right
1 2 1
2 2 2
3 1 2
4 3 1
5 2 4
6 7 1
7 2 8
How do I get a result set like the one below
sno left Right Result
1 2 1 1
2 2 2 2
3 1 2 1
4 3 1 1
5 2 4 2
6 7 1 1
7 2 8 2
I wanna select the Data what mimimum value is matched between two columns,
Eg:3 and 1
1 is minimum value between these two and 1 is matched with 3, so the matched value is 1.
eg: 2 and 4
2 is minimum value between these two and 2 is is mathed with 4, so the matched value is 2.
Edited:
If choose 8 and 2 for example
8 contains(1,2,3,4,5,6,7,8)
2 contains(1,2)
So the Result is 2
Because 2 values are matched here.
I hope i explained it well, thanks
The following SQL will return the positive value of a subtraction operation between the left and right values - in a column with Result as the header. It will calculate the difference between left and right values - ABS will make the result positive.
SELECT
sno,
left,
Right,
ABS(left - right) AS Result
FROM tablename
One of the possible solutions:
DECLARE #t TABLE ( sno INT, l INT, r INT )
INSERT INTO #t
VALUES ( 1, 2, 1 ),
( 2, 2, 2 ),
( 3, 1, 2 ),
( 4, 3, 1 ),
( 5, 2, 4 ),
( 6, 7, 1 ),
( 7, 2, 8 )
SELECT *,
(SELECT MIN(v) FROM (VALUES(l),(r)) m(v)) AS m
FROM #t
Output:
sno l r m
1 2 1 1
2 2 2 2
3 1 2 1
4 3 1 1
5 2 4 2
6 7 1 1
7 2 8 2
case
when left < right then left
else right
end