Generate a sequence in SQL based on the column values - sql

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.

Related

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

Custom aliases for all fields with GROUP BY ROLLUP

I have such tables:
Group - combination of TypeId and ZoneId
ID TypeID ZoneID
-- -- --
1 1 1
2 1 2
3 2 1
4 2 2
5 2 3
6 3 3
Object
ID GroupId
-- --
1 1
2 1
3 2
4 3
5 3
6 3
I want to build a query for grouping all these tables by TypeId and ZoneId, with number of objects which have specific combination of these field:
ResultTable
TypeId ZoneId Number of objects
-- -- --
1 1 2
1 2 1
2 1 3
2 2 1
2 3 0
3 3 0
Query for this:
SELECT
group.TypeId,
group.ZoneId,
COUNT(obj.ID) as NumberOfObjects
FROM[Group] group
JOIN[Object] obj on obj.GroupID = group.ID
GROUP BY group.TypeId, group.ZoneId ORDER BY group.TypeId
But! I want to add summarize row after each group, and make it like:
ResultTableWithSummary
TypeId ZoneId Number of objects
-- -- --
1 1 2
1 2 1
Summary (empty field) 3
2 1 3
2 2 1
2 3 0
Summary (empty field) 4
3 3 0
Summary (empty field) 0
The problem is that I can use GROUP BY ROLLUP(group.TypeId, group.ZoneId):
TypeId ZoneId Number of objects
-- -- --
1 1 2
1 2 1
1 null 3
2 1 3
2 2 1
2 3 0
2 null 4
3 3 0
3 null 0
but I cannot or don't know how to change not-null group.TypeId in summary rows with "Summary".
How can I do this?
The simplest method is coalesce(), but you need to be sure the types match:
SELECT COALESCE(CONVERT(VARCHAR(255), group.TypeId, 'Summary') as TypeId,
. . .
This is not the most general method, because it does not handle real NULL values in the GROUP BY keys. That doesn't seem to be an issue in this case. If it were, you could use a CASE expression with GROUPING().
EDIT:
For your particular variant (which I find strange), you can use:
SELECT (CASE WHEN group.TypeId IS NULL OR group.ZoneID IS NULL
THEN 'Summary' ELSE CONVERT(VARCHAR(255), group.TypeId)
END) as TypeId,
. . .
In practice, I would use something similar to the COALESCE() in both columns, so I don't lose the information on what the summary is for.

How to Subtotal Value with MAX

1.I have data as follows (just a subset - there are 20K records)
sku,id
1 1
1 2
1 2
1 2
1 3
1 4
1 1
1 2
1 3
1 4
1 4
1 4
1 5
1 6
1 6
2 1
2 1
2 2
2 3
2 3
2 3
2 4
2 4
2 5
2 5
2 6
2 7
2 1
2 2
2 3
The above values translate to
1 = 4 records
1 = 6 records
2 = 7 records
2 = 3 records
The MAX would just give me 6 for one and 7 for 2
The actual total is 1 = 10 and 2 = 10
How do I sum up to get the correct values?
You can use order by and some way of limiting rows. In standard SQL this would be:
select t.*
from t
order by id desc
fetch first 2 rows only;
However, some databases might use limit or select top or some other method.
No handling of ties here. Thousands of other questions handle this topic.
select sku, id
from (
select *, row_number() over (order by id desc) rn
from T
) t
where rn <= 2
order by rn desc;

Using UNPIVOT and column names for field values

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)

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