unable to fix the type conversion issue from varchar to int on this query, I wrote - sql

I need help to fix a type conversion issue that is bugging me for few hours on the sp I wrote. I want to split up bill number like this '12/SH/IFCR/7' and get the last int value and store it separately
Eg. '12/SH/IFCR/7' --> 12/SH/IFCR/ and 7
Note: 12/SH/IFCR/ is prefix which stays the same but the last number changes
ALTER PROCEDURE spGenerateCreditInvoiceForApi
#ShopId as int,
#TransId as int
--#CompanyId as int
AS
BEGIN
SET NOCOUNT ON;
declare #CompanyId as int
declare #Prefix as varchar(50)
declare #ProformaId as int
declare #MaxId as int
declare #FinId as int
declare #InvoiceNo as varchar(150)
set #CompanyId=(select CompanyID from aShops where ShopID=#ShopId)
set #FinId=(Select financialid from afinancialyear where Curfinancialyear = 1 and companyid = #CompanyId)
set #Prefix=(SELECT Prefix FROM aPrefix WHERE InterfaceID = 1504 and ShopId=#ShopId and FinancialId = #FinId)
set #ProformaId=(select ISNULL(MAX(CONVERT(INT,REVERSE(LEFT((REVERSE(ihInvoiceNo)),(PATINDEX('%/%' ,(REVERSE (ihInvoiceNo))))-1)))),0)
from LOsInvoiceHeader
where ihInvoiceID= #TransId and ihShopID=#ShopId)
--SET #intBillID = (SELECT CASE WHEN COUNT(poshBillid)=0 THEN 1 ELSE MAX(poshBillid)+1 END FROM losposheader WHERE poshShopID=#intShopId)
set #MaxId=(SELECT CASE WHEN COUNT(ihInvoiceNo)=0 THEN 1
ELSE MAX(ihInvoiceNo)+1 END
from losinvoiceheader
where ihShopId =#ShopId and ihfinancialid=#FinId and ihType='I')
SET #InvoiceNo = (#Prefix+CONVERT(VARCHAR,#MaxId))
--update LOsInvoiceHeader set ihInvoiceNo=#InvoiceNo, ihProformaID=#ProformaId where ihInvoiceID=#TransId and ihShopID=#ShopId
--print #InvoiceNo
END
GO
Error:
Msg 245, Level 16, State 1, Procedure spGenerateCreditInvoiceForApi, Line 33
Conversion failed when converting the varchar value '12/SH/IFCR/7' to data type int.
Thanks in advance.

How about something like this
declare #bill varchar(50),
#reversebill varchar(50),
#reverseResult varchar(50)
select #bill = '12/SH/IFCR/73'
select #reversebill = REVERSE(#bill)
select #reverseResult = SUBSTRING(#reversebill,0,CHARINDEX('/',#reversebill))
select Reverse(#reverseResult)

Thank god I found the issue
I changed use of Max() to count() and added a convert method to the whole query
Before
set #MaxId=(SELECT CASE WHEN COUNT(ihInvoiceNo)=0 THEN 1
ELSE MAX(ihInvoiceNo)+1 END
from losinvoiceheader
where ihShopId =#ShopId and ihfinancialid=#FinId and ihType='I')
After
set #MaxId=CONVERT(INT,(SELECT CASE WHEN COUNT(ihInvoiceNo)=0 THEN 1
ELSE CONVERT(INT,COUNT(ihInvoiceNo)+1) END
from losinvoiceheader
where ihShopId =#ShopId and ihfinancialid=#FinId and ihType='I'))

Related

SQL Function is INCREDIBLY slow

I have a SQL function that is used to return a single value in another view, this function takes well over 30 seconds sometimes on larger databases, I think it might be running over and over?
Honestly I'm just losing my mind at this point and need the help. Does anyone know the best way to optimize this?
The T-SQL function looks like this:
IF OBJECT_ID('Base.fn_AssetPriority') IS NOT NULL
DROP FUNCTION Base.fn_AssetPriority
GO
CREATE FUNCTION Base.fn_AssetPriority
(#LID BIGINT)
RETURNS NVARCHAR(20)
WITH EXECUTE AS CALLER
AS
BEGIN
DECLARE #Priority NVARCHAR(20)
DECLARE #RGID BIGINT
DECLARE #CP TABLE
(
ConsequenceAssessmentID BIGINT,
[Sign] VARCHAR(2),
Score DECIMAL(18,2),
AssetPriority CHAR(1),
ConsNo INT
)
SET #Priority = 'Not Allocated'
INSERT INTO #CP
SELECT
ConsequenceAssessmentID, [Sign], Score, AssetPriority, ConsNo
FROM
Base.ConsequencePriority
ORDER BY
ConsNo DESC
SELECT #RGID = MAX(ID)
FROM ACA.ReviewGroup
WHILE EXISTS (SELECT * FROM #CP)
BEGIN
DECLARE #CAID BIGINT
DECLARE #ConsNo INT
DECLARE #Sign VARCHAR(2)
DECLARE #Score DECIMAL(18,2)
DECLARE #AP CHAR(1)
SELECT TOP 1
#CAID = ConsequenceAssessmentID,
#ConsNo = ConsNo,
#Sign = [Sign],
#Score = Score,
#AP = AssetPriority
FROM
#CP
ORDER BY
ConsNo DESC
IF #Sign = '='
BEGIN
IF EXISTS (SELECT * FROM ACA.ConsequenceAssessment
WHERE LID = #LID AND RGID = #RGID
AND BaseCAID = #CAID AND Score = #Score)
BEGIN
SET #Priority = #AP
BREAK
END
END
ELSE BEGIN
IF EXISTS (SELECT * FROM ACA.ConsequenceAssessment
WHERE LID = #LID AND RGID = #RGID
AND BaseCAID = #CAID AND Score >= #Score)
BEGIN
SET #Priority = #AP
BREAK
END
END
DELETE FROM #CP
WHERE ConsequenceAssessmentID = #CAID
AND ConsNo = #ConsNo
END
RETURN #Priority
END
There is another view that calls this as a field:
Base.fn_AssetPriority(BaseAS.ID) AS AssetPriority,
How on Earth do I optimize this? or get it to run a bit quicker?
It's possible the execution plan for your stored function is stale.
Try doing this and rerunning it.
EXEC sp_recompile N'Base.fn_AssetPriority';
If it gets faster you may want to run that recompile every so often. Maybe use a job to recompile it every day.
You probably don't want to put WITH RECOMPILE in the function's definition, because you use it a lot and the reason for recompilation is changing statistics in the tables it queries.

Update statement, update value is conditionally evaluated

I have the below pseudo code written that I want to implement in T-SQL. I need this code included in an existing stored procedure, I was trying to achieve the below with function call passing in a temp table as a parameter, is it possible to pass a temp table as a function parameter. Please let me know if there is a better approach to this.
Table: #Temp_Table has a column RefId which refers to #TempReadUpdateValue.Id. There are rules to identify if the #TempReadUpdateValue.Id can be applied to #Temp_Table.RefId.
Rule 1: the data qualifies in the DateRange
Rule 2: the #TempReadUpdateValue.Id is available if (Allowed - Used) > 0.
Allowed is fixed value and used will increment as its assigned.
I want to achieve the above with an UPDATE statement on Temp_Table, the challenge that I face is #Temp_Table.RefId = #TempReadUpdateValue.Id, need to increment
#TempReadUpdateValue.Used = #TempReadUpdateValue.Used + #Temp_Table.Units
every next row in #Temp_Table need to re-evaluate rules #1 and #2 for RefId assignment.
Update statement:
DECLARE #OLD INT = 0; -- THIS CAN ALSO BE SET TO 1, basically passed in as param to the stored procedure.
CREATE TABLE #TempReadUpdateValue
(
Id INT,
From_Date DateTime,
Thru_Date DateTime,
Allowed int,
Used int
)
CREATE TABLE #Temp_Table
(
Pk_ID INT,
DOS DateTime,
Units Int,
Ref_Id int
)
UPDATE #Temp_Table
SET Ref_Id = CASE
WHEN #OLD = 0 THEN 121
ELSE NewImplementation(DOS, Units, #TempReadUpdateValue)
END
CREATE OR ALTER FUNCTION NewImplementation
(#DOS DATETIME, #Units INT, #TempReadUpdateValue)
RETURNS INT
AS
BEGIN
DECLARE #Id INT
DECLARE #Allowed INT
DECLARE #Used INT
SELECT
#Id = Id,
#Allowed = Allowed,
#Used = Used
FROM
#TempReadUpdateValue
DECLARE #ReturnValue INT = 0
IF (#Id > 0) AND (#Allowed - #Used ) > 0
BEGIN
#ReturnValue = #Id;
UPDATE #TempReadUpdateValue
SET Used = (Used + #Units);
END
RETURN #ReturnValue
END

Passing Variable from stored procedure to another

I have a stored procedure that I need pass the parameter from one to the other procedure and have it display as an output. I am declaring the following in the header of my procedure [xxx].[zzzz_ERP_Cyyyyy]
DECLARE #ProcedureLogRowKey INT
DECLARE #ProcedureRecordCount INT
DECLARE #ProcedureStartDateTime DATETIME
DECLARE #ProcedureLog_Note NVARCHAR(100)
EXEC [XXX].[spciProcedurePerformanceStartRecord_help]
'.[xxx].[zzzz_ERP_Cyyyyy]',
1,
#ProcedureStartDateTime,
'Contract Check',
#ProcedureLogRowKey OUTPUT
I am getting the following error:
Msg 515, Level 16, State 2, Procedure spciProcedurePerformanceStartRecord_help, Line 33 [Batch Start Line 17]
Cannot insert the value NULL into column 'YSTRTDTT_0', table '000.xxx.YPERLOG'; column does not allow nulls. INSERT fails.
Here is the procedure that I am getting the variable from to pass into my procedure [xxx].[zzzz_ERP_Cyyyyy]
CREATE PROCEDURE [xxx].[spciProcedurePerformanceStartRecord_help]
(#ProcedureName VARCHAR(200),
#ProcedureRecordCount INT = 1,
#ProcedureStartDateTime DATETIME = GETDATE,
#ProcedureLog_Note NVARCHAR(100),
#ProcedureLogRowKey INT OUTPUT --- I am passing this into my proc and
displaying it as output
)
AS
BEGIN
-- Set Default return for #ProcedureLogRowKey, used if logging is not turned on.
SET #ProcedureLogRowKey = -1;
-- Check to see if performance logging is enabled
IF EXISTS(SELECT ROWID FROM LIVE.YPERCON
WHERE YPROCNM_0 = #ProcedureName AND YLOGENA_0 = 2)
BEGIN
INSERT INTO xxx.YPERLOG (YROWKEY_0, YPROCNM_0, YRECCNT_0, YSTRTDTT_0, YENDDTT_0, YLOGNOTE_0,
YDURMS_0, CREDATTIM_0, UPDDATTIM_0, AUUID_0, CREUSR_0, UPDUSR_0)
SELECT
ISNULL(MAX(YROWKEY_0), 0) + 1,
#ProcedureName, #ProcedureRecordCount, #ProcedureStartDateTime,
'1753-01-01',
#ProcedureLog_Note, 0,
GETDATE(), GETDATE(), NEWID(), 'admin', 'admin'
FROM
xxx.YPERLOG
SELECT #ProcedureLogRowKey = ISNULL(MAX(YROWKEY_0), 0)
FROM xxx.YPERLOG
END
ELSE
BEGIN
DECLARE #Count integer
SELECT #Count = COUNT(0)
FROM LIVE.YPERERR
WHERE YPROCNM_0 = #ProcedureName
IS ISNULL(#Count, 0) = 0
INSERT INTO LIVE.YPERERR (YPROCNM_0, YREQDT_0, YLASTDT_0, YERRMSG_0,
CREDATTIM_0, UPDDATTIM_0, AUUID_0, CREUSR_0, UPDUSR_0)
VALUES (#ProcedureName, GETDATE(), '1753-01-01', 'Controller not defined or active',
GETDATE(), GETDATE(), NEWID(), 'admin', 'admin')
ELSE
UPDATE xxx.YPERERR
SET YLASTDT_0 = GETDATE()
WHERE YPROCNM_0 = #ProcedureName
END
END
Thanks in advance.
The issue is in procedure [xxx].[spciProcedurePerformanceStartRecord_help] with parameter #ProcedureStartDateTime DATETIME. You should set its default value this way:
In declaration set default value as NULL
#ProcedureStartDateTime DATETIME = NULL
It would look like tihs
CREATE PROCEDURE [xxx].[spciProcedurePerformanceStartRecord_help]
(
#ProcedureName VARCHAR(200)
,#ProcedureRecordCount INT = 1
,#ProcedureStartDateTime DATETIME = NULL
,#ProcedureLog_Note NVARCHAR(100)
,#ProcedureLogRowKey INT OUTPUT
)
AS
BEGIN
-- procedure's body
END
Inside procedure, at the beginning, check if #ProcedureStartDateTime parameter's value is NULL and if it is, set its value to GETDATE().
SET #ProcedureStartDateTime = ISNULL(#ProcedureStartDateTime, GETDATE())
You have declared DECLARE #ProcedureStartDateTime DATETIME and did not set any value to it. so, it is having NULL value and you are passing NULL value to the procedure execution
EXEC [XXX].[spciProcedurePerformanceStartRecord_help]
'.[xxx].[zzzz_ERP_Cyyyyy]',
1,
#ProcedureStartDateTime, -- NULL value passed here
'Contract Check',
#ProcedureLogRowKey OUTPUT
As the target column 'YSTRTDTT_0', table '000.xxx.YPERLOG', does not allow NULLs, you are getting error.

MSSQL says I am trying to convert a varchar to a string when I'm not

So I have this fairly long procedure at Work that I just made. What it does it not that important, but the end result is what matters.
I need to count some different types of descriptions in a table and that Works fine. I then need to take the two things that I Count and put them in a string that I return to my software. However, every time I run this procedure it gives me this:
Msg 245, Level 16, State 1, Procedure WorkDays, Line 43 Conversion
failed when converting the varchar value
'FlightDeck:161,CabinCrew:189' to data type int.
I just can't figure out why it keeps telling me this when I am not trying to convert a varchar to an int but rather ints to a single varchar.
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[WorkDays] #requestedDate nchar(10)
AS
SET ANSI_WARNINGS OFF
DECLARE #date as nchar(10) = ''
DECLARE #returnVal as varchar(30) = ''
DECLARE #flightDeck as int = 0
DECLARE #cabinCrew as int = 0
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
SET #date = #requestedDate
SELECT
#flightDeck = SUM(CASE WHEN dbo.Crew_Category.Description LIKE 'Flight Deck' THEN 1 END),
#cabinCrew = SUM(CASE WHEN dbo.Crew_Category.Description LIKE 'Cabin Crew' THEN 1 END)
FROM
dbo.CrewMember INNER JOIN
dbo.Crew_Category ON dbo.CrewMember.CrewCategorySeqNo = dbo.Crew_Category.CrewCategorySeqno
WHERE
(dbo.Crew_Category.Description = N'Flight Deck' OR
dbo.Crew_Category.Description = N'Cabin Crew') AND
(dbo.CrewMember.EmploymentEndDate > #date)
AND dbo.CrewMember.CrewSeqno NOT IN (
SELECT
CrewMember_1.CrewSeqno
FROM
dbo.CrewMember AS CrewMember_1 INNER JOIN
dbo.CrewReqAsg ON CrewMember_1.CrewSeqno = dbo.CrewReqAsg.crewSeqno INNER JOIN
dbo.activity ON dbo.CrewReqAsg.act_seqno = dbo.activity.act_seqno INNER JOIN
dbo.ActivityType ON dbo.activity.actType_seqno = dbo.ActivityType.actType_seqno INNER JOIN
dbo.ActivityCategory ON dbo.ActivityType.ActCat_seqno = dbo.ActivityCategory.actCat_seqno INNER JOIN
dbo.Crew_Category AS Crew_Category_1 ON CrewMember_1.CrewCategorySeqNo = Crew_Category_1.CrewCategorySeqno
WHERE (
dbo.ActivityCategory.Category = N'Ferie' OR
dbo.ActivityCategory.Category = N'Fridage' OR
dbo.ActivityCategory.Category = N'Sygdom') AND (Crew_Category_1.Description = N'Flight Deck' OR
Crew_Category_1.Description = N'Cabin Crew') AND (LEFT(dbo.activity.Start,10) LIKE #date));
SET #returnVal = 'FlightDeck:'+CAST(#flightDeck AS varchar);
SET #returnVal += ',CabinCrew:'+CAST(#cabinCrew AS varchar);
END
RETURN #returnVal
It's been a while since I've had to do this so perhaps I just forgot something fundamental. Please help me figure out why this happens? :)
Yes, you forgot something fundamental. To return data to the caller, use SELECT, not RETURN.
You need
SELECT #returnVal

within a sql while loop, get id of last inserted row then insert it into a different table, using OUTPUT?

I am writing some sql, using sql server 2008, to loop around some records. Within this loop I need to insert a row and the ID of that row will be put into a field in an existing record in a different table. I thought I would be able to achieve this with OUTPUT but I couldn't find an example of exactly what I wanted to do.
Here's what I have so far:
DECLARE #courseTempID INT
DECLARE #courseID INT
DECLARE #academicYearID INT
DECLARE #courseCode VARCHAR(10)
DECLARE #uio_id INT
DECLARE #creatorIntranetUserID INT
WHILE (SELECT count(*) FROM CoursesTemp WHERE dmprocessed = 0) > 0
BEGIN
SELECT TOP 1 #id = courseTempID FROM CoursesTemp WHERE dmprocessed = 0
SELECT TOP 1 #academicYearID = academicYearID FROM CoursesTemp WHERE dmprocessed = 0
SELECT TOP 1 #courseCode = courseCode FROM CoursesTemp WHERE dmprocessed = 0
SELECT TOP 1 #uio_id = uio_id FROM CoursesTemp WHERE dmprocessed = 0
SELECT TOP 1 #creatorIntranetUserID = creatorIntranetUserID FROM CoursesTemp WHERE dmprocessed = 0
INSERT INTO dbo.Courses VALUES(3,#academicYearID,1,#courseCode,#uio_id,GETDATE(),#creatorIntranetUserID)
OUTPUT INSERTED.courseID INTO #courseID
UPDATE CoursesTemp SET dmprocessed = 1, courseID = #courseID WHERE courseTempID = #courseTempID
END
The error I am getting is "Incorrect syntax near 'INSERTED'".
Can anyone help me work out how to use OUTPUT in this way please?
A few performance improvements applied. It is safer to use SCOPE_IDENTITY()
DECLARE #courseTempID INT
DECLARE #courseID INT
DECLARE #academicYearID INT
DECLARE #courseCode VARCHAR(10)
DECLARE #uio_id INT
DECLARE #creatorIntranetUserID INT
DECLARE #id INT
WHILE EXISTS(SELECT 1 FROM CoursesTemp WHERE dmprocessed = 0)
BEGIN
SELECT TOP 1 #id = courseTempID
,#academicYearID = academicYearID
,#courseCode = courseCode
,#uio_id = uio_id
,#creatorIntranetUserID = creatorIntranetUserID
FROM CoursesTemp WHERE dmprocessed = 0
INSERT INTO dbo.Courses VALUES(3,#academicYearID,1,#courseCode,#uio_id,GETDATE(),#creatorIntranetUserID)
UPDATE CoursesTemp SET dmprocessed = 1, courseID = SCOPE_IDENTITY() WHERE courseTempID = #courseTempID
END
OUTPUT needs to go between INSERT INTO dbo.Courses and VALUES(...)
You also have OUTPUT INSERTED.courseID INTO #CourseId but #CourseId is an int variable, it needs to be the table you want to insert into.
EDIT I've reread what you're tying to do. Instead of using OUTPUT, you can change your last line to
UPDATE CoursesTemp SET dmprocessed = 1, courseId = SCOPE_IDENTITY()
WHERE courseTempID = #courseTempID
You may be able to use ##IDENTITY to retrieve the last-inserted ID value. http://msdn.microsoft.com/en-us/library/ms187342.aspx