SQL Server : calling a stored procedure or not - sql

I have a stored procedure that has one input parameter.
However, in the stored procedure, there is a condition that needs user's input (WHEN t.flag = 0).
Is there a way not to call a new stored procedure but does the same job?
I do not want to add another input parameter in [dbo].[UpdateTheRate]
itself because I have other processes going on in this stored procedure.
ALTER PROCEDURE [dbo].[UpdateTheRate] (#PID int)
AS
UPDATE t
SET t.rate =
CASE
WHEN t.flag = 1 THEN (select UnitRate
from RateTable
where state ='IL' and Term='20')
WHEN t.flag = 0 THEN EXEC [dbo.rate_procedure] --(USER enter rate)
FROM TblChargeTable t
WHERE t.PID = #PID

Something along these lines?
ALTER PROCEDURE [dbo].[UpdateTheRate] (#PID int)
AS
-- defer executing the procedure unless necessary
IF EXISTS (SELECT 1 FROM TblChargeTable WHERE flag = 0 and PID = #PID)
BEGIN
EXEC dbo.rate_procedure; -- get #user_rate via return code or out parameters perhaps
END
UPDATE t
SET t.rate =
CASE WHEN t.flag = 1 THEN (select UnitRate from RateTable where state ='IL' and Term='20')
WHEN t.flag = 0 THEN #user_rate END -- retrieved above
FROM TblChargeTable t
WHERE t.PID = #PID;

Related

Set Value From Action Update, Delete, Inserted in Stored Procedure SQL

I have created my stored procedure, but I am confused how to set one column of from my table.
This is separate of my code:
CREATE PROC [dbo].[SP_Gabungan]
#REPORT_DT DATE
AS
BEGIN
DECLARE #action NVARCHAR(10),
#insCount INT = (SELECT COUNT (*) FROM INSERTED),
#delCount INT = (SELECT COUNT (*) FROM DELETED)
SELECT
#REPORT_DT AS REPORT_DATE,
FD.BRANCH_CODE AS [BRANCH],
#action AS [ID_OPERATIONAL], -- I want to set this value as 1(if there is a new input data, 2
-- (if there is updated data), 3 (if there is deleted data) from
-- from another field
BR.REGULATOR_BRANCH as [RG_BRANCH]
FROM
[DBO].[F_RR_FUNN] FD
LEFT JOIN
[DBO].[MS_BRANCH] BR ON BR.BRANCH_CD = FD.BRANCH_CODE
WHERE
FD.GROUP_PRODUCT = 'CA'
AND Y17sa = '1'
AND FD.REPORT_DATE = #REPORT_DT
END
How do I set column ID_OPERASIONAL as 1 (if there is a new data from another field), 2 for exists updated data from another field, 3 for deleted data from another field in a stored procedure.
ERROR from this code is:
Invalid object name 'INSERTED'
The problem the ERROR shows is that you cannot use deleted/inserted tables in stored procedures but just accessible in triggers.
If you want to have the count of inserted records or deleted records in a table there are two ways for doing this which the easiest one is:
Create you stored procedure like this:
CREATE PROC [dbo].[SP_Gabungan]
#REPORT_DT DATE,#DeletedCount INT , #InsertedCount Int
AS
BEGIN
...
Create a Trigger after insert and delete (so you can have inserted/deleted tables)
Then get the count just like you did in your code:
DECLARE #action nvarchar (10),
#insCount int = (SELECT COUNT (*) FROM INSERTED),
#delCount int = (SELECT COUNT (*) FROM DELETED)
Call your stored procedure in the Trigger and pass the #insCount and #delCount as inputs
EXEC [dbo].[SP_Gabungan]
#REPORT_DT = GETDATE() , #InsertedCount = #insCount , #DeletedCount = #delCount
A similar question is this for more other ways like temp tables or...
How use inserted\deleted table in stored procedure?
Also the link below is a question asking defining a trigger for both delete and insert so you can use both deleted/inserted tables together
SQL Trigger on Update, Insert, Delete on non-specific row, column, or table
Second way which is better when you are doing all these process a lot, is to get the log of your inserts or updates or deletes so you dont use triggers which reduce performance of your process.
(If usefull I can recommend some ideas for saving table logs)
CREATE PROC [dbo].[SP_Gabungan]
#REPORT_DT DATE
,#DeletedCount INT
,#InsertedCount INT
,#UpdateCount INT
AS BEGIN
DECLARE #action INT
SET #action = CASE
WHEN #InsertCount <> 0 THEN 1
WHEN #UpdateCount <> 0 THEN 2
WHEN #DeletedCount <> 0 THEN 3
END
SELECT
#REPORT_DT AS REPORT_DATE,
FD.BRANCH_CODE AS [BRANCH],
#action AS [ID_OPERATIONAL],
BR.REGULATOR_BRANCH as [RG_BRANCH]
FROM
[DBO].[F_RR_FUNN] FD
LEFT JOIN
[DBO].[MS_BRANCH] BR ON BR.BRANCH_CD = FD.BRANCH_CODE
WHERE
FD.GROUP_PRODUCT = 'CA'
AND Y17sa = '1'
AND FD.REPORT_DATE = #REPORT_DT END
CREATE TRIGGER [YourTriggerName]
AFTER INSERT/UPDATE/DELETE ON [db].[tablename]
FOR EACH ROW
BEGIN
DECLARE
#insCount int = (SELECT COUNT (*) FROM New), -- New in MySQL is same as inserted,deleted,updated
#delCount int = (SELECT COUNT (*) FROM Old),
#upCount int = (SELECT COUNT (*) FROM New),
EXEC [dbo].[SP_Gabungan]
#REPORT_DT = GETDATE()
,#DeletedCount = #delCount
,#InsertedCount = #insCount
,#UpdateCount = #upCount
END

Using different set of WHERE clauses in stored procedure depending on Parameter value

I have 2 stored procedures which return the same columns that I am trying to merge into a single procedure. They both have a different set of parameters and both have different WHERE clauses, but they use the same tables and select the exact same rows.
WHERE clause 1: (uses #UIOID, and #Level)
WHERE ( #UIOID = CASE WHEN #Level = 'Single' THEN C.C_UIOID_PK
WHEN #Level = 'Children' THEN CLC.UIOL_P
WHEN #Level = 'Parent' THEN CLP.UIOL_C
END
OR ( #UIOID = '0'
AND #Level = 'All'
)
)
Where clause 2: (Uses #TeamCode, #Year, #IncludeQCodes)
WHERE C.C_IsChild = 0
AND C.C_MOA <> 'ADD'
AND #TeamCode = C.C_OffOrg
AND C.C_Active = 'Y'
AND ( #Year BETWEEN dbo.f_GetAcYearByDate(C.C_StartDate) AND dbo.f_GetAcYearByDate(C.C_EndDate)
OR #Year = 0 )
AND ( C.C_InstCode NOT LIKE 'Q%'
OR #IncludeQCodes = 1 )
Ideally I want to add a new parameter which basically tells it which of the two WHERE clauses to run, but I can't seem to recreate that with CASE statement because as far as I can tell, they only work for a single WHERE clause, not a whole set of different clauses
I want to do this without having to repeat the select statement again and putting the whole thing in IF statements, and i don't want to put the query into a string either. I just want one select statement ideally.
The problem with using temp tables is the query itself takes a while to run without any parameters and is used in a live website, so I don't want it to have to put all records in a temp table and then filter it.
The problem with using a CTE is you can't follow it with an IF statement, so that wouldn't work either.
Here is the sort of logic I am trying to achieve:
SELECT A
B
C
FROM X
IF #WhichOption = 1 THEN
WHERE ( #UIOID = CASE WHEN #Level = 'Single' THEN C.C_UIOID_PK
WHEN #Level = 'Children' THEN CLC.UIOL_P
WHEN #Level = 'Parent' THEN CLP.UIOL_C
END
OR ( #UIOID = '0'
AND #Level = 'All'
)
)
ELSE IF #WhichOption = 2 THEN
WHERE C.C_IsChild = 0
AND C.C_MOA <> 'ADD'
AND #TeamCode = C.C_OffOrg
AND C.C_Active = 'Y'
AND ( #Year BETWEEN dbo.f_GetAcYearByDate(C.C_StartDate) AND dbo.f_GetAcYearByDate(C.C_EndDate)
OR #Year = 0 )
AND ( C.C_InstCode NOT LIKE 'Q%'
OR #IncludeQCodes = 1 )
Save the following process in a procedure. You can also directly insert into a physical table.
declare #varTable Table (columns exactly as Procedures return)
if(condition is met)
begin
insert into #varTable
exec proc1
end
else
begin
insert into #varTable
exec proc2
end
Add the parameter that you said that it would indicate what filter apply :
select XXXXX
from XXXXX
where (#Mode = 1 and ( filter 1 ))
or
(#Mode = 2 and ( filter 2 ))
option(recompile)
If the #Mode parameter is 1 then it will evaluate the filter 1, otherwise it will evaluate the filter 2.
Add an option(recompile) at the end of the statement, so the SQL engine will replace the variables with their values, eliminate the filter that won't be evaluated, and generate an execution plant for just the filter that you want to apply.
PS: Please notice that although these catchall queries are very easy to code and maintain, and generate a perfectly functional and optimal execution, they are not advised for high-demand applications. The option(recompile) forces the engine to recompile and generate a new execution plan at every execution and that would have a noticeable effect on performance if your query needs to be executed hundreds of times per minute. But for the occasional use it's perfectly fine.
Try to use dynamic SQL:
DECLARE #sql NVARCHAR(max), #where NVARCHAR(max), #WhichOption INT = 1;
SET #sql = 'SELECT A
B
C
FROM X';
IF #WhichOption = 1
SET #where = 'WHERE ( #UIOID = CASE WHEN #Level = ''Single'' THEN C.C_UIOID_PK
WHEN #Level = ''Children'' THEN CLC.UIOL_P
WHEN #Level = ''Parent'' THEN CLP.UIOL_C
END
OR ( #UIOID = ''0''
AND #Level = ''All''
)
)';
ELSE IF #WhichOption = 2
SET #where = ' WHERE C.C_IsChild = 0
AND C.C_MOA <> ''ADD''
AND #TeamCode = C.C_OffOrg
AND C.C_Active = ''Y''
AND ( #Year BETWEEN dbo.f_GetAcYearByDate(C.C_StartDate)
AND dbo.f_GetAcYearByDate(C.C_EndDate)
OR #Year = 0 )
AND ( C.C_InstCode NOT LIKE ''Q%''
OR #IncludeQCodes = 1 ) ';
SET #sql = CONCAT(#sql,' ', #where)
PRINT #sql
EXECUTE sp_executesql #sql

TSQL Cursor calls Stored Procedure 'View Nesting Level Exceeded'

Being a bit of a SQL Newb, I tried to create a SQL script to execute a stored procedure on a number of rows using a cursor. I found the code to create the cursor and the stored procedure works as expected.
However, sometimes during execution I get the error
Maximum stored procedure, function, trigger, or view nesting level exceeded (limit 32)
Here is the SQL script in question. Any ideas why I get this error?
CREATE PROCEDURE p_MigrateRenewalOptions
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
DECLARE #OrderId VARCHAR(255) = NULL
DECLARE #RenewalId INT = 0
DECLARE #DiscountCode VARCHAR(255) = NULL
DECLARE #UpgradeCode VARCHAR(255) = NULL
DECLARE #ProductCode VARCHAR(255) = NULL
DECLARE rCursor CURSOR FOR
SELECT RenewalId FROM t_Renewals WHERE DiscountCode IS NOT NULL AND UpgradeCode IS NOT NULL
OPEN rCursor
FETCH NEXT FROM rCursor INTO #RenewalId
-- Iterate over t_Renewals with DiscountCode, UpgradeCode
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #OrderId = OrderId from t_Renewals where RenewalId = #RenewalId
SELECT #DiscountCode = DiscountCode from t_Renewals where RenewalId = #RenewalId
SELECT #UpgradeCode = UpgradeCode from t_Renewals where RenewalId = #RenewalId
SELECT #ProductCode = ProductCode from t_Order Where OrderId = #OrderId
-- Create renewal options for the t_Renewal entry
EXEC p_SelectOrCreateRenewalOptions #OrderId
-- Migrate the DiscountCode, UpgradeCode from the renewal record
UPDATE t_Renewal_Option
SET CouponCode = #DiscountCode
WHERE RenewalId = #RenewalId AND OptionType = 0 AND CouponCode IS NULL
UPDATE t_Renewal_Option
SET CouponCode = #UpgradeCode
WHERE RenewalId = #RenewalId AND OptionType = 1 AND CouponCode IS NULL
-- NULL the Renewal record DiscountCode, UpgradeCode
UPDATE t_Renewals
SET DiscountCode = NULL, UpgradeCode = NULL
WHERE RenewalId = #RenewalId
FETCH NEXT FROM rCursor INTO #RenewalId
END
CLOSE rCursor
DEALLOCATE rCursor
END
GO
One thing I noted, that the statement
-- Create renewal options for the t_Renewal entry
EXEC p_SelectOrCreateRenewalOptions #OrderId
returns a set, e.g. if you run this code outside of a stored procedure, inside SQL Server Management Studio, you get several sets of results in the output window. Is this the cause? Is it possible to dump the data as I don't need the returned rows from p_SelectOrCreateRenewalOptions
EDIT: As requested, here is the code for p_SelectOrCreateRenewalOptions
CREATE PROCEDURE p_SelectOrCreateRenewalOptions
(
#OrderId VARCHAR(255) = NULL
)
AS
BEGIN
SET NOCOUNT ON
DECLARE #ProductCode VARCHAR(255);
SELECT #ProductCode = ProductCode FROM t_Order WHERE OrderId = #OrderId;
DECLARE #StaticOptionCount INT;
SET #StaticOptionCount = dbo.f_QueryRenewalOptionCount(#ProductCode);
DECLARE #OptionCount INT;
SELECT #OptionCount = COUNT(*) FROM t_Renewal_Option ro INNER JOIN t_Renewals r ON r.RenewalId = ro.RenewalId WHERE r.OrderId = #OrderId
IF (#OptionCount != #StaticOptionCount)
BEGIN
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
BEGIN TRANSACTION
SELECT #OptionCount = COUNT(*) FROM t_Renewal_Option ro INNER JOIN t_Renewals r ON r.RenewalId = ro.RenewalId WHERE r.OrderId = #OrderId
IF (#OptionCount != #StaticOptionCount)
BEGIN
DECLARE #RenewalId INT;
SELECT #RenewalId = RenewalId FROM t_Renewals r WHERE r.OrderId = #OrderId;
DELETE FROM t_Renewal_Option WHERE RenewalId = #RenewalId;
INSERT INTO t_Renewal_Option (RenewalId, OptionProductCode, OptionType, LastUpdated)
SELECT #RenewalId, sro.OptionProductCode, sro.OptionTypeId, GETDATE()
FROM t_Static_Renewal_Option sro
WHERE sro.ProductCode = #ProductCode
END
COMMIT TRANSACTION
END
SELECT ro.* FROM t_Renewal_Option ro INNER JOIN t_Renewals r ON r.RenewalId = ro.RenewalId WHERE r.OrderId = #OrderId
END
GO
f_QueryRenewalOptionCount simply does a select:
-- f_QueryRenewalOptionCount
SELECT #Count = COUNT(*) FROM t_Static_Renewal_Option o WHERE o.ProductCode = #ProductCode
Triggers wise, I don't think we have any triggers. There is a trigger on the t_Order table on INSERT but apart from that, nothing else.
UPDATE 12-SEP-14:
This 'works' but I don't understand the problem fully. I wrapped the EXEC call into a Begin/End Transaction
Note this script is called once to migrate some old part of the schema to a new part. Its not performance intensive and just has to 'work once'
Do you have a trigger on any of your tables? If an update trigger updates its own table, it will trigger itself again, which will update the table again, etc.
Do you use view or function anywhere in your code? Views can call on other views or functions, which call other views / functions.
The cursor here, while makes my skin crawl, is likely not the cause of the error your seeing. Without being able to see what the nested stored procedure is doing, this is just an educated guess, but that's probably where the issue lies.
The error you're seeing happens whenever a recursive SQL operation basically gets stuck in an infinite loop. The two ways I have had it happen is with a poorly written recursive CTE (basically a table which infinitely unions to itself) or when, as is more likely in this case, a store procedure calls a stored procedure which calls a stored procedure... and so on down the line. For instance (and I haven't tested this) if p_selectOrCreateRenewalOptions called p_MigrateRenewalOptions, you'd probably see a similar error.

How to execute a Stored Procedure on the results of a SQL Server CTE

I have a CTE which returns DISTINCT ID's. I want to execute a scalar function on each of the Id's returned.
WITH cte (reqID) as
(SELECT DISTINCT pol.ReqID FROM
LOG_PackingListItems pli
JOIN
v_PO_LN pol on pol.PO_ID = pli.PoId
WHERE
pli.PackingListHeaderID = 1)
EXEC dbo.spUpdateLOG_ReqCompleteCheck reqID -- Error "Incorrect Syntax near EXEC"
The EXEC line is what I want to make work but I get a syntax error. Not sure if what I want to do is possible or if I do in fact have a syntax error. Any ideas?
EDIT:
I'm adding the code for the Stored Procedure since I am now using a Table-Valued Parameter as suggested by realnumber3012
EDIT:
I have changed my CTE code so it populates a Table-Type as realnumber has suggested. I now get an error when executing spUpdateLOG_ReqCompleteCheck "Subquery returns more than one value."
DECLARE #ReqIdTVP as ReqIdType;
DELETE FROM #ReqIDTVP;
with cte (reqID) as
(select distinct pol.ReqID from
LOG_PackingListItems pli
join
v_PO_LN pol on pol.PO_ID = pli.PoId
where
pli.PackingListHeaderID = #PackingListHeaderID)
INSERT INTO #ReqIdTVP
SELECT * FROM cte
EXEC dbo.spUpdateLOG_ReqCompleteCheck #ReqIdTVP
Sproc code :
Alter PROCEDURE spUpdateLOG_ReqCompleteCheck
(#ReqIdTVP ReqIdType READONLY )
AS
BEGIN
DECLARE #TotalOrd int
DECLARE #TotalRx int
DECLARE #ReqID char(8)
SET #ReqID = (SELECT ReqID FROM #ReqIdTVP)
SET #TotalOrd = (SELECT ISNULL(SUM(ORD_QTY),0)
FROM dbo.v_PoLnNonFreight l
WHERE l.ReqID = #reqID)
SET #TotalRx = (SELECT ISNULL(SUM(TotalRxSite),0)
FROM dbo.v_PoLnNonFreight l
WHERE l.ReqID = #reqID)
IF #TotalRx >= #TotalOrd
BEGIN
DECLARE #curDate datetime
SET #CurDate = ISNULL(#CurDate,GetDate())
SET NOCOUNT ON;
UPDATE LOG_ReqHeader
SET
ReqCompleteDate = #curDate,
ReqStatus = 'Complete'
WHERE ReqID = #ReqID
END
END
Seems that the only thing your stored proc does is to update a logging table: (it only changes state via this statement and doesn't return anything????
UPDATE LOG_ReqHeader
SET
ReqCompleteDate = #curDate,
ReqStatus = 'Complete'
WHERE ReqID = #ReqID
How about splitting the logic out and write a function (inline if possible that will evaluate the condition you are looking for (didn't really understand what you are doind there) -- run the function on the results of the CTE (wrapping it in another CTE if you want) with the CROSS APPLY OPERATOR.
You'd end up with a result set that looks like [ReqId], [UpdateLog] (where updateLog is a BIT)
Then simply do a set based upadete JOINING to the results:
UPDATE l SET
ReqCompleteDate = #curDate,
ReqStatus = 'Complete'
FROM
LOG_ReqHeader AS l
JOIN <CTE> AS c ON c.[ReqID] = l.[ReqID]
WHERE
c.[UpdateLog] = 0x1
Does this make any sense?

Execute a Stored Procedure with parameters from a SELECT

I have an SP which needs to be executed as many times as i find rows in a temporary table (Just like insertion using a select statement)
My table #OutMessageQIDs has id column (with say 10 rows)
The SP HL7_InsertComponentLog requires 5 parameters as following
EXEC [HL7_InsertComponentLog] --#iPraID,#iComponentID,#vComponentType,'Send',id
SELECT #iPrcID,#iComponentID,#vComponentType,'Send',id
FROM #OutMessageQIDs
Can i do it without using Cursor or Loop kind of thing?
EDIT 1: a little more explanation - #OutMessageQIDs is a temporary table storing the ids of items Queued in a QueueTable (in an SP). and the SP HL7_InsertComponentLog logs the Queue-ed items. Depending upon #vComponentType, it logs different type of data.
EDIT 2: SP is as :
Create Procedure [dbo].[HL7_InsertComponentLog]
#IPracID [int],
#iComponentID [bigint],
#vComponentType varchar(50),
#vStatus varchar(200),
#iOutMessageQueueID [bigint]
AS
select* from hl7_outmessagelog
IF #vStatus != 'Success'
BEGIN
SELECT -1
END
ELSE IF #vComponentType = 'LabOrder' OR #vComponentType = 'ProcedureOrder' OR #vComponentType = 'RadiologyOrder'
BEGIN
SELECT -1
END
ELSE IF #vComponentType = 'LabResult' OR #vComponentType = 'ProcedureResult'
BEGIN
INSERT INTO OrderResult_Addendum (iOrderDetailID,IUserID,DateTime_Stamp,iType,VchComments,iOrderID,iPracID,vAction,bAcknowledge)
SELECT NULL,0,dTimeStamp,NULL,NULL,#iComponentID,#iPracID,#vStatus,0
FROM HL7_OutMessageQueue Q
WHERE Q.iOutQueueID = #iOutMessageQueueID and iPracID = #iPracID
END
ELSE IF #vComponentType = 'RadiologyResult'
BEGIN
INSERT INTO OrderResult_Addendum (iOrderDetailID,IUserID,DateTime_Stamp,iType,VchComments,iOrderID,iPracID,vAction,bAcknowledge)
SELECT iOrderDetailID,0,Q.dTimeStamp,NULL,NULL,#iComponentID,#iPracID,#vStatus ,0
FROM HL7_OutMessageQueue Q
INNER JOIN OrderResultDetails det ON Q.iComponentID = det.iOrderID
WHERE Q.iOutQueueID = #iOutMessageQueueID and Q.iPracID = #iPracID
END
ELSE IF #vComponentType = 'ClinicalNotes'
BEGIN
INSERT INTO Note_provider_encounter(iReportID,iUserID,iComponentID,dEncounterDate,vaction)
SELECT #iComponentID,0,0,dTimeStamp,#vStatus
FROM HL7_OutMessageLog Where iOutMessageLogID = #iOutMessageQueueID and iPracID = #iPracID
END
ELSE IF #vComponentType = 'PatientDemo'
BEGIN
DECLARE #IPatID int
DECLARE #IUserID int
SELECT #IPatID = iPatID,#IUserID = iUserID
FROM HL7_OutMessageQueue Q
WHERE Q.iOutQueueID = #iOutMessageQueueID and iPracID = #iPracID
EXEC [dbo].[InsertPatientLog] #IPracID,#IPatID,#vStatus,#IUserID
END
No you can't EXEC a stored procedure for each row in a table without using a loop of some kind.
What does your stored procedure do? It may be possible to extract the logic from that and perform it in a set based manner against the whole table.
e.g. Just use
INSERT INTO ComponentLog
SELECT #iPrcID,#iComponentID,#vComponentType,'Send',id
FROM #OutMessageQIDs
instead of calling the stored procedure for each row.