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
Related
I want to create procedure which modify existing trigger. Trigger is responsible for blocking rows from beeing updated with specific ID. I tried something like that:
CREATE PROCEDURE Change_trigger
#List_of_ids varchar(8000)
AS
ALTER TRIGGER blocks
ON ttt
INSTEAD OF update
AS
BEGIN
If (SELECT Id_ttt FROM inserted) IN (#List_of_ids)
BEGIN
raiserror('You cannot modify this record.', 12, 1)
RETURN
END
UPDATE ttt
SET
field1 = INSERTED.field1
FROM INSERTED
WHERE INSERTED.Id_ttt = ttt.Id_ttt
END
Parameter #List_of_ids would be like this: 2,3,4,5,9,52. But when I try to create this procedure I got error:
Msg 156, Level 15, State 1, Procedure Change_trigger, Line 4
Incorrect syntax near the keyword 'TRIGGER'.
The trigger is created.
This is the trigger I'd write, once.
ALTER TRIGGER blocks
ON ttt
INSTEAD OF update
AS
BEGIN
SET NOCOUNT ON
UPDATE t
SET
field1 = i.field1
FROM INSERTED i
inner join
ttt t
on i.Id_ttt = t.Id_ttt
left join
ttt_blocked on tb
on
i.Id_ttt = tb.Id_ttt
WHERE
tb.Id_ttt is null
END
Note that this trigger no longer throws an error for blocked updates but it does allow for a mixed update (some rows blocked, some rows not) to occur. There's no clean way to raise an error whilst still partially applying an update in a trigger.
Then I'd have a table (referenced above):
CREATE TABLE ttt_blocked (
Id_ttt int not null,
constraint PK_ttt_blocked PRIMARY KEY (Id_ttt)
)
And then, if necessary, I'd create a procedure to maintain this table rather than continually changing the database schema:
CREATE PROCEDURE Change_blocking
#BlockedIDs xml
AS
--Better option would be table-valued parameters
--but I've chosen to do XML today
--We expect the XML to be of the form
--<blocks>
-- <id>10</id>
-- <id>15</id>
--</blocks>
MERGE INTO ttt_blocked t
USING (select x.id.value('text()[1]','int')
from #BlockedIDs.nodes('/blocks/id') x(id)) s(Id_ttt)
ON
t.Id_ttt = s.Id_ttt
WHEN NOT MATCHED THEN INSERT (Id_ttt) VALUES (s.Id_ttt)
WHEN NOT MATCHED BY SOURCE THEN DELETE;
As I also allude to above, I'd generally recommend Table-Valued Parameters rather than XML (and either of them ahead of varchar since they're designed to hold multiple values) but it would have added even more code to this answer.
Try this..
CREATE PROCEDURE Change_trigger
#List_of_ids varchar(4000)
AS
begin
declare #sql varchar(8000)
set #sql ='
ALTER TRIGGER blocks
ON ttt
INSTEAD OF update
AS
BEGIN
if exists (SELECT Id_ttt FROM inserted where Id_ttt IN ('+#List_of_ids+'))
BEGIN
raiserror(''You cannot modify this record.'', 12, 1)
RETURN
END
UPDATE ttt
SET
field1 = INSERTED.field1
FROM INSERTED
WHERE INSERTED.Id_ttt = ttt.Id_ttt
END' ;
exec (#sql);
END
I have a stored procedure as follows:
CREATE PROCEDURE [ODataTaskResult_Create]
#ODataTaskId BIGINT,
#ODataTaskResultTypeId INTEGER,
#Details CHARACTER VARYING(MAX)
AS
BEGIN TRANSACTION
INSERT INTO [ODataTaskResult] WITH (ROWLOCK, XLOCK) ([ODataTaskId], [ODataTaskResultTypeId], [Details], [CreatedOn])
VALUES (#ODataTaskId, #ODataTaskResultTypeId, #Details, SYSDATETIMEOFFSET())
DECLARE #ODataTaskResultTypeName CHARACTER VARYING(255)
SET #ODataTaskResultTypeName = (
SELECT TOP 1 [ODataTaskType].[Name] FROM [ODataTaskType]
WHERE [ODataTaskType].[Id] = #ODataTaskResultTypeId)
IF (#ODataTaskResultTypeName = 'Finish')
BEGIN
UPDATE [ODataTask]
SET [ODataTask].[FinishedOn] = SYSDATETIMEOFFSET()
WHERE [ODataTask].[Id] = #ODataTaskId
END ELSE IF (#ODataTaskResultTypeName = 'Delete')
BEGIN
UPDATE [ODataTask]
SET [ODataTask].[DeletedOn] = SYSDATETIMEOFFSET()
WHERE [ODataTask].[Id] = #ODataTaskId
END ELSE
RAISERROR('Invalid result type', 16, 1)
COMMIT TRANSACTION
GO
This procedure is supposed to look at the incoming #ODataTaskResultTypeId parameter, pull the result type down from another table, and do something based on the Name column in that record.
Basically when a result is entered against a task, it defines how it completed. If a task is finished, I need to modify the FinishedOn column on the parent task record and not alter the DeletedOn column. We have a constraint that indicates FinishedOn and DeletedOn may not both be NOT NULL.
Feeling at this point that since I have hard coded the different case logic into the stored procedure, it makes maintainability difficult and prevents this from working properly unless the ODataTaskResult table has the correct initial entries.
Should I make the ODataTaskResult_Create procedure only create the result and then have another procedure called ODataTask_Finish as well as another procedure called ODataTask_Delete?
Is there a different approach to this problem that is generally easier to maintain?
We never hard delete entries, only soft delete.
If you want a flexible solution, you can add a column to your ODataTaskType table to hold the stored procedure to run afterwards. You can then use some dynamic sql to dispatch. If the column is called PostComplete_Proc, say:
create proc dbo.[ODataTaskResult_Create]
#ODataTaskId bigint,
#ODataTaskResultTypeId int,
#Details varchar(max)
as
declare
#proc sysname,
#params nvarchar(max) = '#ODataTaskId bigint'
begin transaction
insert into dbo.[ODataTaskResult] with (rowlock, xlock) (
[ODataTaskId], [ODataTaskResultTypeId], [Details], [CreatedOn]
) values
#ODataTaskId,
#ODataTaskResultTypeId,
#Details,
sysdatetimeoffset()
);
select top 1 -- Would there really be more than 1? Why hide potential errors?
#proc = PostComplete_Proc
from
dbo.[ODataTaskType]
where
Id = #ODataTaskResultTypeId;
if #proc is null
raiserror('Invalid result type', 16, 1);
else
exec #proc, #params, #ODataTaskId;
commit transaction;
then create the relevant stored procedures. If you have many result types and few procedures, you can even add another level, where the procedures are stored on a separate table and referenced via foreign keys.
I find it hard to convince myself that rowlock, xlock is doing anything here.
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
How can i know which sql statement fired through trigger for select, insert, update and delete on table?
As Jonas says, Profiler is your best option (and only option for SELECT queries). For INSERT, UPDATE, DELETEs, the closest you can get without Profiler may be to look at the input buffer via DBCC INPUTBUFFER(##SPID). This will only work for ad-hoc language events, not RPC calls, and will only show you the first 256 characters of the SQL statement (depending on version, I believe). Some example code, (run as dbo):
CREATE TABLE TBL (a int, b varchar(50))
go
INSERT INTO TBL SELECT 1,'hello'
INSERT INTO TBL SELECT 2,'goodbye'
go
GRANT SELECT, UPDATE ON TBL TO guest
go
CREATE TABLE AUDIT ( audittime datetime default(getdate())
, targettable sysname
, loginname sysname
, spid int
, sqltext nvarchar(max))
go
CREATE TRIGGER TR_TBL ON TBL FOR INSERT, UPDATE, DELETE
AS BEGIN
CREATE TABLE #DBCC (EventType varchar(50), Parameters varchar(50), EventInfo nvarchar(max))
INSERT INTO #DBCC
EXEC ('DBCC INPUTBUFFER(##SPID)')
INSERT INTO AUDIT (targettable, loginname, spid, sqltext)
SELECT targettable = 'TBL'
, suser = suser_name()
, spid = ##SPID
, sqltext = EventInfo
FROM #DBCC
END
GO
/* Test the Audit Trigger (can be run as guest) */
UPDATE TBL SET a = 3 WHERE a = 2
First, there are no select dml triggers, only triggers that work on INSERT, UPDATE or DELETE
Secondly, you can't know which sql statement triggered the trigger (at least not in the trigger). However, you can use profiler to debug what's happening in the database. There's a decent explanation of this here.
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