Cursor For Volatile Table in Teradata - cursor

I have a volatile table (say, vtTempTableForPI) that I am creating inside a procedure. Once the volatile table gets created, I am inserting rows into it.
Once I have records in the volatile table, I need to take MAX(ModifiedDatetime) from this volatile table (vtTempTableForPI) into a local variable. However, I am unable to find a way to do so.
Here is a piece of code from my procedure for some context:
SELECT MAX(ModifiedDatetime)
FROM vtTempTableForPI
INTO lvMaxUpdateDateTime
WHERE Template = 'Schedule_Stream'
;
And then I want to use this local variable to insert into another volatile table, like below:
SET lvQuery = '
INSERT INTO vtScheduleVersionUpdatedTime
SELECT
''Schedule_Stream''
, ''' || CAST(lvMaxUpdateDateTime AS VARCHAR(19)) || '''
';
EXECUTE IMMEDIATE lvQuery;
I can't use this cursor on volatile table since its definition will not be present in DBC when we compile the procedure, so it will give an error. If I try to use dynamic query in order to avoid this error, I run into another error like below:
SET lvMaxModifiedDateQuery = '
SELECT MAX(ModifiedDatetime)
FROM vtTempTableForPI
WHERE Template = ''Schedule_Tank''
';
PREPARE stMaxModifiedDateQuery03 FROM lvMaxModifiedDateQuery;
OPEN crGetMaxModifiedDate03;
FETCH crGetMaxModifiedDate03 INTO lvMaxUpdateDateTime;
--CLOSE crGetMaxModifiedDate;
SET lvQuery = '
INSERT INTO vtScheduleVersionUpdatedTime
SELECT
''Schedule_Tank''
, ''' || CASt(lvMaxUpdateDateTime AS VARCHAR(19)) || '''
';
EXECUTE IMMEDIATE lvQuery;
Below is the error that I am getting:
Failure occured while Creating Dynamic Query
SQL State:T7688,
SQL Code:7688,
SQL SESSION: 252898254,
Execution Start Time:2019-01-13 21:44:44,
Execution End Time:2019-01-13 21:44:54,
ERROR Message: Error occurred generating Evl code for dynamic fetch.
Need help!

What's your Teradata release? This is working fine for me:
REPLACE PROCEDURE SP_test(
IN v_CALC_BASIS VARCHAR(100))
BEGIN
DECLARE lvMaxUpdateDateTime INT;
DECLARE v_LogStmt VARCHAR(5000);
SELECT Max(ModifiedDatetime)
FROM vtTempTableForPI
INTO lvMaxUpdateDateTime
WHERE Template = 'Schedule_Stream'
;
END;
CALL SP_test ('bla');
CALL Failed. [3807] SP_TEST:Object 'vtTempTableForPI' does not exist.
Either create the Volatile Table before you compile it or simply switch to a Global Temporary Table instead (recommended) .

Related

SQL Server Syntax failure

I am running the following code and I am getting a syntax error near '.TableReference' error, the code use to work then I did something and now I have this error and I can't seem to find the issue
Through troubleshooting I have narrowed the code issue to the ' FROM ' + #TableName section but it appears to be good code.
BEGIN
--SET NOCOUNT ON;
DECLARE #TableName AS NVARCHAR(MAX) --The Fully qualified database name
DECLARE #Ref AS NVARCHAR(MAX) --The name of the Table we are processing
DECLARE #TempTab AS NVARCHAR(MAX) --the temporary table we are subjecting to the tortures of this process
DECLARE #TempQuery AS NVARCHAR(MAX) --Query to move all data into the temporary table
--This selects the first record in the Website Request Table which hasn't been processed and passes it into the TempTab variable
SET #NDTRef = (SELECT TOP 1 Reference from dbo.WebRequestTable Where Processing IS NULL)
SET #TableName = 'Processing.dbo.'+#NDTRef
Set #TempTab = 'TEMP' + #NDTRef
SET #TempQuery = 'SELECT * INTO '+ #TempTab +' FROM ' + #TableName
EXEC sp_sqlexec #TempQuery;
END
Any help would be appreciated it is a stand alone instance of SQL Server 2019 and the code is a part of a stored procedure but the rest of the code runs off the temporary table created in this block
After suggestions I put in a print statement regarding the #TempQuery when put straight after and the EXEC removed the output is
SELECT * INTO TEMP2294690 FROM Processing.dbo.2294690
With the EXEC back in play I get the error
Msg 102, Level 15, State 1, Line 17 Incorrect syntax near '.2294690'.
The print output after the EXEC shows:
SELECT * INTO TEMP2294690 FROM Processing.dbo.2294690
The Table 2294690 exists in the database Processing the Temp2294690 is a table that should be created by this block but it isn't being created
In SQL Server, regular Identifiers must begin with a letter, an underscore (_), at sign (#) or the number sigh (#).
(There are other rules as well, but this is the one relevant to the question...)
Identifiers that don't follow the rules of regular identifiers can be only used if they are enclosed in square brackets ([]) or double quotation marks (").
The best way to handle identifiers when creating dynamic SQL statements is to use the built in QUOTENAME function - this way you can make sure your query doesn't break even if the identifier doesn't follow the rules of regular identifiers.
So your SQL should look like this:
SET #TableName = '[Processing].[dbo].'+ QUOTENAME(#NDTRef)
SET #TempTab = 'TEMP' + #NDTRef
SET #TempQuery = 'SELECT * INTO '+ QUOTENAME(#TempTab) +' FROM ' + #TableName
That being said, you should also probably check my blog post entitled The do’s and don’ts of dynamic SQL for SQL Server where you can find some more information about how to safely create dynamic SQL.
You know that if the view is temporary only you can see it and if you close your session user the view is deleted and dont save try with create view dont temp and drop after you read.
If all previus you have it in your mind you could try this for see if the view has created --> its a extract of Microsoft official pagge:
VIEW_METADATA
Specifying the instance of SQL Server will return the DB-Library, ODBC, and OLE DB APIs the metadata information about the view instead of the base tables when you request the browse mode metadata for a query that references the view. Browse mode metadata is additional metadata that the SQL Server instance returns to these client-side APIs. This metadata enables client-side APIs to implement updateable client-side cursors. Browse mode metadata includes information about the base table to which the columns in the result set belong.

SQL Server: How to find what lines are executed

I am working on a mutation test framework for SQL Server, for this I need to be able to calculate what lines of a stored procedure, function or trigger are executed when I execute a certain stored procedure.
The difficult part is that I want to know the exact lines or statements being executed from the stored procedure I call.
With a query like this I can see what stored procedures/triggers/functions are being executed, since I know when I call the stored procedure I can use the time to see if it was executed.
SELECT d.object_id, d.database_id,
OBJECT_NAME(object_id, database_id) AS proc_name,
MAX( d.last_execution_time) as last_execution_time,
OBJECT_DEFINITION(object_id) as definition
FROM sys.dm_exec_procedure_stats AS d
WHERE d.database_id = DB_ID()
GROUP BY d.object_id, d.database_id,
OBJECT_NAME(object_id, database_id)
How would I find the lines/statements that have been executed, I also have to know inside what stored procedure/trigger/function the lines/statements exists and in which shema this is. I have to take into account that a IF/ELSE statement may be used.
With this data I can do 2 important things:
generate a code coverage report
optimize what lines to mutate, since I dont have to mutate uncovered lines.
A possible, but not a very nice, solution would be to automaticly change stored procedures to add a line that inserts the previous line into a table, but this will require splitting up the procedure into statements, which I don't know how to do.
Please note that I cannot change the code users want to test with my framework. I can search for patterns and replace but manually changing procedures is NOT a option.
EDIT:
Lets redifine this question: How to split a stored procedure definition into its different statements in a way that does not depend on code style?
and How to add a new statement in between found statements?
EDIT: in the SO post SQL Server: How to parse code into its different statements I have found a way to trace statement execution, but I can't filter it yet.
So the extended events are the solution, this is how I have done it:
IF EXISTS(SELECT * FROM sys.server_event_sessions WHERE name='testMSSQLTrace')
DROP EVENT SESSION testMSSQLTrace ON SERVER;
DECLARE #cmd VARCHAR(MAX) = '';
SELECT #cmd = 'CREATE EVENT SESSION testMSSQLTrace
ON SERVER
ADD EVENT sqlserver.sp_statement_completed
(WHERE (sqlserver.database_name = N''' + DB_NAME() + '''))
ADD TARGET package0.ring_buffer
WITH (
MAX_MEMORY = 2048 KB,
EVENT_RETENTION_MODE = NO_EVENT_LOSS,
MAX_DISPATCH_LATENCY = 3 SECONDS,
MAX_EVENT_SIZE = 0 KB,
MEMORY_PARTITION_MODE = NONE,
TRACK_CAUSALITY = OFF,
STARTUP_STATE = OFF
);'
EXEC (#cmd)
This creates an event that can be fired after every statement completion, this is done dynamicly to filter on the database
Then I have 3 procedures that make controlling this event easy
/*******************************************************************************************
Starts the statement trace
*******************************************************************************************/
CREATE OR ALTER PROC testMSSQL.Private_StartTrace
AS
BEGIN
ALTER EVENT SESSION testMSSQLTrace
ON SERVER
STATE = START;
END
GO
/*******************************************************************************************
Ends the statement trace, this also clears the trace
*******************************************************************************************/
CREATE OR ALTER PROC testMSSQL.Private_StopTrace
AS
BEGIN
ALTER EVENT SESSION testMSSQLTrace
ON SERVER
STATE = STOP;
END
GO
/*******************************************************************************************
Saves the statements trace
*******************************************************************************************/
CREATE OR ALTER PROC testMSSQL.Private_SaveTrace
AS
BEGIN
DECLARE #xml XML;
SELECT #xml = CAST(xet.target_data AS xml)
FROM sys.dm_xe_session_targets AS xet INNER JOIN sys.dm_xe_sessions AS xe ON (xe.address = xet.event_session_address)
WHERE xe.name = 'testMSSQLTrace'
INSERT INTO testMSSQL.StatementInvocations (testProcedure, procedureName, lineNumber, statement)
SELECT testMSSQL.GetCurrentTest(),
OBJECT_NAME(T.c.value('(data[#name="object_id"]/value)[1]', 'int')),
T.c.value('(data[#name="line_number"]/value)[1]', 'int'),
T.c.value('(data[#name="statement"]/value)[1]', 'VARCHAR(900)')
FROM #xml.nodes('RingBufferTarget/event') T(c)
WHERE T.c.value('(data[#name="nest_level"]/value)[1]', 'int') > 3
END
GO
These procedures respectivly start and stop the trace and the last one stores the result in a table where it filters on the nest level so my own code is not traced.
Finally I use it a bit like this:
start trace
start tran/savepoint
run SetUp (users code)
run test (users code)
save trace
save trace to variable
rollback tran (also catch errors and stuff like that)
save variable back to table so the trace is not rolled back
Special thanks to #Jeroen Mosterd for originally coming up with a proposal for this solution in this SQL Server: How to parse code into its different statements SO post
You can either:
Add a #DEBUG parameter to each stored procedure you call, or
Log everything you want, or
Only log when you want.
With the #Debug parameter, you can default it to OFF, then call it with ON when you want to trace your statements, with the following code:
IF (#Debug = 1) PRINT 'your tracing information goes here';
If you want to log everything, create a log table and insert a row into it wherever you need to know which statement was executed, such as:
DECLARE #log AS TABLE (msg VARCHAR(MAX));
and
INSERT INTO #log VALUES('your tracing information goes here');
Or you can combine them:
IF (#Debug = 1) INSERT INTO #log VALUES('your tracing information goes here');
Of course these will affect performance even when you don't output/log.

SQL Server stored procedure only works with locally re-declared variables; not variables passed to the procedure

I have edited my SQL code blocks to more accurately show what is going on
Say I have a simple stored procedure:
CREATE PROCEDURE [DBO].[FOO]
(#VARIABLE VARCHAR(500))
AS
BEGIN
SELECT AVG(BAR)
FROM MYTABLE
WHERE THING = #VARIABLE AND RTRIM(LTRIM(THING)) <> ''
END
When I call this stored procedure from my classic ASP page; which in this case would be with:
Set foo = Server.CreateObject("ADODB.RecordSet")
curCmd = "Foo 'MYVARIABLE'"
foo.Open curCmd, connectionString
I get this error (on the same line as the page opens the foo object):
Arithmetic overflow error converting varchar to data type numeric.
If I call the stored procedure manually in the terminal (IDE?); then it works fine.
Also if I recreate the stored procedure as the following:
CREATE PROCEDURE [DBO].[FOO]
(#VARIABLE VARCHAR(500))
AS
BEGIN
DECLARE #VARIABLE2 VARCHAR(500) = #VARIABLE
SELECT AVG(BAR)
FROM MYTABLE
WHERE THING = #VARIABLE2 AND RTRIM(LTRIM(THING)) <> ''
END
Then the stored procedure runs fine.
I have tried dropping and recreating the stored procedure (without using the re-declaration trick), but it does not fix the issue.
*As an aside; there is validation on the data being inserted into the table to ensure that only numbers (integers) are being entered for the THING field. The THING field can also be blank; hence the where clause.
I basically have two questions:
Why does re-declaring the same variable type with the same data fix the issue?
Is there a way I can fix my problem without using this silly "re-declaration" trick?
Thanks in advance for any help with this.
I think you can get the same error if you use begin/end:
CREATE PROCEDURE [DBO].[FOO] (
#VARIABLE VARCHAR(500)
)
AS
BEGIN
DECLARE #VARIABLE2 VARCHAR(500) = #VARIABLE;
SELECT AVG(BAR) FROM MYTABLE WHERE THING = #VARIABLE2;
END;
Then, both statements will be part of the stored procedure body and you can work on fixing the data so it will work.

Retrieve column definition for stored procedure result set that uses temp table

I am trying to retrieve column definitions for a stored procedure using the following query:
exec sp_describe_first_result_set #tsql = N'EXEC #return_value = [dbo].[foo]
#DATABASENAME = dbname,
#TABLENAME = tblname,
#DATEFROM = N''20170101'',
#DATETO = N''20170201'''
And I get the following response:
Msg 11526, Level 16, State 1, Procedure sp_describe_first_result_set, Line 1
The metadata could not be determined because statement 'INSERT INTO #Tables(CubeSchema,TableName,DateFilterColumn,SelectColumns) SELECT 'Col1','Col2' in procedure 'foo' uses a temp table
Is there a workaround for this issue?
Edit: I have not the rights to view and/or alter the stored procedures, so unfortunately solutions of this sort won't work.
Without altering your stored procedure the answer is MAYBE.
The option you are using ('sp_describe_first_result_set') wont work with the temp table, since you are using an INSERT statement.
If you would alter the INSERT statement to a SELECT INTO statement it might work for storing the data into the temp table, but you still wouldn't get your data out of it, since it isn't available. I am not sure about the internals of 'sp_describe_first_result_set', but it looks like it is using the SET FMTONLY ON option, which will fail because of the temp table.
So, if you want to avoid this, alter the stored procedure to use a table variable instead of a temp table (#table vs #table).
If you are not limited to SQL Server and you can program your way around this in a programming language (for example .Net using ADO.Net), you are able to use the GetSchemaTable method of the ExecuteReader result, like this:
var reader = sqlCommand.ExecuteReader();
var schemaTable = reader.GetSchemaTable();
Hope it helps!

Dynamically created temporary table does not persist

I want to create a temporary table in a dynamic query and use it afterwards. It will be created from a permanent table:
create table t (a integer);
insert into t values (1);
And the temp table creation is like this:
declare #command varchar(max) = '
select *
into #t
from t
;
select * from #t;
';
execute (#command);
When the #command is executed the select from the temporary table works.
Now if I select from the temporary table an error message is shown:
select * from #t;
Invalid object name '#t'
If the temporary table is created outside of the dynamic query it works:
select top 0 *
into #t
from t
declare #command varchar(max) = '
insert into #t
select *
from t
';
execute (#command);
select * from #t;
Is it possible to persist a dynamically created temporary table?
You are close in your assumption that EXECUTE is carried out in a different session.
According to the MSDN here
Executes a command string or character string within a Transact-SQL
batch
So your temporary table only exists inside the scope of the SQL executed by the EXECUTE command.
You can also create global temporary tables. For example, ##MyTemp.
But, global temporary tables are visible to all SQL Server connections.

Resources