I wrote a stored procedure to insert future dates (excluding Sat and Sun) into a permanent table. The routine uses just one temp table. The purpose of this post is not to critique the Stored Procedure. Although, I am certain it could be improved. However, the purpose of this post is to analyze why the stored procedure is throwing these errors when called under one set of circumstances and not others.
1 - Here are the errors I receive
Msg 207, Level 16, State 1, Procedure MAKE_FUTURE_DATES, Line 25
Invalid column name 'tdate'.
Msg 207, Level 16, State 1, Procedure MAKE_FUTURE_DATES, Line 31
Invalid column name 'wday'.
2 - Here is the stored procedure
ALTER PROCEDURE [dbo].[MAKE_FUTURE_DATES] (#STARTDATE DATE)
AS
BEGIN
-- We need to populate FUTURE_DATES (table) with forward looking dates (week days only)
-- We do not consider/exclude holidays here. We just exclude Sat/Suns
-- Temp table to hold the dates and days of the week
CREATE TABLE #TMP_DATES(
[tdate] [date] NULL,
[wday] [varchar](10) NULL,
)
-- To generate 'enough' future dates loop up to 1199 days in the future
-- and insert dates that start with the current date and increase with each loop
DECLARE #Loop INT
SET #Loop = 0
WHILE #Loop < 1200
BEGIN
INSERT INTO #TMP_DATES (tdate) VALUES (DATEADD(weekday,#Loop,#STARTDATE))
SET #Loop = #Loop + 1
END
-- Now update the wday column with the weekday name so we can get rid of
-- Sat/Sun in the next step
UPDATE #TMP_DATES
SET wday = UPPER(LEFT(DATENAME(dw,tdate),3))
-- Get rid of Sat/Sun
DELETE FROM #TMP_DATES WHERE wday = 'SAT' or wday = 'SUN'
-- Now clear the final destination table
TRUNCATE TABLE FUTURE_DATES
-- Insert the weekday dates into future_dates
INSERT INTO FUTURE_DATES (fdate,wday)
SELECT tdate,wday FROM #TMP_DATES
DROP TABLE #TMP_DATES
3 - I have been calling the above stored procedure within another stored procedure as a SQL Server task in the background (via the SQL Server job scheduler) for about 6 months without any errors or problems. More recently, I created a new stored procedure, let's call it 'ABC', that calls a stored procedure that calls MAKE_FUTURE_DATEs.
4 - Here is the part I am trying to solve. When ABC is invoked as a SQL Server task in the background (via the SQL Server job scheduler) it throws the errors every time (which is daily) and the results are not produced/it crashes. When I first start up SQL Server Management Studio and run ABC it sometimes throws the error the first time. The second time in this sequence and all subsequent times it does not throw the error. Also, keep in mind that the stored procedure that has been calling MAKE_FUTURE_DATES for 6 months is still quite happy with no errors.
I am looking for suggestions on how to debug this or what to look for. Particularly how can it throw the error sometimes and not others?
The code that you've posted looks fine to me. Is this the complete code?
The only thing that comes to mind is that your "ABC" stored procedure also has a temp table called #TMP_DATES. The temp table in ABC would then also be available in the scope of the called stored procedure.
However, if that were the case, you should get a different error when you called CREATE TABLE #TMP_DATES in the called procedure.
Temp tables have a scope greater than just one procedure.
So you can create a temp table in uspProc1...and if in uspProc1, you call uspProc2, uspProc2 "can see" the temp table you created.
Make sure you provide unique names to your #temp tables.
Below is an example demonstrating the point.
IF EXISTS
(
SELECT * FROM INFORMATION_SCHEMA.ROUTINES
WHERE ROUTINE_TYPE = N'PROCEDURE' and ROUTINE_SCHEMA = N'dbo' and ROUTINE_NAME = N'uspProc001'
)
BEGIN
DROP PROCEDURE [dbo].[uspProc001]
END
GO
CREATE Procedure dbo.uspProc001 (
#Param1 int
)
AS
BEGIN
IF OBJECT_ID('tempdb..#TableOne') IS NOT NULL
begin
drop table #TableOne
end
CREATE TABLE #TableOne
(
SurrogateKey int ,
NameOf varchar(12)
)
Insert into #TableOne ( SurrogateKey , NameOf ) select 1001, 'uspProc001'
Select * from #TableOne
EXEC dbo.uspProc002
Select * from #TableOne
IF OBJECT_ID('tempdb..#TableOne') IS NOT NULL
begin
drop table #TableOne
end
END
GO
IF EXISTS
(
SELECT * FROM INFORMATION_SCHEMA.ROUTINES
WHERE ROUTINE_TYPE = N'PROCEDURE' and ROUTINE_SCHEMA = N'dbo' and ROUTINE_NAME = N'uspProc002'
)
BEGIN
DROP PROCEDURE [dbo].[uspProc002]
END
GO
CREATE Procedure dbo.uspProc002
AS
BEGIN
IF OBJECT_ID('tempdb..#TableOne') IS NOT NULL
begin
Insert into #TableOne ( SurrogateKey , NameOf ) select 2001, 'uspProc002'
end
END
GO
exec dbo.uspProc001 0
Related
I was keen to have some creation of SQL Server temp tables occur in a sub process to break up the stored procedure into parts rather than one big monolithic process.
i.e.
CREATE PROCEDURE dbo.P422270HRF605CreateTempTables
AS
IF OBJECT_ID('tempdb..#tmpState') IS NOT NULL
DROP TABLE #tmpState
CREATE TABLE #tmpState
(
StateID VARCHAR(8) NOT NULL,
Include VARCHAR(1) NOT NULL
)
GO
And I have another procedure call this and then use the temp tables ..
ALTER PROCEDURE Membership.GenerateHRF605Data
#FromDate DateTime,
#ToDate DateTime,
#MemberNo INT
AS
BEGIN
-- create all the temp tables required for HRF 605 processing
EXEC dbo.P422270HRF605CreateTempTables
INSERT INTO #tmpState
SELECT StateID, Include = 'N'
FROM dbo.States
UNION
SELECT 'Overseas', 'N'
UNION
SELECT 'Unknown', 'N'
but this seems to fail at runtime:
EXEC [Membership].[GenerateHRF605Data] #FromDate='20190901', #ToDate='20190930', #MemberNo=90001
I get this error:
Msg 208, Level 16, State 0, Procedure Membership.GenerateHRF605Data, Line 125 [Batch Start Line 0]
Invalid object name '#tmpState'.
Maybe SQL Server doesn't allow for the creation of temp tables in a called procedure and use of them in calling procedure?
I have a stored procedure in Sybase, it's called by the Java code on a row by row insertion, how do I find out whether this SP is locking table or locking row? To check the underlying table properties or the SP itself?
CREATE PROCEDURE dbo.sp1
(#id_code varchar(10),
#position_id numeric(10,0) OUTPUT
)
AS
BEGIN
BEGIN TRANSACTION
INSERT INTO abc..table1(
id_code,
position_id
)
values (
#id_code
#position_id
)
COMMIT
SELECT #position_id = ##identity
END
go
EXEC sp_procxmode 'dbo.sp1', 'unchained'
go
IF OBJECT_ID('dbo.sp1') IS NOT NULL
PRINT '<<< CREATED PROCEDURE dbo.sp1 >>>'
ELSE
PRINT '<<< FAILED CREATING PROCEDURE dbo.sp1 >>>'
The lock scheme for a table can be determined using the lockscheme() built in command.
lockscheme('tableName')
You can also specify obj_id and dbid.
ASE 16 Documentation: lockscheme
I have a a number of sp's that create a temporary table #TempData with various fields. Within these sp's I call some processing sp that operates on #TempData. Temp data processing depends on sp input parameters. SP code is:
CREATE PROCEDURE [dbo].[tempdata_proc]
#ID int,
#NeedAvg tinyint = 0
AS
BEGIN
SET NOCOUNT ON;
if #NeedAvg = 1
Update #TempData set AvgValue = 1
Update #TempData set Value = -1;
END
Then, this sp is called in outer sp with the following code:
USE [BN]
--GO
--DBCC FREEPROCCACHE;
GO
Create table #TempData
(
tele_time datetime
, Value float
--, AvgValue float
)
Create clustered index IXTemp on #TempData(tele_time);
insert into #TempData(tele_time, Value ) values( GETDATE(), 50 ); --sample data
declare
#ID int,
#UpdAvg int;
select
#ID = 1000,
#UpdAvg = 1
;
Exec dbo.tempdata_proc #ID, #UpdAvg ;
select * from #TempData;
drop table #TempData
This code throws an error: Msg 207, Level 16, State 1, Procedure tempdata_proc, Line 8: Invalid column name "AvgValue".
But if only I uncomment declaration AvgValue float - everything works OK.
The question: is there any workaround letting the stored proc code remain the same and providing a tip to the optimizer - skip this because AvgValue column will not be used by the sp due to params passed.
Dynamic SQL is not a welcomed solution BTW. Using alternative to #TempData tablename is undesireable solution according to existing tsql code (huge modifications necessary for that).
Tried SET FMTONLY, tempdb.tempdb.sys.columns, try-catch wrapping without any success.
The way that stored procedures are processed is split into two parts - one part, checking for syntactical correctness, is performed at the time that the stored procedure is created or altered. The remaining part of compilation is deferred until the point in time at which the store procedure is executed. This is referred to as Deferred Name Resolution and allows a stored procedure to include references to tables (not just limited to temp tables) that do not exist at the point in time that the procedure is created.
Unfortunately, when it comes to the point in time that the procedure is executed, it needs to be able to compile all of the individual statements, and it's at this time that it will discover that the table exists but that the column doesn't - and so at this time, it will generate an error and refuse to run the procedure.
The T-SQL language is unfortunately a very simplistic compiler, and doesn't take runtime control flow into account when attempting to perform the compilation. It doesn't analyse the control flow or attempt to defer the compilation in conditional paths - it just fails the compilation because the column doesn't (at this time) exist.
Unfortunately, there aren't any mechanisms built in to SQL Server to control this behaviour - this is the behaviour you get, and anything that addresses it is going to be perceived as a workaround - as evidenced already by the (valid) suggestions in the comments - the two main ways to deal with it are to use dynamic SQL or to ensure that the temp table always contains all columns required.
One way to workaround your concerns about maintenance if you go down the "all uses of the temp table should have all columns" is to move the column definitions into a separate stored procedure, that can then augment the temporary table with all of the required columns - something like:
create procedure S_TT_Init
as
alter table #TT add Column1 int not null
alter table #TT add Column2 varchar(9) null
go
create procedure S_TT_Consumer
as
insert into #TT(Column1,Column2) values (9,'abc')
go
create procedure S_TT_User
as
create table #TT (tmp int null)
exec S_TT_Init
insert into #TT(Column1) values (8)
exec S_TT_Consumer
select Column1 from #TT
go
exec S_TT_User
Which produces the output 8 and 9. You'd put your temp table definition in S_TT_Init, S_TT_Consumer is the inner query that multiple stored procedures call, and S_TT_User is an example of one such stored procedure.
Create the table with the column initially. If you're populating the TEMP table with SPROC output just make it an IDENTITY INT (1,1) so the columns line up with your output.
Then drop the column and re-add it as the appropriate data type later on in the SPROC.
The only (or maybe best) way i can thing off beyond dynamic SQL is using checks for database structure.
if exists (Select 1 From tempdb.sys.columns Where object_id=OBJECT_ID('tempdb.dbo.#TTT') and name = 'AvgValue')
begin
--do something AvgValue related
end
maybe create a simple function that takes table name and column or only column if its always #TempTable and retursn 1/0 if the column exists, would be useful in the long run i think
if dbo.TempTableHasField('AvgValue')=1
begin
-- do something AvgValue related
end
EDIT1: Dang, you are right, sorry about that, i was sure i had ... this.... :( let me thing a bit more
Sorry I'm a bit new to this so just trying to get my head around linking everything up.
At the moment I have a normal query - SELECT FROM WHERE which basically finds about 2000 records that I need to update which link across several tables.
Can someone tell me how I can link this simple query to something else so I can basically execute several stored procedures, all in the same script? But only affecting the records returned by my simple query?
Apologies, that probably sounds as clear as mud!
*EDIT - MORE DETAIL *
So here is my Select query:
SELECT [MembershipTermID]
,[MemberStatusProgKey]
,[StartDate]
,[EndDate]
,[AdditionalDiscount]
,[EntryDateTime]
,[UpdateDateTime]
,[MembershipID]
,[AgentID]
,[PlanVersionID]
,[ForceThroughReference]
,[IsForceThrough]
,[NextTermPrePaid]
,[IsBillingMonthly]
,[CICSMEMBERNUM]
,[CICSHISTORY]
,[TMPSeqNoColumn]
,[LastPaymentDate]
,[PaidToDate]
,[IsIndeterminate]
,DATEDIFF(MONTH, PaidToDate, GETDATE()) as MonthsDifference
,dbo.FullMonthsSeparation (PaidToDate, GETDATE())
FROM [Apollo].[dbo].[MembershipTerm]
WHERE MemberStatusProgKey='DORMANT'
AND IsBillingMonthly=1
AND dbo.FullMonthsSeparation (PaidToDate, GETDATE()) >= 2
So using the rows that this returns I want to exec several stored procedures to update everything I need to in the database which would be affected by changing these rows. An example of one stored procedure is below, I think I will need to execute about 10 of these if not more:
USE [Apollo]
GO
/****** Object: StoredProcedure [dbo].[spCancellationDetailInsert] Script Date: 01/10/2012 10:21:50 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
/* ************************* INSERT *************************/
/* Auto Generated 11/29/2006 7:28:53 PM by Object Builder */
/* ************************* INSERT *************************/
ALTER Procedure [dbo].[spCancellationDetailInsert]
#StampUser char (10),
#CancellationDetailID int,
#RefundAmount float,
#OldEndDate datetime,
#EffectiveDate datetime,
#CancelDate datetime,
#ReasonCodeProgKey nvarchar (50)
As
/* insert CancellationDetail record */
Insert [CancellationDetail]
(
RefundAmount,
OldEndDate,
EffectiveDate,
CancelDate,
ReasonCodeProgKey
)
Values
(
#RefundAmount,
#OldEndDate,
#EffectiveDate,
#CancelDate,
#ReasonCodeProgKey
)
If ##Error <> 0 GoTo InsertErrorHandler
/* save the key of the new row created by the insert */
Select #CancellationDetailID = Scope_Identity()
/* add audit record */
Insert CancellationDetailAudit
(StampUser,
StampDateTime,
StampAction,
CancellationDetailID,
RefundAmount,
OldEndDate,
EffectiveDate,
CancelDate,
ReasonCodeProgKey)
Values
(#StampUser ,
GetDate() ,
'I',
#CancellationDetailID,
#RefundAmount,
#OldEndDate,
#EffectiveDate,
#CancelDate,
#ReasonCodeProgKey)
If ##Error <> 0 GoTo AuditInsertErrorHandler
Select
CancellationDetailID = #CancellationDetailID
Return (0)
InsertErrorHandler:
Raiserror ('SQL Error whilst inserting CancellationDetailrecord: Error Code %d',17,1,##Error)
With Log
Return (99)
AuditInsertErrorHandler:
Raiserror ('SQL Error whilst inserting audit record for CancellationDetailInsert: Error Code %d',17,1,##Error)
With Log
Return (99)
If you're asking what I think you are -
Stored procedures can contain (pretty much) any valid SQL statement. This includes returning multiple results sets, performing multiple updates and calling other stored procedures.
For example:
CREATE PROCEDURE usp_Sample AS
SELECT * FROM INFORMATION_SCHEMA.COLUMNS
SELECT * FROM INFORMATION_SCHEMA.TABLES
UPDATE Users SET Active = 0 WHERE ExpiredDate < GetDate()
SELECT Active, COUNT(*) FROM Users GROUP BY Active
EXEC usp_Sample2
GO
Obviously that's a rather artificial example, but assuming all the objects existed it'd run perfectly well.
In order to perform more queries at the same time you just need to append them after your select.
So you can do
Select *
From table1
Select *
From table2
Select *
From table3
as many times as you want and they'll all execute independently.
If you want to UPDATE based on a SELECT you usually do something like:
UPDATE table1
WHERE ID IN (SELECT ID FROM TABLE2)
with regards to your stored procedures it would help if you posted more details.
I have an Informix stored procedure that returns two columns and multiple rows. I can use "EXECUTE FUNCTION curr_sess(2009,'SP')" fine, but how do I get the results into a temp table.
EDIT: We are on version 10.00.HC5
Testing Jonathan Leffler's idea didn't work.
EXECUTE FUNCTION curr_sess(2009,'SP')
works fine. Then I did
CREATE TEMP TABLE t12(yr smallint, sess char(4));
But when I try
INSERT INTO t12 EXECUTE FUNCTION curr_sess(2009,'SP');
It doesn't work, I get a " Illegal SQL statement in SPL routine." error.
The source for curr_sess
begin procedure
DEFINE _yr smallint;
DEFINE _sess char(4);
SELECT
DISTINCT
sess_vw.yr,
sess_vw.sess,
sess_vw.sess_sort
FROM
sess_vw
ORDER BY
sess_vw.sess_sort DESC
INTO temp tmp_sess WITH NO LOG;
SELECT
FIRST 1
tmp_sess.yr,
tmp_sess.sess
FROM
tmp_sess
WHERE
tmp_sess.sess_sort = sess_sort(iYear,sSess)
INTO temp tmp_final WITH NO LOG;
FOREACH cursor1 FOR
SELECT
tmp_final.yr,
tmp_final.sess
INTO
_yr,
_sess
FROM
tmp_final
RETURN _yr, _sess WITH RESUME;
END FOREACH;
DROP TABLE tmp_sess;
DROP TABLE tmp_final;
end procedure
EDIT: sess_sort() does a lookup.
I have tried to rewrite the function as one query. Here is next_sess:
SELECT
FIRST 1
sess_vw.sess_sort
FROM
sess_vw
WHERE
sess_vw.sess_sort > sess_sort(2009,'SP')
ORDER BY
sess_vw.sess_sort ASC
Someone from IBM emailed me and suggested using something like this:
SELECT
*
FROM
TABLE(next_sess(2009,'SP'))
But that still didn't work.
One possibility is a stored procedure. Another (tested on IDS 11.50.FC1), which I wasn't sure would work, is:
CREATE PROCEDURE r12() RETURNING INT, INT;
RETURN 1, 2 WITH RESUME;
RETURN 2, 3 WITH RESUME;
END PROCEDURE;
CREATE TEMP TABLE t12(c1 INT, c2 INT);
INSERT INTO t12 EXECUTE PROCEDURE r12();
The last line is the important one.
Given the observation that the stored procedure cannot be executed as shown just above (because it contains some non-permitted SQL statement), then you need to use stored procedures another way - illustrated by this test code (which works: worked first time, which pleasantly surprised me):
CREATE TEMP TABLE t12(yr smallint, sess char(4));
CREATE PROCEDURE curr_sess(yearnum SMALLINT, sesscode CHAR(2))
RETURNING SMALLINT AS yr, CHAR(4) AS sess;
RETURN yearnum, (sesscode || 'AD') WITH RESUME;
RETURN yearnum, (sesscode || 'BC') WITH RESUME;
END PROCEDURE;
CREATE PROCEDURE r12(yearnum SMALLINT, sesscode CHAR(2))
DEFINE yr SMALLINT;
DEFINE sess CHAR(4);
FOREACH EXECUTE PROCEDURE curr_sess(yearnum, sesscode) INTO yr, sess
INSERT INTO t12 VALUES(yr, sess);
END FOREACH;
END PROCEDURE;
EXECUTE PROCEDURE r12(2009,'SP');
SELECT * from t12;
You could incorporate the creation of the temp table into the stored procedure; you could even arrange to drop a pre-existing table with the same name as the temp table (use exception handling). Given that you're using IDS 10.00, you are stuck with a fixed name for the temp table. It would be possible, though not recommended (by me) to use the dynamic SQL facility in 11.50 to name the temp table at runtime.
Be aware that stored procedures that access temporary tables get reoptimized when reused - the table that is used is not the same as the last time (because it is a temporary) so the query plan isn't all that much help.
That fails possibly because the 'drop table' is not valid statment in a procedure that is used in this context?
http://publib.boulder.ibm.com/infocenter/idshelp/v115/topic/com.ibm.sqls.doc/ids_sqs_1755.htm#ids_sqs_1755