Why SQL2008 debugger would NOT step into a certain child stored procedure - sql-server-2008

I'm encountering differences in T-SQL with SQL2008 (vs. SQL2000) that are leading me to dead-ends. I've verified that the technique of sharing #TEMP tables between a caller which CREATES the #TEMP and the child sProc which references it remain valid in SQL2008 See recent SO question.
My core problem remains a critical "child" stored procedure that works fine in SQL2000 but fails in SQL2008 (i.e. a FROM clause in the child sProc is coded as: SELECT * FROM #AREAS A) despite #AREAS being created by the calling parent. Rather than post snippets of the code now, here is another symptom that may help you suggest something.
I fired up the new debugger in SQL Mgmt Studio:
EXEC dbo.AMS1 #S1='06',#C1='037',#StartDate='01/01/2008',#EndDate='07/31/2008',#Type=1,#ACReq = 1,#Output = 0,#NumofLines = 30,#SourceTable = 'P',#LoanPurposeCatg='P'
This is a very large sProc and the key snippet that is weird is the following:
create table #Areas
(
State char(2)
, County char(3)
, ZipCode char(5) NULL
, CityName varchar(28) NULL
, PData varchar(3) NULL
, RData varchar(3) NULL
, SMSA_CD varchar(10) NULL
, TypeCounty varchar(50)
, StateAbbr char(2)
)
EXECUTE dbo.AMS_I_GetAreasV5 -- this child populates #Areas
#SMSA = #SMSA
, #S1 = #S1
, #C1 = #C1
, #Z1 = #Z1
, #SourceTable = #SourceTable
, #CustomID = #CustomID
, #UserName = #UserName
, #CityName = #CityName
, #Debug=0
EXECUTE dbo.AMS_I_GetAreas_FixAC -- this child cannot reference #Areas (in 2008 only!)
#StartDate = #StartDate -- secondarily, I cannot STEP INTO this sProc
, #EndDate = #EndDate
, #SMSA_CD = #SMSA_CD
, #S1 = #S1
, #C1 = #C1
, #Z1 = #Z1
, #CityName = #CityName
, #CustomID = #CustomID
, #Debug=0
-- continuation of the parent sProc**
I can step through the execution of the parent stored procedure. When I get to the first child sproc above, I can either STEP INTO dbo.AMS_I_GetAreasV5 or STEP OVER its execution. When I arrive at the invocation of the 2nd child sProc - dbo.AMS_I_GetAreas_FixAC - I try to STEP INTO it (because that is where the problem statement is) and STEP INTO is ignored (i.e. treated like STEP OVER instead; yet I KNOW I pressed F11 not F10). It WAS executed however, because when control is returned to the statement after the EXECUTE, I click Continue to finish execution and the results windows shows the errors in the dbo.AMS_I_GetAreas_FixAC (i.e. the 2nd child) stored procedure.
Is there a way to "pre-load" an sProc with the goal of setting a breakpoint on its entry so that I can pursue execution inside it?
In summary, I wonder if the inability to step into a given child sproc might be related to the same inability of this particular child to reference a #temp created by its parent (caller).

I found that one of the called stored procedures (i.e. the children) apparently had
SET NOCOUNT OFF
for some long-forgotten reason.
Apparently, the behavior of things in SQL 2008 is quite different when this is in effect.
I ended up identifying all lines like the above in all sProcs, changing them to
SET NOCOUNT ON
..and also, I checked NOCOUNT at the instance level. This fixed these very strange problems.

Related

want to Write a Stored Procedure in MYSQL [duplicate]

I have made a stored procedure. I want it to filter the data by different parameters. If I pass one parameter, it should be filtered by one; if I pass two, it should be filtered by two, and so on, but it is not working.
Can anyone help me please?
DROP PROCEDURE IF EXISTS medatabase.SP_rptProvince2;
CREATE PROCEDURE medatabase.`SP_rptProvince2`(
IN e_Region VARCHAR(45)
)
BEGIN
DECLARE strQuery VARCHAR(1024);
DECLARE stmtp VARCHAR(1024);
SET #strQuery = CONCAT('SELECT * FROM alldata where 1=1');
IF e_region IS NOT NULL THEN
SET #strQuery = CONCAT(#strQuery, ' AND (regionName)'=e_Region);
END IF;
PREPARE stmtp FROM #strQuery;
EXECUTE stmtp;
END;
AFAIK, you can't have a variable argument list like that. You can do one of a couple of things:
Take a fixed maximum number of parameters, and check them for null-ness before concatenating:
CREATE PROCEDURE SP_rptProvince2(a1 VARCHAR(45), a2 VARCHAR(45), ...)
...
IF a1 IS NOT NULL THEN
SET #strQuery = CONCAT(#strQuery, ' AND ', a2);
END IF;
If you need predetermined fields to which the criteria in the argument apply (like the e_Region parameter in your existing code), then you modify the CONCAT operation appropriately.
Possible invocation:
CALL SP_rptProvince2('''North''', 'column3 = ''South''')
Take a single parameter that is much bigger than just 45 characters, and simply append it to the query (assuming it is not null).
Clearly, this places the onus on the user to provide the correct SQL code.
Possible invocation:
CALL SP_rptProvince2('RegionName = ''North'' AND column3 = ''South''')
There's not a lot to choose between the two. Either can be made to work; neither is entirely satisfactory.
You might note that there was a need to protect the strings in the arguments with extra quotes; that is the sort of thing that makes this problematic.
I found a JSON-based approach which works with the latest MySQL/MariaDB systems. Check the link below (Original Author is Federico Razzoli): https://federico-razzoli.com/variable-number-of-parameters-and-optional-parameters-in-mysql-mariadb-procedures
Basically, you take a BLOB parameter which is actually a JSON object and then do JSON_UNQUOTE(JSON_EXTRACT(json object, key)) as appropriate.
Lifted an extract here:
CREATE FUNCTION table_exists(params BLOB)
RETURNS BOOL
NOT DETERMINISTIC
READS SQL DATA
COMMENT '
Return whether a table exists.
Parameters must be passed in a JSON document:
* schema (optional). : Schema that could contain the table.
By default, the schema containing this procedure.
* table : Name of the table to check.
'
BEGIN
DECLARE v_table VARCHAR(64)
DEFAULT JSON_UNQUOTE(JSON_EXTRACT(params, '$.table'));
DECLARE v_schema VARCHAR(64)
DEFAULT JSON_UNQUOTE(JSON_EXTRACT(params, '$.schema'));
IF v_schema IS NULL THEN
RETURN EXISTS (
SELECT TABLE_NAME
FROM information_schema.TABLES
WHERE
TABLE_SCHEMA = SCHEMA()
AND TABLE_NAME = v_table
);
ELSE
RETURN EXISTS (
SELECT TABLE_NAME
FROM information_schema.TABLES
WHERE
TABLE_SCHEMA = v_schema
AND TABLE_NAME = v_table
);
END IF;
END;

stored procedure broke in release mySQL 8.0.22

With the release of mySQL 8.0.22, this stored procedure
CREATE DEFINER=`workbench`#`%` PROCEDURE `loopPIDCreate`( IN startPID VARCHAR(4) , IN i INTEGER)
SET #cpid = startPID
WHILE ( i > 0 ) DO
SET #PPPX = (SELECT MID(#cpid,4,1));
SET #NNNX = (SELECT `Next` FROM `f930_nonsalespidsequence` WHERE `Current` = #PPPX);
....[other processing ]
SET #newpid = (SELECT CONCAT(#XNNN,#NXNN,#NNXN,#NNNX));
INSERT INTO `e1p1`.`f940_newpidtable` (SELECT #newpid);
SET cpid = newpid;
END WHILE;
END
would stop updating values after the first pass through (i on its second and subsequent passes as it gets decremented toward 0).
To quote Fred Willard from A Mighty Wind, "Hey, what happened?"
This is a direct result of optimizations introduced in mySQL 8.0.22. In particular, "a statement inside a stored procedure is also now prepared only once" apparently applies to session variables as well. They get initialized and evaluated only once, and in every subsequent iteraton they are null (or otherwise undefined). Update and working version looks like:
BEGIN
DECLARE PPPX CHAR(4); -- 20201112 New Optimization 8.0.22 requires local instead of session (#var) variables
DECLARE PPXP CHAR(4);
DECLARE PXPP CHAR(4);
DECLARE XPPP CHAR(4);
[...other processing]
SET NNNX = (SELECT `Next` FROM `f930_nonsalespidsequence` WHERE `Current` = PPPX);
-- note the loss of '#', from session to local with matching DECLARE up top
The solution is to use properly DECLARE-ed local variables in the stored procedure. With more experience, I would have avoided this pratfall. Session variables work in stored procedures but were never intended to be local variables. The mySQL optimizer finally caught up to this fact with this latest release.
A short and useful reference on local variables in stored procedures is here.

SQL Server performance issue: Why simple SQL statements and a UD scalar-valued function appear to be expensive

We've been experiencing some severe performance issues on our SQL 2008 r2 DB. When we run the Activity Monitor in SQL Server Management Studio, and a SP that returns who/what is active, it shows the following three transactions as being very expensive:
Query 1:
SET #DateMonth = '0' + #DateMonth
Query 2:
SET #DateMonth = CAST(datepart(mm, #TheDate) AS VARCHAR(2))
Function:
CREATE FUNCTION [dbo].[DateToNGDate] (#TheDate datetime)
RETURNS VARCHAR(10)
AS
BEGIN
DECLARE #DateYear VARCHAR(4)
DECLARE #DateMonth VARCHAR(2)
DECLARE #DateDay VARCHAR(2)
SET #DateYear = CAST(datepart(yyyy, #TheDate) AS VARCHAR(4))
SET #DateMonth = CAST(datepart(mm, #TheDate) AS VARCHAR(2))
IF (LEN (#DateMonth) = 1) SET #DateMonth = '0' + #DateMonth
SET #DateDay = CAST(datepart(dd, #TheDate) AS VARCHAR(2))
IF (LEN (#DateDay) = 1) SET #DateDay = '0' + #DateDay
RETURN #DateYear+#DateMonth+#DateDay
END
That last one comes back like that, but i'm pretty sure it isn't creating the function (it already exists), rather it is just running it. It is also the one that comes up the most as appearing to be a performance killer (it's used throughout our code).
I'm sure these aren't what is actually causing the problem, but why would they appear as they are?
Scalar-valued functions are a known performance issue in SQL Server. One option is to define your function like this:
CREATE FUNCTION [dbo].[DateToNGDateX] (#TheDate datetime)
RETURNS table as return
(
select
cast(
CAST(datepart(yyyy, #TheDate) AS VARCHAR(4)) -- DateYear
+ right('0' + CAST(datepart(mm, #TheDate) AS VARCHAR(2)),2) -- DateMonth
+ right('0' + CAST(datepart(dd, #TheDate) AS VARCHAR(2)),2) -- DateDay
as varchar(30)) as Value
)
and reference it like this:
select Value from [dbo].[DateToNGDateX] ('20140110');
However, for the specific functionality you desire, test this as well:
select convert(char(8), cast('20140110' as date), 112);
which will return a date formatted as yyyymmdd.
Scalar UDFs are very slow if your select statement is returning a lot of rows, because they are going to be executed once for every row that is returned. Maybe you can rewrite as an inline function as per this link inline UDFs
I like Pieter Geerkens answer and I hope it has resolved your problem. The scalar function will return results for each of the matching rows wherever it is being used. So it is something similar to cursors. A table valued function will accept a set and return a set.
To verify the I\O changes between both processes, management studio might not help but on your own machine you can put a small trace to see what magnitude of performance you get.
Cheers!!

Determine what param to use in Select statement in a Stored Procedure

I have a stored procedure that returns a common query, I need to call it in several functions but some functions may call it through Period Id or others through Header Id, so far I would like to know how can I determine what param to use in order to retrive data properly, I have something like this implemented.
CREATE PROCEDURE dbo.GetTFDRecordInfo
#PeriodId int = null,
#HeaderId int = null
AS
BEGIN
SELECT
-- I have a lot more fields and joins here, that's why I need to get the statement in a single call through either period id or header id
*
From NT_CSRTNVPeriodInfo t
-- how can I make possible something like shown above, can I use a "Case When"?
Where (
/*
if #PeriodId is null
Where t.HeaderId = #HeaderId
if #HeaderId is null
Where t.PeriodId = #PeriodId
*/
)
END
GO
-- swtich between params
Exec NT_CSRTNVPeriodInfo null, 2654
Exec NT_CSRTNVPeriodInfo 196, null
This is the answer:
CREATE PROCEDURE dbo.GetTFDRecordInfo
#PeriodId int = null,
#HeaderId int = null
AS
BEGIN
SELECT
-- I have a lot more fields and joins here, that's why I need to get the statement in a single call through either period id or header id
*
From NT_CSRTNVPeriodInfo t
-- how can I make possible something like shown above, can I use a "Case When"?
Where ((#PeriodId IS NULL) or (t.PeriodId = #PeriodId))
And ((#HeaderId IS NULL) or (t.HeaderId = #HeaderId))
END
GO
You have to use conditional OR to check NULLs, if param is set, the second condition is checked, if not, the procedure will consider always true the statement and go to the next.

Optional parameters in SQL Server stored procedure

I'm writing some stored procedures in SQL Server 2008. Is the concept of optional input parameters possible here?
I suppose I could always pass in NULL for parameters I don't want to use, check the value in the stored procedure, and then take things from there, but I was interested if the concept is available here.
You can declare it like this:
CREATE PROCEDURE MyProcName
#Parameter1 INT = 1,
#Parameter2 VARCHAR (100) = 'StringValue',
#Parameter3 VARCHAR (100) = NULL
AS
/* Check for the NULL / default value (indicating nothing was passed) */
if (#Parameter3 IS NULL)
BEGIN
/* Whatever code you desire for a missing parameter */
INSERT INTO ........
END
/* And use it in the query as so */
SELECT *
FROM Table
WHERE Column = #Parameter
Yes, it is. Declare the parameter as so:
#Sort varchar(50) = NULL
Now you don't even have to pass the parameter in. It will default to NULL (or whatever you choose to default to).
In SQL Server 2014 and above at least, you can set a default, and it will take that and not error when you do not pass that parameter.
Partial example: the third parameter is added as optional. Execution (exec) of the actual procedure with only the first two parameters worked fine.
exec getlist 47,1,0
create procedure getlist
#convId int,
#SortOrder int,
#contestantsOnly bit = 0
as
The default mentioned in previous answers only works for simple cases. In more complicated cases, I use an IF clause near the beginning of the stored procedure to provide a value, if the parameter is NULL or empty and calculations are required.
I often use optional parameters in the WHERE clause, and discovered that SQL does not short circuit logic, so use a CASE statement to make sure not to try to evaluate NULL or empty dates or unique identifiers, like so:
CREATE Procedure ActivityReport
(
#FromDate varchar(50) = NULL,
#ToDate varchar(50) = NULL
)
AS
SET ARITHABORT ON
IF #ToDate IS NULL OR #ToDate = '' BEGIN
SET #ToDate = CONVERT(varchar, GETDATE(), 101)
END
SELECT ActivityDate, Details
FROM Activity
WHERE
1 = CASE
WHEN #FromDate IS NULL THEN 1
WHEN #FromDate = '' THEN 1
WHEN ActivityDate >= #FromDate AND ActivityDate < DATEADD(DD,1,#ToDate) THEN 1
ELSE 0
END