Duplicate row based on other columns values - sql-server

I am extracting orders from a table, each order has a status and a lead time.
An order passes from the status in the exact order as following:
'placed' > 'confirmed' > 'shipped'
The rows looks like
id_order dsc_status lead
1 placed 8
1 confirmed 5
1 shipped 1
I need to return:
id_order dsc_status lead iter
1 placed 8 0
1 placed 8 1
1 placed 8 2
1 placed 8 3
1 confirmed 5 0
1 confirmed 5 1
1 confirmed 5 2
1 confirmed 5 3
1 confirmed 5 4
1 shipped 1 0
1 shipped 1 1
Logic:
I the example the lead time difference between placed and confirmed is 3 so I repeat the placed row 4 times (0 based counting), same for confirmed > shipped.
For shipped we repeat as if followed by a fictive status having lead = 0 which means we repeat 2 times, please check the results.

You can use CURSOR to get your desired output-
DECLARE #id_order INT
DECLARE #id_Status_order INT
DECLARE #dsc_statue VARCHAR(100)
DECLARE #lead INT
DECLARE #LoopCount INT
DECLARE #TmpTable TABLE
(
id_order INT, dsc_status VARCHAR(200), lead INT, iter INT
)
DECLARE #id_order_prev INT
DECLARE #dsc_statue_prev VARCHAR(100)
DECLARE #lead_prev INT
DECLARE db_cursor CURSOR FOR
SELECT id_order,Status_Order,dsc_status,lead
FROM
(
SELECT id_order,dsc_status,lead,
CASE
WHEN dsc_status = 'placed' THEN 1
WHEN dsc_status = 'confirmed' THEN 2
WHEN dsc_status = 'shipped' THEN 3
END Status_Order
FROM your_table
)A
ORDER BY 1,2
OPEN db_cursor
FETCH NEXT FROM db_cursor INTO #id_order,#id_Status_order ,#dsc_statue,#lead
WHILE ##FETCH_STATUS = 0
BEGIN
IF #id_order_prev IS NULL
BEGIN
SET #id_order_prev = #id_order
SET #dsc_statue_prev = #dsc_statue
SET #lead_prev = #lead
END
ELSE
BEGIN
SET #LoopCount = 0
WHILE #LoopCount <= CASE WHEN #id_order_prev = #id_order THEN ABS(#lead_prev-#lead) ELSE #lead_prev END
BEGIN
INSERT INTO #TmpTable (id_order,dsc_status,lead,iter)
VALUES (#id_order_prev,#dsc_statue_prev,#lead_prev,#LoopCount)
SET #LoopCount = #LoopCount + 1
END
SET #id_order_prev = #id_order
SET #dsc_statue_prev = #dsc_statue
SET #lead_prev = #lead
END
FETCH NEXT FROM db_cursor INTO #id_order,#id_Status_order ,#dsc_statue,#lead
IF ##FETCH_STATUS <> 0
BEGIN
SET #LoopCount = 0
WHILE #LoopCount <= #lead
BEGIN
INSERT INTO #TmpTable (id_order,dsc_status,lead,iter)
VALUES (#id_order_prev,#dsc_statue_prev,#lead_prev,#LoopCount)
SET #LoopCount = #LoopCount + 1
END
END
END
CLOSE db_cursor
DEALLOCATE db_cursor
SELECT *
FROM #TmpTable
ORDER BY 1

Related

Condition insert query in while loop in sql server

I want to insert count values in table with conditions using while loop.
I want to result like
EmpID
EMpNo
IsActive
IsScience
1
1
1
1
2
2
1
1
3
3
1
1
.
.
.
.
27
27
1
0
28
28
1
0
.
.
.
.
565
565
1
0
BEGIN
DECLARE #i int = 0
WHILE #i < 565
BEGIN
SET #i = #i + 1
while #i < 26
BEGIN
INSERT INTO [dbo].[T_Emp] ([EmpID],[EMpNo],[IsActive],[IsScience])
VALUES(#i,#i,1,1);
END
while #i > 26 AND #i <565
BEGIN
INSERT INTO [dbo].[T_Emp] ([EmpID],[EMpNo],[IsActive],[IsScience])
VALUES(#i,#i,1,0);
END
END
END
As you can see I want to change IsScience after #i > 26 .
Also My #i isn't incremented. What I am doing wrong with looping.
My loop executing executing wrongly.
remove multi time while loop in your code use this code
BEGIN
DECLARE #i int = 0
WHILE #i < 565
BEGIN
SET #i = #i + 1
IF(#i < 26)
BEGIN
INSERT INTO [dbo].[T_Emp]
([EmpID],[EMpNo],[IsActive],[IsScience])
VALUES(#i,#i,1,1);
END
IF(#i > 26 AND #i <565)
BEGIN
INSERT INTO [dbo].[T_Emp]
([EmpID],[EMpNo],[IsActive],[IsScience])
VALUES(#i,#i,1,0);
END
END
END

Remove duplicate detail transaction (header detail store procedure)

I have problem when creating header detail transaction in store procedure. Assume 1 header contain 3 details transaction. If I want to get invoice I must join some table for getting the formula.
USE [M_TENANT]
GO
/****** Object: StoredProcedure [dbo].[generate_billingv2] Script Date: 07/02/2020 09:32:42 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[generate_billingv2]
AS
--exec generate_billingv2
SET NOCOUNT ON;
BEGIN TRANSACTION
DECLARE #genbill_nochar INT
,#OPT_UTILITY_NOCHAR NVARCHAR(50)
,#NAME_CHAR VARCHAR(80)
,#opt_utility_detail NVARCHAR(50)
,#MD_BILLING_ID_INT INT
,#BILLING_ID_UTILITY INT
,#OPT_UTILITY_AMT NUMERIC
,#BILLING_ID_FORMULA INT
,#FORMULA_PRICE NUMERIC
,#FORMULA_VA NUMERIC
,#FORMULA_NAME VARCHAR(50)
,#MD_FORMULA_PERCENTAGE DECIMAL(18, 3)
,#bill_TYPE INT
,#BASE_INVOICE_AMT NUMERIC
,#OPT_UTILITY_USED NUMERIC
,#ABODEMEN NUMERIC
,#UNIT_LB DECIMAL(18, 3)
,#TAX_UTILITY NUMERIC(18, 3)
,#TOTAL_UTILITY NUMERIC(18, 3)
,#OPT_GENBILL_NOCHAR VARCHAR(50)
,#NOUNIT_CHAR CHAR(20)
,#OPT_GENBILL_STATUS_INT INT
,#OPT_GENBILL_TRX_DATE DATE
,#OPT_UTILITY_TRX_DATE DATE
,#OPT_TRANS_ID_INT INT
,#OPT_GENERATE_BY VARCHAR(50)
,#MD_FORMULA_PRICE NUMERIC
,#USAGE DECIMAL(18, 2)
,#OPT_FLOOR CHAR(10)
,#MD_FORMULA_NAME VARCHAR(50)
,#OPT_START NUMERIC
,#OPT_BILL_END NUMERIC
,#OPT_START_DATE DATE
,#OPT_END_DATE DATE
,#FORMULA_PERCENTAGE DECIMAL(18, 2)
SET #BASE_INVOICE_AMT = 0
SET #TAX_UTILITY = 0
SET #TOTAL_UTILITY = 0
SET #OPT_GENBILL_STATUS_INT = 0
SET #OPT_GENBILL_TRX_DATE = GETDATE()
SET #OPT_GENERATE_BY = 'AUTO'
DECLARE bill_header CURSOR
FOR
SELECT a.OPT_UTILITY_NOCHAR
,a.NOUNIT_CHAR
,b.UNIT_LB
,b.NAME_CHAR
,b.OPT_FLOOR
,a.OPT_TRANS_ID_INT
,a.OPT_UTILITY_TRX_DATE
,0 AS BASE_INVOICE
,0 AS TAX_UTILITY
,0 AS TOTAL_ULTILITY
FROM OPT_UTILITY a
INNER JOIN OPT_TRANS b ON a.OPT_TRANS_ID_INT = b.OPT_TRANS_ID_INT
WHERE a.OPT_GENBILL_STATUS_INT = 0
OPEN bill_header
FETCH NEXT
FROM bill_header
INTO #OPT_UTILITY_NOCHAR
,#NOUNIT_CHAR
,#UNIT_LB
,#NAME_CHAR
,#OPT_FLOOR
,#OPT_TRANS_ID_INT
,#OPT_UTILITY_TRX_DATE
,#BASE_INVOICE_AMT
,#TAX_UTILITY
,#TOTAL_UTILITY
WHILE ##FETCH_STATUS = 0
BEGIN
--HEADER TRANSACTION
--PRINT 'ini header'+'-'+ #OPT_UTILITY_NOCHAR
SET #OPT_GENBILL_NOCHAR = convert(VARCHAR, (
SELECT 'KALINV' + convert(VARCHAR(4), YEAR(GETDATE())) + REPLICATE('0', 5 - LEN(RTRIM(invoiced_count))) + RTRIM(invoiced_count)
FROM counter_table
));
INSERT INTO OPT_GENBILL (
OPT_GENBILL_NOCHAR
,OPT_UTILITY_NOCHAR
,NOUNIT_CHAR
,NAME_CHAR
,LB
,BASE_UTILITY
,TAX_UTILITY
,TOTAL_UTILITY
,OPT_GENBILL_STATUS_INT
,OPT_GENBILL_TRX_DATE
,OPT_UTILITY_TRX_DATE
,OPT_TRANS_ID_INT
,OPT_GENERATE_BY
)
VALUES (
#OPT_GENBILL_NOCHAR
,#OPT_UTILITY_NOCHAR
,#NOUNIT_CHAR
,#NAME_CHAR
,#UNIT_LB
,#BASE_INVOICE_AMT
,#TAX_UTILITY
,#TOTAL_UTILITY
,#OPT_GENBILL_STATUS_INT
,#OPT_GENBILL_TRX_DATE
,#OPT_UTILITY_TRX_DATE
,#OPT_TRANS_ID_INT
,#OPT_GENERATE_BY
)
DECLARE bill_detail CURSOR
FOR
SELECT C.MD_BILLING_ID_INT AS BILL_TYPE
,a.OPT_UTILITY_NOCHAR
,a.MD_BILLING_ID_INT
,b.MD_FORMULA_PRICE
,b.MD_FORMULA_NAME
,b.MD_FORMULA_PERCENTAGE
,b.MD_FORMULA_PJU_ABD AS ABODEMEN
,b.MD_FORMULA_VA AS FORMULA_VA
,a.OPT_BILL_END - a.OPT_UTILITY_AMT AS OPT_START
,a.OPT_BILL_END
,a.OPT_UTILITY_AMT AS USAGE
,a.OPT_START_DATE
,a.OPT_END_DATE
FROM OPT_UTILITY_DETAIL a
INNER JOIN MD_FORMULA b ON a.MD_BILLING_ID_INT = b.MD_FORMULA_ID_INT
INNER JOIN MD_BILLING_TYPE c ON b.MD_BILLING_ID_INT = c.MD_BILLING_ID_INT
WHERE a.OPT_UTILITY_NOCHAR = #OPT_UTILITY_NOCHAR
OPEN bill_detail
FETCH NEXT
FROM bill_detail
INTO #BILL_TYPE
,#OPT_UTILITY_NOCHAR
,#MD_BILLING_ID_INT
,#MD_FORMULA_PRICE
,#MD_FORMULA_NAME
,#MD_FORMULA_PERCENTAGE
,#ABODEMEN
,#FORMULA_VA
,#OPT_START
,#OPT_BILL_END
,#USAGE
,#OPT_START_DATE
,#OPT_END_DATE
--IF exists(select TOP 1* from OPT_GENBILL where OPT_UTILITY_NOCHAR=#OPT_UTILITY_NOCHAR)
--BEGIN
-- ROLLBACK TRANSACTION
-- DEALLOCATE bill_detail
-- RAISERROR('invoice sudah pernah generate ',16,-1,#OPT_UTILITY_NOCHAR)
-- RETURN
--END
IF ##FETCH_STATUS <> 0
PRINT ' <<None>>'
WHILE ##FETCH_STATUS = 0
BEGIN
--SET #BASE_INVOICE_AMT = CASE WHEN #BILL_TYPE=3 AND #USAGE<=40 THEN
-- ((#ABODEMEN*(#FORMULA_VA/1000)*#FORMULA_PRICE)+ ((#ABODEMEN*(#FORMULA_VA/1000)*#FORMULA_PRICE)*#FORMULA_PERCENTAGE))
-- when #bill_TYPE=3 and #USAGE>40 then
-- ((#USAGE*(#FORMULA_VA/1000)*#FORMULA_PRICE)+ ((#USAGE*(#FORMULA_VA/1000)*#FORMULA_PRICE)*#FORMULA_PERCENTAGE))
-- when #bill_TYPE=2 then ((#USAGE*#FORMULA_PRICE)+#ABODEMEN)
-- when #bill_TYPE=1 then #UNIT_LB*#FORMULA_PRICE
-- else 0 end
--print #OPT_UTILITY_NOCHAR
-- print #BILL_TYPE
-- print #USAGE
-- print #ABODEMEN
-- print #FORMULA_PRICE
-- print #FORMULA_VA
-- print #FORMULA_PERCENTAGE
INSERT INTO OPT_GENBILL_DETAIL (
OPT_UTILITY_NOCHAR
,NOUNIT_CHAR
,OPT_START_DATE
,OPT_END_DATE
,OPT_BILL_START
,OPT_BILL_END
,OPT_UTILITY_AMT
,OPT_GENBILL_NOCHAR
)
SELECT #OPT_UTILITY_NOCHAR
,#NOUNIT_CHAR
,OPT_START_DATE
,OPT_END_DATE
,OPT_BILL_END - OPT_UTILITY_AMT
,OPT_BILL_END
,CASE
WHEN #BILL_TYPE = 3
AND #USAGE <= 40
THEN ((#ABODEMEN * (#FORMULA_VA / 1000) * #FORMULA_PRICE) + ((#ABODEMEN * (#FORMULA_VA / 1000) * #FORMULA_PRICE) * #FORMULA_PERCENTAGE))
WHEN #bill_TYPE = 3
AND #USAGE > 40
THEN ((#USAGE * (#FORMULA_VA / 1000) * #FORMULA_PRICE) + ((#USAGE * (#FORMULA_VA / 1000) * #FORMULA_PRICE) * #FORMULA_PERCENTAGE))
WHEN #bill_TYPE = 2
THEN ((#USAGE * #FORMULA_PRICE) + #ABODEMEN)
ELSE 0
END
,#OPT_GENBILL_NOCHAR
FROM OPT_UTILITY_DETAIL
WHERE OPT_UTILITY_NOCHAR = #OPT_UTILITY_NOCHAR
FETCH NEXT
FROM bill_detail
INTO #BILL_TYPE
,#OPT_UTILITY_NOCHAR
,#MD_BILLING_ID_INT
,#MD_FORMULA_PRICE
,#MD_FORMULA_NAME
,#MD_FORMULA_PERCENTAGE
,#ABODEMEN
,#FORMULA_VA
,#OPT_START
,#OPT_BILL_END
,#USAGE
,#OPT_START_DATE
,#OPT_END_DATE
END
CLOSE bill_detail
DEALLOCATE bill_detail
UPDATE counter_table
SET invoiced_count = invoiced_count + 1
UPDATE OPT_UTILITY
SET OPT_GENBILL_STATUS_INT = 1
WHERE OPT_UTILITY_NOCHAR = #OPT_UTILITY_NOCHAR
-- Get the next vendor.
FETCH NEXT
FROM bill_header
INTO #OPT_UTILITY_NOCHAR
,#NOUNIT_CHAR
,#UNIT_LB
,#NAME_CHAR
,#OPT_FLOOR
,#OPT_TRANS_ID_INT
,#OPT_UTILITY_TRX_DATE
,#BASE_INVOICE_AMT
,#TAX_UTILITY
,#TOTAL_UTILITY
END
CLOSE bill_header;
DEALLOCATE bill_header;
COMMIT TRANSACTION
RETURN
But the result 3 detail generating 9 details rows.

Adding Rows from one table to Another Table based on days of month SQL

I have a table named Students having 10 records of students
ID StudentName
1 Student a
2 Student b
- ------ -
10 Student N
now i want to add these 10 students to another table based on days of month e.g
ID StudentName DayOfMonth
1 Student a 1
2 Student a 2
- --------- -
- Student a 31
- Student b 1
- ------- b 31
And for all the students is there any SQL dynamic Solution
I tried using Cursor but it takes approx 2 minutes if there are 55 Students in a table. While I checked the table during execution of proc it generate 1705 rows i.e (55x31) in a matter of seconds but than it reacts as it is hanged or something and after 2 minutes it shows the success message.
any help will be greatly appreciated.
#fkStudentID int,
#fkClassID int,
#fkSessionID int,
#Dated date,
AS
Declare #Days as int
Set #Days = DAY(DATEADD(DD,-1,DATEADD(MM,DATEDIFF(MM,-1,#Dated),0)))
Declare #OffSet as int
DECLARE #MyCursor CURSOR;
DECLARE #MyField int;
BEGIN
SET #MyCursor = CURSOR FOR
select fkStudentID from dbo.tblAdmission
where fkClassID = #fkClassID and fkSessionID = #fkSessionID
OPEN #MyCursor
FETCH NEXT FROM #MyCursor
INTO #MyField
WHILE ##FETCH_STATUS = 0
BEGIN
While(#OffSet <= #Days)
Begin
if(IsNull((Select count(RegisterID) from tblRegister where #MyField = fkStudentID and fkClassID = #fkClassID and fkSessionID = #fkSessionID and [Dayofmonth] = #OffSet),0) = 0)
Begin
Insert into tblRegister (fkStudentID, fkClassID, fkSessionID, [DayOfMonth], Dated) values (#MyField, #fkClassID, #fkSessionID, #OffSet, DATEADD(DAY, (#OffSet - 1), DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()), 0)))
End
Set #OffSet = #OffSet + 1
End
Set #OffSet = 1
FETCH NEXT FROM #MyCursor
INTO #MyField
END;
CLOSE #MyCursor ;
DEALLOCATE #MyCursor;
END;
I'm not sure why you see a delay before the proc ends but it is best to use a set-based query instead of cursor whenever possible for best performance. I expect the example below will run in less than a second as long as you have appropriate indexes (ideally, a unique clustered index on dbo.tblAdmission fkClassID and fkSessionID columns and a unique index on dbo.tblRegister fkStudentID, fkClassID, fkSessionID, and DayOfMonth).
CREATE PROC dbo.Example
#fkClassID int,
#fkSessionID int,
#Dated date
AS
INSERT INTO dbo.tblRegister
(
fkStudentID
, fkClassID
, fkSessionID
, DayOfMonth
, Dated
)
SELECT
a.fkStudentID
, a.fkClassID
, a.fkSessionID
, o.offset
, DATEADD(DAY, (o.offset - 1), DATEADD(MONTH, DATEDIFF(MONTH, '', GETDATE()), ''))
FROM dbo.tblAdmission AS a
CROSS JOIN (VALUES(1),(2),(3),(4),(5),(6),(7),(8),(9),(10)
,(11),(12),(13),(14),(15),(16),(17),(18),(19),(20)
,(21),(22),(23),(24),(25),(26),(27),(28),(29),(30),(31)) AS o(offset)
WHERE
a.fkClassID = #fkClassID
AND a.fkSessionID = #fkSessionID
AND o.offset <= DAY(DATEADD(DD,-1,DATEADD(MM,DATEDIFF(MM,-1,#Dated),0)))
AND NOT EXISTS(
SELECT 1
FROM dbo.tblRegister AS r
WHERE
r.fkStudentID = a.fkStudentID
AND r.fkClassID = a.fkClassID
AND r.fkSessionID = a.fkSessionID
AND [Dayofmonth] = o.offset
);

SQL Server order by part of string

One of my tables have values like this..
Year 1
Year 9
Year 4
Kindy [can be any word without numbers]
Pre-School [can be any word without numbers]
Year 8
Year 22
Year 15....
How can I select Them in alphabetically first and then by numerically in ascending order like this..
Kindy [can be any word without numbers]
Pre-School [can be any word without numbers]
Year 1
Year 4
Year 8
Year 9
Year 15
Year 22
I could not extract the integer and order in this case as some of the years don't have it..
UPDATE
MY ANSWER -- MISSING DISTINCT
SELECT YearLevel FROM Student
ORDER BY
CASE WHEN YearLevel NOT LIKE '%[0-9]%' THEN 0
ELSE CAST(RIGHT(YearLevel, LEN(YearLevel) - 5) AS int)
END
Try this in the ORDER BY clause:
ORDER BY
CASE WHEN col = 'Kindy' then 0
WHEN col = 'Pre-School' then 1
ELSE CAST(SUBSTRING(col,6,LEN(col)) AS INT) + 1
END
In first you must create following function for get text part and number part of your string :
CREATE FUNCTION [dbo].[GetNumbersFromText](#String varchar(2000))
RETURNS INT
AS BEGIN
DECLARE #Count INT = 0,
#IntNumbers VARCHAR(1000) = '',
#FindNumber BIT = 0
WHILE #Count <= LEN(#String) BEGIN
IF SUBSTRING(#String,#Count,1) >= '0' AND SUBSTRING(#String,#Count,1) <= '9' BEGIN
SET #IntNumbers = #IntNumbers + SUBSTRING(#String,#Count,1)
SET #FindNumber = 1
END ELSE IF (#FindNumber = 1) BEGIN
BREAK
END
SET #Count = #Count + 1
END
RETURN CAST(#IntNumbers AS INT)
END
CREATE FUNCTION [dbo].[GetTextPartOfText](#String varchar(2000))
RETURNS INT
AS BEGIN
DECLARE #Count INT = 0,
#Text VARCHAR(1000) = '',
WHILE #Count <= LEN(#String) BEGIN
IF SUBSTRING(#String,#Count,1) >= '0' AND SUBSTRING(#String,#Count,1) <= '9' BEGIN
BREAK
END ELSE BEGIN
SET #Text = #Text + SUBSTRING(#String,#Count,1)
END
SET #Count = #Count + 1
END
RETURN #Text
END
In second you use following query for your sort:
SELECT *
FROM YourTable
ORDER BY [dbo].[GetTextPartOfText](TextColumn),[dbo].[GetNumbersFromText](TextColumn), TextColumn
SELECT YearLevel FROM student
GROUP BY YearLevel
ORDER BY
(CASE
WHEN YearLevel LIKE 'Year%'
THEN 'Year' + CONVERT(varchar,LEN(YearLevel)) + YearLevel
ELSE YearLevel
END)

update value based on row number?

i'm pretty new to SQL and stored procedures and i'm a bit stuck - so any help would be appreciated
how do i loop through each row and assign it the random value i'm generating?
Here is my Storedproc:
CREATE PROCEDURE StoredProc8
AS
BEGIN
DECLARE #total INT
DECLARE #Count INT = 0
DECLARE #Random INT = 0
SELECT #total = COUNT(CustomerID) FROM Customers
WHILE(#Count<= #total)
BEGIN
SELECT #Random = 2 * RAND()
EXEC ('update Customers set col1= ' + #Random )
SELECT #Count = #Count+1
END
END
If you simple need to assign 0 or 1 randomly - you can use RAND() with random seed:
UPDATE Customers SET COL1 = RAND(CHECKSUM(NEWID()))*2
Demo: http://sqlfiddle.com/#!3/31699/9

Resources