Why is my sql code throwing errors - sql-server

Here's the concept: when a user selects a user list from the frontend treeview and clicks the Save button, the table which contains the mapping for role to users must clear itself of all the users with the role and re insert records with the given role id and userlist.
I am using following stored procedure and function:
Stored procedure:
ALTER PROCEDURE [dbo].[AssignRoleToUser]
#RoleID INT = 0,
#UserID varchar(max) = ''
AS
BEGIN
delete from UserRole
where RoleID = #RoleID
AND UserID IN (SELECT UserID FROM UserRole)
INSERT INTO UserRole(UserID, RoleId)
SELECT id, #RoleID
FROM dbo.CSVToTable(#UserID)
END
Function
CREATE FUNCTION [dbo].[CSVToTable] (#InStr VARCHAR(MAX))
RETURNS #TempTab TABLE (id int not null)
AS
BEGIN
;-- Ensure input ends with comma
SET #InStr = REPLACE(#InStr + ',', ',,', ',')
DECLARE #SP INT
DECLARE #VALUE VARCHAR(1000)
WHILE PATINDEX('%,%', #INSTR ) <> 0
BEGIN
SELECT #SP = PATINDEX('%,%',#INSTR)
SELECT #VALUE = LEFT(#INSTR , #SP - 1)
SELECT #INSTR = STUFF(#INSTR, 1, #SP, '')
INSERT INTO #TempTab(id) VALUES (#VALUE)
END
RETURN
END
But I get this error
Msg 217, Level 16, State 1, Procedure AssignRoleToUser, Line 7
Maximum stored procedure, function, trigger, or view nesting level exceeded (limit 32).

Error is exactly saying what went wrong as below, along with the line number.
Msg 217, Level 16, State 1, Procedure AssignRoleToUser, Line 7
Take out the line 7 from your procedure AssignRoleToUser
INSERT INTO UserRole(UserID, RoleId) SELECT id, #RoleID
FROM dbo.CSVToTable(#UserID)
As can be seen, it's calling another function named dbo.CSVToTable. Likewise dbo.CSVToTable may be calling some other procedure\function and that's how the nesting depth has reached to maximum limit. so the error is.
Also, In Line 6 in your procedure AND UserID IN (Select UserID from UserRole) is not needed. which makes it as below
delete from UserRole where RoleID = #RoleID
You can run the below query which will list out all procedure that gets called in chain when running AssignRoleToUser procedure (Query observed from Get All Nested Stored Procedures)
SELECT * FROM
(SELECT NAME AS ProcedureName, SUBSTRING(( SELECT ', ' + OBJDEP.NAME
FROM sysdepends
INNER JOIN sys.objects OBJ ON sysdepends.ID = OBJ.OBJECT_ID
INNER JOIN sys.objects OBJDEP ON sysdepends.DEPID = OBJDEP.OBJECT_ID
WHERE obj.type = 'P'
AND Objdep.type = 'P'
AND sysdepends.id = procs.object_id
ORDER BY OBJ.name
FOR
XML PATH('')
), 2, 8000) AS NestedProcedures
FROM sys.procedures procs )InnerTab
WHERE NestedProcedures IS NOT NULL
AND NAME = 'AssignRoleToUser'

The error says that you have a call to a procedure, function, or trigger which calls another one which calls another one, etc etc, until the call is 32 calls deep. At this point it's giving and saying 'too much!'.
Do you have any triggers on the UserRole table? Will they call other things? If so, what will they do? And so on...

Related

How to get the information that the update was used in the trigger?

I'm creating a trigger that gets the information when the user deletes or modifies the payment method insert the D for deleted and M for modified, the is being used in the trigger o for DELETE, update.
CREATE TRIGGER [dbo].[Trg_forpag_Drop]
on [dbo].[CUPONS2]
for DELETE, update
as
BEGIN
DECLARE #seqecf INT,
#seqecf2 INT,
#CODFORPAG INT,
#NOMOPERADORES INT,
#isDelivery char(1),
#Data datetime,
#status char(1)
SELECT #seqecf = deleted.SEQECF, #CODFORPAG = deleted.CODFORPAG FROM DELETED
SELECT #nomoperadores = codope, #isDelivery = cupdelivery FROM CUPONS WHERE SEQECF = #SEQECF
if #isDelivery = 'S' AND EXISTS (SELECT * FROM DELETED)
insert into forpagdel (seqecfforpagdel, codope, datforpagdel, codforpagdel, movstat) select deleted.seqecf, #nomoperadores, getDate(), deleted.codforpag, 'D' from DELETED
else IF exists (SELECT * FROM INSERTED)
insert into forpagdel (seqecfforpagdel, codope, datforpagdel, codforpagdel, movstat) select seqecf, #nomoperadores, getDate(), codforpag, 'M' from update
END
And the return of SQL is this:
Msg 156, Level 15, State 1, Procedure Trg_forpag_Drop, Line 24 [Batch 7 Start Line]
Incorrect syntax next to the 'update' keyword.
Is it possible to get the information when the update is used?
Note: I tested it with inserted, but it ends up NULL the codope field because its select comes from a deleted

Invalid Object Name for Temp Table in Stored Procedure

I'm trying to use a temp table to update a database table in a stored procedure.
My client was throwing an error about an illegal null value, so I tried testing within SSMS and it told me that the name of the temp table was invalid (regardless of what I named it).
If I run the beginning and change the INSERT INTO SERVICE SELECT S.* to simply SELECT S.* and run the code after Declaring and Defining #OldServicesString (so I can leave out the ALTER/CREATE PROCEDURE line) it runs exactly as expected.
Here's the beginning of the SP:
ALTER PROCEDURE [dbo].[app_CreateNewServicesOldFSD] (#OldServicesStr nvarchar(max)) AS
SET NOCOUNT ON
DECLARE #User char(10) = (SELECT TOP 1 CREATED_BY_USER FROM BATCHLOG WHERE BPROCESS_ID = 3 ORDER BY ID DESC);
DECLARE #LastOldService int = (SELECT MAX(ID) FROM SERVICE);
SELECT TOP 0 * INTO #Service FROM SERVICE;
ALTER TABLE #Service
DROP COLUMN RECNUM;
INSERT INTO #Service exec dbo.app_NewServicesOldFSD
;WITH cteOldFSDsToCreate AS (
SELECT JobID.Item JobID, CONVERT(date,ServDate.Item,103) ServDate
FROM dbo.SplitStringToTable(#OldServicesStr,',',2) JobID
INNER JOIN dbo.SplitStringToTable(#OldServicesStr,',',2) ServDate ON JobID.Rw = ServDate.Rw AND JobID.Cl = 0 AND ServDate.Cl = 1
)
INSERT INTO SERVICE SELECT S.*
FROM #Service S
INNER JOIN cteOldFSDsToCreate N ON N.JobID = S.JOB_ID AND N.ServDate = S.DATE
DROP TABLE #Service
A useable and likely #OldServicesStr could be '11428,23/07/2019,11429,23/07/2019,15186,5/10/2019'
To test it in SSMS I opened a new Query and typed in
exec app_CreateNewServicesOldFSD '11428,23/07/2019,11429,23/07/2019,15186,5/10/2019'
And got the following error:
Warning: Null value is eliminated by an aggregate or other SET operation.
Msg 208, Level 16, State 0, Procedure app_CreateNewServicesOldFSD, Line 65 [Batch Start Line 7]
Invalid object name '#Service'.
Completion time: 2020-11-20T20:36:57.1356921+11:00
I know that is not Your case but in the sake of not forget, there is also a limitation using Biztalk WCF-Custom adapter with SQL Bindings and temp tables.
According to this site :
http://thoughtsofmarcus.blogspot.com/2010/11/calling-stored-procedures-from-biztalk.html
You need to SET FMTONLY OFF before creating temp tables and inserting into them and
SET FMTONLY ON before selecting from them as in example from below.
ALTER PROCEDURE [dbo].[FetchTestData]
(#a4 varchar(4))
AS
DECLARE #FmtOnlyIsSet bit = 0
IF (1=0) BEGIN SET #FmtOnlyIsSet = 1 END
IF #FmtOnlyIsSet = 1
SET FMTONLY OFF
SELECT t1, t2, t3 INTO #temp01 FROM Table_1
IF #FmtOnlyIsSet IS NULL
SET FMTONLY ON
SELECT t1, t2, t3 FROM #temp01
RETURN

T-SQL different result between code in stored and same code in query pane

I'm working on a procedure that should return a o or a 1, depending on result from parameter calculation (parameters used to interrogate 2 tables in a database).
When I excute that code in a query pane, it gives me the results i'm expecting.
code looks like:
SELECT TOP 1 state, updDate INTO #history
FROM [xxx].[dbo].[ImportHystory] WHERE (db = 'EB') ORDER BY addDate DESC;
IF (SELECT state FROM #history) = 'O'
BEGIN
SELECT TOP 1 * INTO #process_status
FROM yyy.dbo.process_status WHERE KeyName = 'eb-importer';
IF(SELECT s.EndDate FROM #process_status s) IS NOT NULL
IF (SELECT s.EndDate FROM #process_status s) > (SELECT h.updDate FROM #history h)
BEGIN
IF (SELECT MessageLog from #process_status) IS NOT NULL SELECT 1;
ELSE SELECT 0;
END
ELSE
SELECT 1;
ELSE
SELECT 1;
END
ELSE
SELECT 0
I'm in the situation where EndDate from #process_status is null, so the execution returns 1.
Once i put the SAME code in a SP, and pass 'EB' and 'eb-importer' as parameters, it returns 0.
And I exec the procedure with the data from the table right in front of me, so i know for sure that result is wrong.
Inside the procedure:
ALTER PROCEDURE [dbo].[can_start_import] (#keyName varchar, #db varchar, #result bit output)
DECLARE #result bit;
and replace every
SELECT {0|1}
with
SELECT #result = {0|1}
Executed from the Query pane:
DECLARE #result bit;
EXEC [dbo].[can_start_import] #KeyName = 'eb-importer', #db = 'EB', #result = #result OUTPUT
SELECT #result AS N'#result'
Why does this happen?
You are doing a top(1) query without an order by. That means SQL Server can pick any row from table1 that matches the where clause.
If you want to guarantee that the result is the same every time you execute that code you need an order by statement that unambiguously orders the rows.
So, apparently 2 things needed to be done:
set the length of the varchar parameter with a higher length,
filter with ' like ' instead of ' = ' for god knows what reason
Now it work as i expected to do, but i still don't get the different results between the query pane and the procedure if i use the equal...

How can I include a Temp Table in a SQL Function

I have a SQL Server 2014 server linked to an Oracle server. I want to use a temp table in a function to return a dataset from the Oracle database and then use the my function to return results using regular T-SQL. Since I am rather new to this I am close but am getting an error message
Msg 156, Level 15, State 1, Procedure GetBond, Line 37
Incorrect syntax near the keyword 'BEGIN'.
I have posted the function code here:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER FUNCTION [dbo].[GetBond]
(#WarControlID bigint)
RETURNS VARCHAR(MAX)
AS
BEGIN
--Create Temp Table
declare #TSQL varchar(max)
DECLARE #WarrantBail table
(
WR_INVL varchar(5),
WR_WARR_CTL VarChar(10),
WR_Bail VarChar(50),
WC_BAIL VarChar(50)
)
SELECT #TSQL = 'SELECT * FROM OPENQUERY(RMSPROD2,''SELECT TIBURON.WRMAST.WR_INVL, TIBURON.WRMAST.WR_WARR_CTL,TIBURON.WRMAST.WR_BAIL,TIBURON.WRWCHG.WC_BAIL
FROM TIBURON.WRMAST
LEFT JOIN TIBURON.WRWCHG ON WRWCHG.WC_WR_CHAIN = WRMAST.WRMAST_ROW
WHERE TIBURON.WRMAST.WR_WARR_CTL = ''''' + #WarControlID + ''''''')'
INSERT INTO #WarrantBail
EXEC (#TSQL)
END
BEGIN
-- Create a Variable
DECLARE #NoBailCount int
DECLARE #ChgCount int
DECLARE #WarTotalBond float
DECLARE #CHGTotalBond float
DECLARE #War_Final_Bail varchar(max)
Select COUNT(DISTINCT w.WR_Bail) AS NoBond_Count
From #WarrantBail w
Where w.WC_BAIL In ('No Bond', 'No Bail','None') Or w.WR_Bail In ('No Bond', 'No Bail','None')
--***********Get Charge Count
Select COUNT(w.WC_BAIL) As ChgCount FROM #WarrantBail w
--******************IF the above fails then we have a bond check the Warrant bond amount
Select SUM (DISTINCT cast(w.WR_Bail As int)) AS WAR_Bond_Total
From #WarrantBail w
Where w.WR_Bail Not In ('No Bond', 'No Bail','None')
--****************We may have additional charges get the total for those charges
Select SUM (cast(w.WC_BAIL As int)) AS CHG_BondTotal
From #WarrantBail w
Where w.WC_BAIL Not In ('No Bond', 'No Bail','None')
IF (#NoBailCount > 0)
Begin
SET #War_Final_Bail = 'NO BAIL'
End
ELSE IF #ChgCount > 0
Begin
SET #War_Final_Bail = #WarTotalBond + #CHGTotalBond
End
Else
Begin
SET #War_Final_Bail = #WarTotalBond
End
RETURN CONVERT(varchar(max), #War_Final_Bail)
END
In addition to the Error when I Execute the code I am also seeing a squiggly line under the Line "ALTER FUNCTION [dbo].[GetBond]
That error states:
Incorrect syntax: 'ALTER FUNCTION' must be the only statement in this batch.
Does this error mean I cannot create a temp table in the function?
Why do you have an END and BEGIN here? Think this is likely (one of) your problem(s).
Insert Into #WarrantBail
EXEC (#TSQL)
END
BEGIN
-- Create a Variable
DECLARE #NoBailCount int
Do you absolutely have to use dynamic SQL? Why not do something like this...
INSERT INTO #WarrantBail
SELECT *
FROM OPENQUERY(RMSPROD2, '
SELECT TIBURON.WRMAST.WR_INVL,
TIBURON.WRMAST.WR_WARR_CTL,
TIBURON.WRMAST.WR_BAIL,
TIBURON.WRWCHG.WC_BAIL
FROM TIBURON.WRMAST
LEFT JOIN TIBURON.WRWCHG ON WRWCHG.WC_WR_CHAIN = WRMAST.WRMAST_ROW
WHERE TIBURON.WRMAST.WR_WARR_CTL = ' + CAST(#WarControlID AS VARCHAR(30)) + ')')
Thanks for your comments I am a junior developer and am just getting my first taste of SQL programming after a discussion with one of the senior developers I was able to complete the task by using a Stored Procedure.
Thank you for your comments.

Already an object named in the database issue

I am trying to execute an email that contains the results of a stored procedure. I was looking at other posts in stackoverflow but cannot seem to get past an error that states "Msg 2714, Level 16, State 1, Procedure CompareConfirm_FraudRules, Line 38
There is already an object named '##returnInactiveRules' in the database." I've looked in the DB and there is no object that exists with this name already. Any suggestions on how to fix this issue would be appreciated. Thanks.
Here is the my SP:
BEGIN
CREATE TABLE ##returnInactiveRules (
ProductName varchar(100),
ChannelName varchar(100),
StrategyCode varchar(100),
StrategyName varchar(100),
RuleCode varchar(100),
RuleName varchar(100),
On_Off varchar(5)
);
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
-- SELECT R.RuleCode, R.Name as RuleName, S.StrategyCode, S.Name as StrategyName, R.IsActive
SELECT DISTINCT
CASE
WHEN PC.ProductIdentifier='2000' THEN 'GP'
WHEN PC.ProductIdentifier='1000' THEN 'MB'
END as ProductName, C.Name as ChannelName, S.StrategyCode, S.Name as StrategyName, R.RuleCode, R.Name as RuleName,
CASE
WHEN R.IsActive = 1 THEN 'On'
WHEN R.IsActive = 0 THEN 'Off'
END as On_Off
INTO ##returnInactiveRules
FROM dbo.[Rule] R
INNER JOIN dbo.Strategy S
on S.KnockoutRuleSet = R.KnockoutRuleSet
INNER JOIN dbo.RFAI P
on R.RFAIId = P.RFAIId
INNER JOIN dbo.DecisionStatus D
on D. StatusId = R. StatusId
LEFT OUTER JOIN dbo.NOAA N
on N.NOAAId = R.NOAAId
INNER JOIN dbo.RuleQuestionsXRef Q
ON Q.RuleId = R.RuleId
INNER JOIN ProductChannelStrategyRuleXref X
ON X.RuleId = R.RuleId
INNER JOIN ProductChannelStrategy CS
ON CS.ProductChannelStrategyId = X.ProductChannelStrategyId
INNER JOIN ProductChannel PC
ON PC.ProductChannelId = CS.ProductChannelId
INNER JOIN dbo.Channel C
ON C.ChannelId = PC.ChannelId
WHERE R.IsActive = 0
AND R.RuleCode IN ('F06',
'F07',
'F11',
'F12',
'F14',
'F15',
'F16',
'F17',
'F19',
'F23',
'F25',
'F26',
'F10'
)
-- ORDER BY R.RuleCode, R.Name;
ORDER BY ProductName, C.Name, S.StrategyCode, S.Name, R.RuleCode, R.Name;
-- SELECT * FROM #returnValue;
-- Email the results in the #returnValue table --
EXEC msdb.dbo.sp_send_dbmail
#execute_query_database='Prod-XXX',
#recipients=N'msavoy#xxx.com',
#body='Attached please find a file with the results.',
#subject ='Compare Fraud Rule Results',
#profile_name ='Reports',
#query ='EXEC CompareConfirm_Rules',
#attach_query_result_as_file = 1,
#query_attachment_filename ='CompareRuleResults.txt'
END
DROP TABLE ##returnInactiveRules;
GO
If exists Drop it, then create the table
IF Object_id('tempdb..##returnInactiveRules') IS NOT NULL
DROP TABLE ##returnInactiveRules
CREATE TABLE ##returnInactiveRules
(
ProductName VARCHAR(100),
ChannelName VARCHAR(100),
StrategyCode VARCHAR(100),
StrategyName VARCHAR(100),
RuleCode VARCHAR(100),
RuleName VARCHAR(100),
On_Off VARCHAR(5)
);
Temp tables which start with ## are global temp tables, so if you have 2 connection opened, and one of them created a table called ##Temp, you would not be able to create the same table from connection 2, until it is dropped by any of the 2 connections.
Best thing would be to use #returnInactiveRules and check for existence before creating it.
IF OBJECT_ID('tempdb..#returnInactiveRules') IS NOT NULL
DROP TABLE #returnInactiveRules
GO
Have the drop statement at the beginning and if it's a global temp table ensure that only one process is creating it at any one time as two cannot exist simultaneously..
Also, unless you require other sessions to access the data, consider using a temp table rather than a global temp table by replacing the ## with #

Resources