SQL - How to Multiply Prior Row value to Current Row - sql

Can this be done and if so what is the best approach. I tried Over(Partition) but cannot figure out how to add the prior columns. Should I use IF BEGIN ENDS and just walk through it row by row for running balance? Any assistance on initial direction would be appreciated.
CREATE TABLE [dbo].[PStatement](
[ID] [int],
[Source] [nvarchar](255),
[PaymentNumber] [int],
[PaymentAmt] [money],
[RBalance] [money],
[CBalance] [money],
[AllocationPercent] [float]
)
With the following values:
INSERT INTO PStatement (ID, [Source], PaymentNumber, PaymentAmt, RBalance, CBalance, AllocationPercent)
VALUES (1, N'SR', 1, 0, 7500, 10000, .75)
,(2, N'ER', 1, 0, 2500, 10000, .25)
,(3, N'SR', 2, 50, 0, 11000, 0)
,(4, N'ER', 2, 75, 0, 11000, .0)
,(5, N'SR', 3, 50, 0, 12000, 0)
,(6, N'ER', 3, 75, 0, 12000, .0)
I need to take the CBalance from the prior row, subtract the sum of the current rows PaymentAmt and multiple that amount by the prior rows AllocationPercentage and then add back the PaymentAmt based on PaymentNumber and Source.
So Row 3 RBalance is $11000 - ($50 + $75) (from current row) * .75 (from prior row) + 50 (from current row), AllocationPercent is RBalance/CBalance.
So Row 4 is $11000 - ($50 + $75) * .25 + 75
The end table would look like this:

You can access 'prior row' data by using something like:
select ID
, PaymentAmt
, PreviousPaymentAmt=LAG(PaymentAmt,1,0) OVER (ORDER BY ID)
from PStatement
The 2nd parameter in LAG is telling it to go only 1 record back; and the third parameter is telling it to return 0 (zero) if there are no prior rows.

Here is how I got it to work.
DECLARE #MyCursor CURSOR;
DECLARE #id INT, #Source NVARCHAR(255), #CBalance MONEY, #RBalance MONEY, #PaymentAmt MONEY, #AllocationPercent decimal(18,10), #PaymentNumber INT, #Amount MONEY;
BEGIN
SET #MyCursor = CURSOR FOR
SELECT ID FROM PStatement ORDER BY Source, PaymentNumber
OPEN #MyCursor
FETCH NEXT FROM #MyCursor
INTO #id
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #Source = Source, #PaymentNumber = PaymentNumber, #CBalance = CBalance, #PaymentAmt = PaymentAmt FROM PStatement WHERE ID = #id
IF(#PaymentNumber = 1)
BEGIN
UPDATE PStatement SET AllocationPercent = RBalance/ CAST(CBalance AS decimal(18, 10)) WHERE (ID = #id)
SET #AllocationPercent = (SELECT AllocationPercent FROM PStatement WHERE (ID = #id))
END
ELSE
BEGIN
SET #PaymentAmt = (SELECT SUM(PaymentAmt) FROM PStatement WHERE (PaymentNumber = #PaymentNumber))
SET #RBalance = ((#CBalance - #PaymentAmt) * #AllocationPercent) + #PaymentAmt
UPDATE PStatement SET RBalance = #RBalance WHERE (ID = #id)
UPDATE PStatement SET AllocationPercent = RBalance/ CAST(CBalance AS decimal(18, 10)) WHERE (ID = #id)
SET #AllocationPercent = (SELECT AllocationPercent FROM PStatement WHERE (ID = #id))
END
FETCH NEXT FROM #MyCursor
INTO #id
END;
CLOSE #MyCursor ;
DEALLOCATE #MyCursor;
END;

Related

Subquery returned more than 1 value. Only splitting my query works to fix this

Good afternoon everyone,
Towards the end of my query I start sending PVP Mail Rewards to my players for the game I'm hosting. The issue is that all the queries work successfully until the very last one.
I think this error is generated after the functions I am calling are returning a value causing the script to go bonkers at the end. I can resolve this by splitting all my PVP Mail rewards in to different queries, but I would really prefer keeping them in one master query with all the other things I'm sending.
How would I go about fixing this? Thanks for your time and patience to read this.
Here is the error
> Msg 512, Level 16, State 1, Server LAPTOP-K2EKS8H0, Procedure , Line 0
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
> [21000] [Microsoft][SQL Server Native Client 11.0][SQL Server]Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression. (512)
> Time: 0.029s
-- Create temporary table to hold data from dbo.Characters and dbo.PVPRanking --
USE DNWorld
IF OBJECT_ID('tempdb..#TempAccountTable') IS NOT NULL
begin
drop table #TempAccountTable
end
Select AccountID, G.AccountName, G.CharacterName, PVPLevel, PVPExp, TotalRank
INTO #TempAccountTable
FROM Characters as G
INNER JOIN (
SELECT *, RANK() OVER (ORDER BY TotalRank) AS rn
FROM PvPRanking
Where totalrank between 1 and 100
) as tmp on tmp.CharacterID = G.CharacterID
;
--------------------------------------------------- Rank 1 PvP QC Distribution ---------------------------------------------------
USE DNMembership
GO
DECLARE #AccountName nvarchar(50), #CompensationAmt INT, #i int = 0, #Rank1Rewards int
SET #Rank1Rewards = 1
-- Set and deliver QC by their total rank.
SET #AccountName = (SELECT AccountName FROM #TempAccountTable where TotalRank = 1)
EXEC dbo.P_AddCashIncome #AccountName, 2, NULL, NULL, #Rank1Rewards
;
--------------------------------------------------- PVP Rank #2 - #10 QC distribution via while loop ---------------------------------------------------
USE DNMembership
GO
DECLARE #AccountName nvarchar(50), #CompensationAmt INT, #i int = 0, #RankRewards int
SET #RankRewards = 2
WHILE (#i <= 10) -- BE AWARE IF EDITING
BEGIN
SET #i = #i + 1
SET #AccountName = (SELECT AccountName FROM #TempAccountTable where TotalRank = 1 + #i) -- BE AWARE IF EDITING
EXEC dbo.P_AddCashIncome #AccountName, 2, NULL, NULL, #RankRewards;
END;
-- PVP Rank #11 - #25 distribution via while loop
USE DNMembership
GO
DECLARE #AccountName nvarchar(50), #CompensationAmt INT, #i int = 0, #RankRewards int
SET #RankRewards = 3
WHILE (#i <= 25)
BEGIN
SET #i = #i + 1
SET #AccountName = (SELECT AccountName FROM #TempAccountTable where TotalRank = 10 + #i)
EXEC dbo.P_AddCashIncome #AccountName, 2, NULL, NULL, #RankRewards;
END;
--------------------------------------------------- PVP Rank #26 - #100 QC distribution via while loop ---------------------------------------------------
USE DNMembership
GO
DECLARE #AccountName nvarchar(50), #CompensationAmt INT, #i int = 0, #RankRewards int
SET #RankRewards = 4
WHILE (#i <= 100)
BEGIN
SET #i = #i + 1
SET #AccountName = (SELECT AccountName FROM #TempAccountTable where TotalRank = 25 + #i)
EXEC dbo.P_AddCashIncome #AccountName, 2, NULL, NULL, #RankRewards;
END;
--------------------------------------------------- PVP Mail Rewards #2 - #10 ---------------------------------------------------
USE DNWorld
GO
SET ANSI_NULLS, QUOTED_IDENTIFIER ON
GO
DECLARE
#nvcSenderName nvarchar(50),
#Subject nvarchar(50),
#Content nvarchar(300),
#CharacterName nvarchar(50),
#CoinAmount bigint,
#ItemID1 bigint ,
#ItemID2 bigint ,
#insItemCount1 smallint,
#insItemCount2 smallint,
#LevelItem1 bigint,
#LevelItem2 bigint,
#MailTabCode tinyint,
#i int = 0
WHILE (#i <= 10) -- BE AWARE IF EDITING
BEGIN
SET #i = #i + 1
-- FROM
SET #nvcSenderName = 'PVP Logistics Bot'
-- SUBJECT
SET #Subject = 'Top 10 Player Killers'
-- MESSAGE
SET #Content = 'Outstanding job on maintaining your status as one of the top 10 players throughout Skitzovania.'
-- TARGET CHAR and Loop to desired rank #
SET #CharacterName = (SELECT CharacterName FROM #TempAccountTable where TotalRank = 1 + #i)
-- COPPER
SET #CoinAmount = '0'
-- ITEM ID
SET #ItemID1 = '374341682' -- Goddess Medal
SET #ItemID2 = '335833344' -- Warrior's Trophy
-- QUANTITY
SET #insItemCount1 = '1'
SET #insItemCount2 = '2'
-- ENHANCEMENT LEVEL
SET #LevelItem1 = '0'
SET #LevelItem2 = '0'
-- MAIL TAB TYPE
-- 0 All, 1 Character, 2 Contents, 3 Event, 4 System
SET #MailTabCode = 4
DECLARE
#ItemSerial1 bigint
DECLARE
#ItemSerialfix1 bigint
DECLARE
#inbReceiverCharacterID int
BEGIN
SET #inbReceiverCharacterID = (Select CharacterID from DNWorld.dbo.Characters where CharacterName = #CharacterName)
SET #ItemSerial1 = (SELECT max(ItemSerial) as ItemSerial
FROM dnworld.dbo.MaterializedItems)
SET #ItemSerialfix1 = (#ItemSerial1 + 1)
DECLARE
#ItemSerial2 bigint
DECLARE
#ItemSerialfix2 bigint
SET #ItemSerial2 = (SELECT max(ItemSerial) as ItemSerial
FROM dnworld.dbo.MaterializedItems)
SET #ItemSerialfix2 = (#ItemSerial2 + 1)
EXEC DNWorld.dbo.P_SendSystemMail
#nvcSenderName,
#inbReceiverCharacterID,
3,
4,
#Subject,
#Content,
#CoinAmount,
#ItemSerialfix1,
#ItemID1,
#insItemCount1,
24000,
0,
#LevelItem1,
0,
0,
1,
0,
1,
0,
#ItemSerialfix2,
#itemid2,
#intChannelID = 0,
#intMapID = 0,
#intTotalMailCount = null,
#intNotReadMailCount = 1,
#int7DaysLeftMailCount = null,
#intMailID = null,
#inyMailTabCode = #MailTabCode
END
END
GO
--------------------------------------------------- PVP Mail Rewards #11 - #25 ---------------------------------------------------
USE DNWorld
GO
SET ANSI_NULLS, QUOTED_IDENTIFIER ON
GO
DECLARE
#nvcSenderName nvarchar(50),
#Subject nvarchar(50),
#Content nvarchar(300),
#CharacterName nvarchar(50),
#CoinAmount bigint,
#ItemID1 bigint ,
#ItemID2 bigint ,
#insItemCount1 smallint,
#insItemCount2 smallint,
#LevelItem1 bigint,
#LevelItem2 bigint,
#MailTabCode tinyint,
#i int = 0
WHILE (#i <= 25) -- BE AWARE IF EDITING
BEGIN
SET #i = #i + 1
-- FROM
SET #nvcSenderName = 'PVP Logistics Bot'
-- SUBJECT
SET #Subject = 'Top 25 Player Killers'
-- MESSAGE
SET #Content = 'Outstanding job on maintaining your status as one of the top 25 players throughout Skitzovania.'
-- TARGET CHAR and Loop to desired rank #
SET #CharacterName = (SELECT CharacterName FROM #TempAccountTable where TotalRank = 10 + #i)
-- COPPER
SET #CoinAmount = '0'
-- ITEM ID
SET #ItemID1 = '374341682' -- Goddess Medal
SET #ItemID2 = '335833344' -- Warrior's Trophy
-- QUANTITY
SET #insItemCount1 = '1'
SET #insItemCount2 = '2'
-- ENHANCEMENT LEVEL
SET #LevelItem1 = '0'
SET #LevelItem2 = '0'
-- MAIL TAB TYPE
-- 0 All, 1 Character, 2 Contents, 3 Event, 4 System
SET #MailTabCode = 4
DECLARE
#ItemSerial1 bigint
DECLARE
#ItemSerialfix1 bigint
DECLARE
#inbReceiverCharacterID int
BEGIN
SET #inbReceiverCharacterID = (Select CharacterID from DNWorld.dbo.Characters where CharacterName = #CharacterName)
SET #ItemSerial1 = (SELECT max(ItemSerial) as ItemSerial
FROM dnworld.dbo.MaterializedItems)
SET #ItemSerialfix1 = (#ItemSerial1 + 1)
DECLARE
#ItemSerial2 bigint
DECLARE
#ItemSerialfix2 bigint
SET #ItemSerial2 = (SELECT max(ItemSerial) as ItemSerial
FROM dnworld.dbo.MaterializedItems)
SET #ItemSerialfix2 = (#ItemSerial2 + 1)
EXEC DNWorld.dbo.P_SendSystemMail
#nvcSenderName,
#inbReceiverCharacterID,
3,
4,
#Subject,
#Content,
#CoinAmount,
#ItemSerialfix1,
#ItemID1,
#insItemCount1,
24000,
0,
#LevelItem1,
0,
0,
1,
0,
1,
0,
#ItemSerialfix2,
#itemid2,
#intChannelID = 0,
#intMapID = 0,
#intTotalMailCount = null,
#intNotReadMailCount = 1,
#int7DaysLeftMailCount = null,
#intMailID = null,
#inyMailTabCode = #MailTabCode
END
END
GO
--------------------------------------------------- PVP Mail Rewards #26 - #100 ---------------------------------------------------
USE DNWorld
GO
SET ANSI_NULLS, QUOTED_IDENTIFIER ON
GO
DECLARE
#nvcSenderName nvarchar(50),
#Subject nvarchar(50),
#Content nvarchar(300),
#CharacterName nvarchar(50),
#CoinAmount bigint,
#ItemID1 bigint ,
#ItemID2 bigint ,
#insItemCount1 smallint,
#insItemCount2 smallint,
#LevelItem1 bigint,
#LevelItem2 bigint,
#MailTabCode tinyint,
#i int = 0
WHILE (#i <= 100) -- BE AWARE IF EDITING
BEGIN
SET #i = #i + 1
-- FROM
SET #nvcSenderName = 'PVP Logistics Bot'
-- SUBJECT
SET #Subject = 'Top 100 Player Killers'
-- MESSAGE
SET #Content = 'Outstanding job on maintaining your status as one of the top 100 players throughout Skitzovania.'
-- TARGET CHAR and Loop to desired rank #
SET #CharacterName = (SELECT CharacterName FROM #TempAccountTable where TotalRank = 25 + #i)
-- COPPER
SET #CoinAmount = '0'
-- ITEM ID
SET #ItemID1 = '374341682' -- Goddess Medal
SET #ItemID2 = '335833344' -- Warrior's Trophy
-- QUANTITY
SET #insItemCount1 = '1'
SET #insItemCount2 = '2'
-- ENHANCEMENT LEVEL
SET #LevelItem1 = '0'
SET #LevelItem2 = '0'
-- MAIL TAB TYPE
-- 0 All, 1 Character, 2 Contents, 3 Event, 4 System
SET #MailTabCode = 4
DECLARE
#ItemSerial1 bigint
DECLARE
#ItemSerialfix1 bigint
DECLARE
#inbReceiverCharacterID int
BEGIN
SET #inbReceiverCharacterID = (Select CharacterID from DNWorld.dbo.Characters where CharacterName = #CharacterName)
SET #ItemSerial1 = (SELECT max(ItemSerial) as ItemSerial
FROM dnworld.dbo.MaterializedItems)
SET #ItemSerialfix1 = (#ItemSerial1 + 1)
DECLARE
#ItemSerial2 bigint
DECLARE
#ItemSerialfix2 bigint
SET #ItemSerial2 = (SELECT max(ItemSerial) as ItemSerial
FROM dnworld.dbo.MaterializedItems)
SET #ItemSerialfix2 = (#ItemSerial2 + 1)
EXEC DNWorld.dbo.P_SendSystemMail
#nvcSenderName,
#inbReceiverCharacterID,
3,
4,
#Subject,
#Content,
#CoinAmount,
#ItemSerialfix1,
#ItemID1,
#insItemCount1,
24000,
0,
#LevelItem1,
0,
0,
1,
0,
1,
0,
#ItemSerialfix2,
#itemid2,
#intChannelID = 0,
#intMapID = 0,
#intTotalMailCount = null,
#intNotReadMailCount = 1,
#int7DaysLeftMailCount = null,
#intMailID = null,
#inyMailTabCode = #MailTabCode
END
END
GO

Error converting data type varchar to numeric in sql server

I have created a stored procedure for Save InvoicePayment. When I tried to execute the stored procedure, I get the below error:
Msg 8114, Level 16, State 5, Procedure USP_SaveInvoicePayment_LKO,
Line 0 [Batch Start Line 118] Error converting data type varchar to
numeric.
ALTER PROC Usp_saveinvoicepayment_lko #RECEIPTNO VARCHAR(500),
#INVOICEID BIGINT,
#PayableAmount NUMERIC(10, 2),
#RECEIPTDT DATE,
#PAYMENTMODE VARCHAR(50),
#TRANREF VARCHAR(500),
#USERID BIGINT,
#CHEQUEDATE DATE,
#isValid INT,
#remark VARCHAR(100),
#InvoiceNo VARCHAR(50),
#PaymentRecieved VARCHAR(20),
#PreviousBalance NUMERIC(10, 2),
#ChequeNumber VARCHAR(20),
#PaymentMonth VARCHAR(20),
#PaymentDate DATE,
#CollectorMobile INT,
#Latitude NUMERIC(10, 2),
#Longitude NUMERIC(10, 2),
#RESPONSECODE INT output,
#RESPONSEMESSAGE VARCHAR(255) output,
#IDRESPONSE VARCHAR(200) output
AS
SET nocount ON
BEGIN
--declare #PRJCD varchar(10),
--#KML GEOMETRY ,
--#rowcount int
BEGIN try
--SELECT #PRJCD=PRJCD FROM PRJMST WHERE ID=#PRJID
IF #RECEIPTNO IS NULL
BEGIN
DECLARE #id_out TABLE
(
id VARCHAR(200)
)
--set #ENTRYDATE=getdate()
-- select
convert(varchar,isnull(max(convert(numeric,substring(receiptno,21,50)))+1,1))
from invoicepayment where invoiceid=#invoiceid
INSERT INTO invoicepayment_lko
(receiptno,
invoiceid,
payableamount,
receiptdt,
paymentmode,
trnreference,
userid,
entrydate,
chequedate,
isvalid,
remark,
invoiceno,
paymentrecieved,
previousbalance,
chequenumber,
paymentmonth,
paymentdate,
collectormobile,
latitude,
longitude)
output inserted.receiptno
INTO #id_out
VALUES ( #INVOICENO + '/REC/'
+ (SELECT CONVERT(VARCHAR, Isnull(
Max(CONVERT(NUMERIC, Substring(
receiptno, 21,
50
)))
+ 1, 1))
FROM invoicepayment_lko
WHERE invoiceid = #invoiceid),
#INVOICEID,
#PayableAmount,
#RECEIPTDT,
#PAYMENTMODE,
#TRANREF,
#USERID,
Getdate(),
#CHEQUEDATE,
#isValid,
#remark,
#InvoiceNo,
#PaymentRecieved,
#PreviousBalance,
#ChequeNumber,
#PaymentMonth,
#PaymentDate,
#CollectorMobile,
#Latitude,
#Longitude)
SELECT #IDRESPONSE = id
FROM #id_out
SELECT Ident_current(id)
FROM invoicepayment_lko
UPDATE invoicedetails
SET balanceamt = balanceamt - #PayableAmount
WHERE id = #INVOICEID
END
ELSE
BEGIN
UPDATE invoicepayment_lko
SET chequedate = #CHEQUEDATE,
invoiceid = #INVOICEID,
payableamount = #PayableAmount,
receiptdt = #RECEIPTDT,
paymentmode = #PAYMENTMODE,
trnreference = #TRANREF,
userid = #USERID,
updatedt = Getdate(),
isvalid = #isValid,
remark = #remark,
invoiceno = #InvoiceNo,
paymentrecieved = #PaymentRecieved,
previousbalance = #PreviousBalance,
chequenumber = #ChequeNumber,
paymentmonth = #PaymentMonth,
paymentdate = #PaymentDate,
collectormobile = #CollectorMobile,
latitude = #Latitude,
longitude = #Longitude
WHERE receiptno = #RECEIPTNO
SET #IDRESPONSE = #RECEIPTNO
END
DECLARE #totalPayment NUMERIC(10, 2)
SELECT #totalPayment = Sum(payableamount)
FROM invoicepayment_lko
WHERE invoiceid = #invoiceid
AND isvalid = 1
SELECT #totalPayment,
#invoiceid
END try
BEGIN catch
SELECT #RESPONSECODE = Error_number(),
#RESPONSEMESSAGE = Error_message();
--set #RESPONSEMESSAGE= #prjid;
SELECT #RESPONSECODE,
#RESPONSEMESSAGE
END catch
IF #RESPONSECODE IS NULL
BEGIN
IF #isValid = 0
BEGIN
SET #RESPONSEMESSAGE = 'Payment marked as Invalid'
END
ELSE
BEGIN
SET #RESPONSEMESSAGE = 'Payment created/Updated
Successfully'
END
SET #RESPONSECODE = 200
END
END
--Execution
DECLARE #RESPONSECODE INT = 20,
#RESPONSEMESSAGE VARCHAR(255) = 'Payment Created',
#IDRESPONSE VARCHAR(200) = 'No Response'
EXEC Usp_saveinvoicepayment_lko
NULL,
123,
'87.09',
'2019-07-11',
'Debit',
'test',
12,
'2019-08-11',
1,
'test',
'LP/0819/0000183',
'yES',
'90.09',
'9875',
'7',
'2019-07-18',
988893739,
'28.09',
'76.09',
#RESPONSECODE out,
#RESPONSEMESSAGE out,
#IDRESPONSE out
Please check your input again. As per the parameters passed in procedure
EXEC Usp_saveinvoicepayment_lko
NULL,
123,
'87.09',
'2019-07-11',
'Debit',
'test',
12,
'2019-08-11',
1,
'test',
'LP/0819/0000183',
'yES',
'90.09',
'9875',
'7',
'2019-07-18',
988893739, --- this is one of the reason for your error as you took this as int, but this value will definitly not convert into int.
'28.09',
'76.09',
#RESPONSECODE out,
#RESPONSEMESSAGE out,
#IDRESPONSE out
If you can see for phone number is taken as int and int is not right data type for your field mobile number, either take BigInt or use varchar
My suggestion for mobile number field.
#CollectorMobile BIGINT or VARCHAR(15)

Year number range formatting

I have these year number ranges
1993-1997
1923-1935
1998-2015
I'm trying to produce this shortened version of these year ranges.
1993-7
1923-35
1998-2015
So far my query looks like this. But its not working on the 2nd and 3rd samples, 1923-1935, and 1998-2015.
declare #bookyear varchar(50)
declare #year1 char(4)
declare #year2 char(4)
set #bookyear = '1993-1997'
set #year1 = substring(#bookyear, 1, charindex('-', #bookyear)-1)
set #year2 = substring(#bookyear, charindex('-', #bookyear) + 1, len(#bookyear))
select cast(#year1 as varchar(50)) + '-'+ substring(#year2, 4, 1)
Note: Year is always in 4 digits.
I am assuming you want a single digit year if its within the same decade, and double digit year when its a different decade. In which case use a case statement to compare the decade component and then display the appropriate number of digits e.g.
declare #bookyear varchar(50), #year1 char(4), #year2 char(4);
set #bookyear = '1993-1997';
set #year1 = substring(#bookyear, 1, charindex('-', #bookyear)-1);
set #year2 = substring(#bookyear, charindex('-', #bookyear) + 1, len(#bookyear));
select cast(#year1 as varchar(50)) + '-'
+ case when substring(#Year1,1,3) = substring(#Year2,1,3) then substring(#year2, 4, 1)
when substring(#Year1,1,2) = substring(#Year2,1,2) then substring(#year2, 3, 2)
else substring(#year2, 1, 4) end;
Assuming your input strings will always have same length i.e. 9 characters
drop table if exists t
create table t (d varchar(9))
insert into t values
('1993-1997')
,('1923-1935')
,('1998-2015')
,('2095-2115')
SQLFIDDLE
select d
, case
when LEFT(d, 3) = LEFT(RIGHT(d , 4), 3) then LEFT(d, 5) + RIGHT(d, 1)
when LEFT(d, 2) = LEFT(RIGHT(d , 4), 2) then LEFT(d, 5) + RIGHT(d, 2)
when LEFT(d, 1) = LEFT(RIGHT(d , 4), 1) then LEFT(d, 5) + RIGHT(d, 3)
ELSE d
end
FROM t
There are actually 2 ways to address this.
Looking for the same from the right.
Or looking for differences from the left.
Example snippet:
declare #bookyear varchar(9);
set #bookyear = '1993-1997';
--
-- looking for different digits from left to right
--
set #bookyear = left(#bookyear,5) +
case
when left(#bookyear,1) != substring(#bookyear,6,1) then right(#bookyear,4)
when left(#bookyear,2) != substring(#bookyear,6,2) then right(#bookyear,3)
when left(#bookyear,3) != substring(#bookyear,6,3) then right(#bookyear,2)
else right(#bookyear,1)
end;
select #bookyear as bookyear1;
-- reset variable
set #bookyear = '1993-1997';
--
-- looking for same digits from right to left
--
set #bookyear = left(#bookyear,5) +
case
when left(#bookyear,3) = substring(#bookyear,6,3) then substring(#bookyear,9,1)
when left(#bookyear,2) = substring(#bookyear,6,2) then substring(#bookyear,8,2)
when left(#bookyear,1) = substring(#bookyear,6,1) then substring(#bookyear,7,3)
else substring(#bookyear,6,4)
end;
select #bookyear as bookyear2;
A test snippet using a table variable:
declare #Test table (bookyear varchar(9));
insert into #Test (bookyear) values
('1993-1997'),
('1923-1935'),
('1998-2015'),
('2095-2115');
select
bookyear,
left(bookyear,5) +
case
when left(bookyear,1) != substring(bookyear,6,1) then right(bookyear,4)
when left(bookyear,2) != substring(bookyear,6,2) then right(bookyear,3)
when left(bookyear,3) != substring(bookyear,6,3) then right(bookyear,2)
else right(bookyear,1)
end as bookyear1,
left(bookyear,5) +
case
when left(bookyear,3) = substring(bookyear,6,3) then substring(bookyear,9,1)
when left(bookyear,2) = substring(bookyear,6,2) then substring(bookyear,8,2)
when left(bookyear,1) = substring(bookyear,6,1) then substring(bookyear,7,3)
else substring(bookyear,6,4)
end as bookyear2
from #Test;
Returns:
bookyear bookyear1 bookyear2
1993-1997 1993-7 1993-7
1923-1935 1923-35 1923-35
1998-2015 1998-2015 1998-2015
2095-2115 2095-115 2095-115
A test on rextester here

How to loop each record in CTE and read column values

I have to loop data in CTE and fetch each record and process based on one column value.
CREATE PROCEDURE [dbo].[sp_saveVmssMapping]
(#serverTypes INT = 0,
#vmsses INT = 0,
#regions INT = 0,
#countryList VARCHAR)
AS
WITH countryTbl AS
(
SELECT value
FROM STRING_SPLIT(#countryList, ',')
)
DECLARE #cnt INT = 0;
DECLARE #cnt_total INT;
SET #cnt_total = SELECT COUNT(*) FROM countryTbl;
WHILE #cnt < cnt_total
BEGIN
IF NOT EXISTS (SELECT * FROM VmssCountryMapping
WHERE VmssId = #vmsses
AND Country_code = #userName
AND ServerTypeId = #serverTypes)
BEGIN
INSERT INTO VmssCountryMapping (VmssId, Country_code, ServerTypeId)
VALUES (#vmsses, #userName, #serverTypes)
END
SET #cnt = #cnt + 1;
END
Here I have created a CTE where I am adding comma separated records. Now I will loop each record and process the insert if it matches the query.
There's no need to loop a result, you can simply insert everything at once.
CREATE PROCEDURE [dbo].[sp_saveVmssMapping]
(
#serverTypes INT = 0,
#vmsses INT = 0,
#regions INT = 0,
#countryList VARCHAR(AlwaysAssignALength)
)
AS
with countryTbl as
(
SELECT value FROM STRING_SPLIT(#countryList, ',')
)
INSERT INTO VmssCountryMapping (VmssId, Country_code, ServerTypeId)
SELECT #vmsses, value, #serverTypes
FROM countryTbl c
WHERE NOT EXISTS (SELECT *
FROM VmssCountryMapping m
WHERE m.VmssId = #vmsses
AND m.Country_code = c.value
AND m.ServerTypeId = #serverTypes);

Procedure inserts only one record

I have this stored procedure:
CREATE PROCEDURE [dbo].[TVP_OfferPrice] #OfferPriceTVP TVP_OfferPrice READONLY
AS
BEGIN
DECLARE #OfferId INT;
DECLARE #CountryId INT ;
DECLARE #VatRateId INT ;
DECLARE #SalePrice DECIMAL(16, 4) ;
DECLARE #SaleFromDate DATETIME;
DECLARE #SaleToDate DATETIME;
DECLARE #DefaultPrice DECIMAL(16, 4);
DECLARE #Price DECIMAL(16,4);
SELECT
#OfferId = a.OfferId, #CountryId = a.CountryId, #VatRateId = a.VatRateId,
#SalePrice = a.SalePrice, #SaleFromDate = a.SaleFromDate, #SaleToDate = a.SaleToDate,
#DefaultPrice =a.DefaultPrice
FROM
#OfferPriceTVP a;
SET #Price = (SELECT TOP 1 pp.Price
FROM [dbo].[Promotion] p
INNER JOIN [dbo].[PromotionProduct] pp ON pp.ProductId = p.Id
INNER JOIN [dbo].[Offer] do ON do.ProductId = pp.ProductId AND do.Id = #OfferId
INNER JOIN [dbo].[PromotionAssignment] pda ON pda.PromotionId = p.Id AND pda.Id = do.Id
WHERE p.CountryId = #CountryId
AND GETUTCDATE() >= p.ValidFrom AND GETUTCDATE() < p.ValidTo
ORDER BY p.ValidFrom DESC, pp.Price)
IF(#Price IS NULL AND #SalePrice IS NOT NULL AND GETUTCDATE() >= #SaleFromDate AND GETUTCDATE() < #SaleFromDate)
SET #Price = #SalePrice
IF #Price IS NULL
SET #Price = #DefaultPrice
IF NOT EXISTS (SELECT * FROM [dbo].[OfferPrice] dop WHERE dop.OfferId = #OfferId AND dop.CountryId = #CountryId)
INSERT INTO [dbo].[OfferPrice](OfferId, CountryId, VatRateId, Price, DefaultPrice, SalePrice, SaleFromDate, SaleToDate)
SELECT
#OfferId, #CountryId, #VatRateId, #Price, #DefaultPrice,
#SalePrice, #SaleFromDate, #SaleToDate
ELSE
UPDATE b
SET b.VatRateId = #VatRateId, #Price = #Price, b.DefaultPrice = #DefaultPrice,
b.SalePrice = #SalePrice, b.SaleFromDate = #SaleFromDate, b.SaleToDate = #SaleToDate
FROM
[dbo].OfferPrice b
WHERE
b.OfferId = #OfferId AND b.CountryId = #CountryId;
END
and when I try to execute it with some values for example:
DECLARE #OfferPriceTVP AS [dbo].[TVP_DealerOfferPrice]
INSERT INTO #OfferPriceTVP (DealerOfferId, CountryId, VatRateId, DefaultGrossPrice, SaleGrossPrice, SaleFromDate, SaleToDate)
VALUES (10006805, 1, 1, 1, 1, 2, NULL),
(10006806, 1, 1, 2, 1, NULL, NULL),
(10006807, 1, 1, 3, 1, NULL, NULL),
(10006808, 1, 1, 4, 1, NULL, NULL),
(10006809, 1, 1, 5, 1, NULL, NULL),
(10006810, 1, 1, 6, 1, NULL, NULL);
EXEC [dbo].[TVP_DealerOfferPrice] #OfferPriceTVP;
GO
SQL Server shows me that only 1 row gets affected and indeed last value gets only into my table. Any idea why?
Basically, since your variable cannot hold more than one value at the same time, with this statement:
SELECT #OfferId = a.OfferId
,#CountryId = a.CountryId
,#VatRateId = a.VatRateId
,#SalePrice = a.SalePrice
,#SaleFromDate = a.SaleFromDate
,#SaleToDate = a.SaleToDate
,#DefaultPrice = a.DefaultPrice
FROM #OfferPriceTVP a;
you are holding only one record of your input table.
I guess that you are trying to merge the input table with the OfferPrice table. So, you better use the MERGE statement. Here is an example:
MERGE OfferPrice AS TARGET
USING (SELECT VatRateId
,CASE WHEN Price IS NULL AND SalePrice IS NOT NULL AND GETUTCDATE() >= SaleFromDate AND GETUTCDATE() < SaleFromDate THEN SalePrice ELSE DefaultPrice END AS Price
-- And so on and so forth
FROM #OfferPriceTVP) AS SOURCE
ON TARGET.OfferId = SOURCE.OfferId
WHEN MATCHED THEN
UPDATE SET VatRateId = SOURCE.VatRateId
,Price = SOURCE.Price
-- And so on and so forth
WHEN NOT MATCHED THEN
INSERT (OfferId, CountryId) -- And so on and so forth
VALUES (SOURCE.OfferId, SOURCE.CountryId) -- And so on and so forth
More informations here:
MERGE (Transact-SQL)
CASE (Transact-SQL)