When I execute the code :
declare #result int
exec recostcos #result
select #result
I get 'Msg 208, Level 16, State 0, Procedure RecostCOS, Line 138
Invalid object name '#rLB'.'
The strange thing is The first reference to #rLB did not produce any error but the second reference on line 138 produces the error above.
Please find below the code for recostcos :
set ANSI_NULLS ON
set QUOTED_IDENTIFIER ON
GO
ALTER proc [dbo].[RecostCOS](#result int output)
as
BEGIN TRY
BEGIN TRANSACTION
SET NOCOUNT ON
--variables
declare #itemName varchar(50)
declare #invoiceNo varchar(50)
declare #customerName varchar(100)
declare #recordedBy varchar(100)
declare #transDate datetime
declare #supCode varchar(50)
declare #decemQty1 decimal(18,6)
declare #decemQty2 decimal(18,6)
declare #decemQty3 decimal(18,6)
declare #recostQty decimal(18,6)
declare #count int
declare #innerCount int
declare #outputCode int
declare #outputCounter int
declare #recId int
declare #innerId int
create table #supcodes(supcode varchar(50),item_name varchar(50),
qty decimal(18,6),id int identity(1,1))
create table #recostList(invoice_no varchar(50),qty decimal(18,6), id int identity(1,1))
create table #rLB(invc_no varchar(50),qty decimal(18,6), id int identity(1,1))
--check for supplyCode needing reprocessing
select #count=count(*) from someTable where recosted=0;
if(#count<=0)
begin
commit
set #result=0
return #result
end
--
insert into #supcodes(supcode,item_name,qty)
select supply_code,item_name,quantity from someTable
where recosted=0
--recost each supply code needing recosting
while(#count>0)
begin
select top 1 #supCode=supcode,#itemName=item_name,#decemQty1=qty,#recId=id
from #supcodes
select #decemQty2=sum(current_qty) from someTable2 where item_name=#itemName
select #decemQty3=quantity from someTable3 where item_name=#itemName
if(#decemQty2 is null)
set #decemQty2=0
if(#decemQty3 is null)
set #decemQty3=0
--debug
if(#decemQty2<>#decemQty3)
begin
--check if there is a log of this error
select #count=count(*) from someTable4 where error_code=1 and
item_name=#itemName and supply_code=#supCode and resolved=0
if(#count<=0)
--error in stock quantities (this must be resolved b4 any recosting)
insert into someTable4(supply_code,item_name,error_code,error_source,error_detail)
values(#supCode,#itemName,1,'re-costing','Mismatch in stock quantities in stock state and supply codes tables')
end
else if(#decemQty1>#decemQty2)
begin
--check if there is a log of this error
select #count=count(*) from someTable4 where error_code=2 and
item_name=#itemName and supply_code=#supCode and resolved=0
--insufficient stock for recosting
if(#count<=0)
insert into someTable4(supply_code,item_name,error_code,error_source,error_detail)
values(#supCode,#itemName,2,'re-costing','insufficient stock for recosting')
end
else
begin
--recost cost of sales of item involved
--get list of invoices of item to be recosted
set #recostQty=#decemQty1
insert into #recostList(invoice_no,qty)
select invoice_no,quantity from someTable5
where supply_code=#supCode and item_name=#itemName
insert into #rLB(invc_no,qty) select invoice_no,qty from #recostList
--delete cost of sales relating to supcod and item in stock account
select #innerCount=count(*) from #recostList
while(#innerCount>0)
begin
select top 1 #invoiceNo=invoice_no,#decemQty2=qty,#innerId=id
from #recostList
delete someTable6 where description
like 'sales of '+ltrim(rtrim(convert(varchar(20),#decemQty2)))+'%'+
#itemName+'%'+#invoiceNo
delete someTable5 where supply_code=#supCode and item_name=#itemName
and quantity=#decemQty2
delete #recostList where id=#innerId
set #innerCount=#innerCount-1
end
--call costByFIFO to recost item
select #innerCount=count(*) from #rLB
set #outputCounter=#innerCount
while(#innerCount>0)
begin
select top 1 #invoiceNo=invc_no,#decemQty2=qty,#innerId=id
from #rLB
select #customerName=customer_name, #transDate=trans_date,
#recordedBy=recorded_by from someTable7 where invoice_no=#invoiceNo
exec #outputCode=costByFIFO #itemName,#invoiceNo,#customerName,
#decemQty2,#transDate,#recordedBy
--ensure each invoice is costed or reverse entire process
if(#outputCounter=0)
begin
set #outputCounter=#outputCounter-1
end
else
begin
set #result=3 --failed to cost all invoices involved
rollback
end
delete #rLB where id=#innerId
set #innerCount=#innerCount-1
end
--outputCounter must be 0 to indicate all invoices where costed
if(#outputCounter<>0)
begin
set #result=3 --failed to cost all invoices involved
rollback
end
else
begin
update someTable set recosted=1 where supply_code=#supCode
and item_name=#itemName
end
end
delete #supcodes where id=#recId
set #count=#count-1
end
SET NOCOUNT OFF
drop table #supcodes
drop table #recostList
drop table #rLB
set #result=0
COMMIT
END TRY
BEGIN CATCH
SELECT ERROR_NUMBER() ErrorNBR, ERROR_SEVERITY() Severity,
ERROR_LINE () ErrorLine, ERROR_MESSAGE() Msg
set #result=2 --unexpected error occured
ROLLBACK
END CATCH
Related
The table below stores sql insert statements and I run those from a sp. I need to also add an insert to the last_run_dt column. I put the code together via existing stackoverflow questions. I need help implementing this in my code, any feedback will be helpful.
How can I update my code to update the last_run_dt column?
Table:
audit_sql_id audit_sql last_run_dt
1 select * from <<need to add last run_dt value>>
2 select * from <<need to add last run_dt value>>
Code:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
alter proc [dbo].[sp_sqlAudit]
#packagename as varchar(255)
as
begin
set nocount on;
if #packagename='SQL_DM_AUDIT'
begin
declare #queries table (audit_sql_id int identity(1,1),sqlscript varchar(max))
declare #str_query varchar(max);
declare #startloop int
declare #endloop int
insert into #queries
select audit_sql
from dw.dbo.audit_sql with(nolock)
select #endloop = max(audit_sql_id), #startloop = min(audit_sql_id)
from #queries
while #startloop < = #endloop
begin
select #str_query = sqlscript
from #queries
where audit_sql_id = #startloop
exec (#str_query)
set #startloop = #startloop + 1
end
end
end
I would suggest a slight refactor something like the below. There's no need to bring the entire list of sql statements into TemDB, just iterate over it and get each statement in turn. I would also always add a #debug parameter to print the sql instead if executing.
create or alter procedure dbo.sqlAudit
#packagename as varchar(255)
as
set nocount on;
declare #str_query varchar(max), #Id int
declare #AuditID table (Id int)
if #packagename='SQL_DM_AUDIT'
begin
insert into #AuditID (Id) /* Get list of IDs */
select audit_sql_id
from dw.dbo.audit_sql
while exists(select * from #AuditID) /* Continue while there are IDs in the list */
begin
select top (1) #Id=Id from #AuditID /* Get an ID */
select #str_query=audit_sql /* Get the sql for the ID */
from dw.dbo.audit_sql
where audit_sql_id=#Id
delete from #AuditID where Id=#Id /* Remove this ID from the list */
begin try
exec (#str_query)
if ##Error=0
begin
update dw.dbo.audit_sql set last_run_dt=GetDate() /* Update date for ID if run successful */
where audit_sql_id=#Id
end
end try
begin catch
/*handle error*/
end catch
end
end
go
I'm getting an error message in a stored procedure, saying that I can't insert a NULL value into a table, when I should be getting errors earlier in the code if the value is null.
Here's the relevant part of the stored procedure:
CREATE PROCEDURE [dbo].[udp_AddUpdateStaffVariable]
-- Add the parameters for the stored procedure here
#StaffID int=null,
#VariableTypeID int,
#VariableIntValue int=null,
#VariableVarcharValue varchar(max)=null,
#VariableDatetimeValue datetime=null,
#VariableDecimalValue decimal=null
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
BEGIN TRY
DECLARE #PrintOutput varchar(150)
SET #PrintOutput = '#StaffID = ' + CASE WHEN #StaffID = NULL THEN 'Null' ELSE CONVERT(varchar(20), #StaffID) END
RAISERROR (#PrintOutput, 10, 1) WITH NOWAIT
IF (#StaffID = NULL) -- If the staffid of the current user was not supplied, find it in the Staff table
BEGIN
DECLARE #CurrentUser nvarchar(255) = SUSER_SNAME();
SELECT #StaffID = [StaffID] FROM [dbo].[Staff] WHERE [UserName] = #CurrentUser;
SET #PrintOutput = '#StaffID = ' + CASE WHEN #StaffID = NULL THEN 'Null' ELSE CONVERT(varchar(20), #StaffID) END
RAISERROR (#PrintOutput, 10, 1) WITH NOWAIT
IF #StaffID = NULL -- raise error if staffid wasn't found
BEGIN
RAISERROR (50001 --error number
, 16 -- severity
, 1 --state
, #CurrentUser -- parameter
)
END
END
-- Get the variable data type (used to determine where the variable is stored)
DECLARE #VarDataTypeDesc varchar(20)
DECLARE #StaffVarID int
SELECT #VarDataTypeDesc = dt.[StaffVariableDataType]
FROM [list].[DataTypes] dt INNER JOIN [list].[StaffVariableTypes] svt ON dt.DataTypeID = svt.DataTypeID
WHERE svt.VariableTypeID = #VariableTypeID
-- update or add the staff variable
IF EXISTS (SELECT 1 FROM [dbo].[StaffVariables] WHERE StaffID = #StaffID AND [VariableTypeID] = #VariableTypeID) -- update
BEGIN
IF #VarDataTypeDesc = 'int'
BEGIN -- only update here - other data types are updated further down
UPDATE [dbo].[StaffVariables] SET VariableIntValue = #VariableIntValue WHERE StaffID = #StaffID AND VariableTypeID = #VariableTypeID
END
ELSE -- StaffVariableID is only needed if the variable type is not int
BEGIN
SELECT #StaffVarID = StaffVariableID FROM [dbo].[StaffVariables] WHERE StaffID = #StaffID AND [VariableTypeID] = #VariableTypeID
END
END
ELSE -- insert
BEGIN
IF #VarDataTypeDesc = 'int'
BEGIN
INSERT INTO [dbo].[StaffVariables] (StaffID, VariableTypeID, VariableIntValue)
VALUES (#StaffID, #VariableTypeID, #VariableIntValue)
END
ELSE -- StaffVariableID is only needed if the variable type is not int
BEGIN
DECLARE #StaffVarIDTbl table(ID int)
INSERT INTO [dbo].[StaffVariables] (StaffID, VariableTypeID, VariableIntValue)
OUTPUT INSERTED.[StaffVariableID] INTO #StaffVarIDTbl
VALUES (#StaffID, #VariableTypeID, #VariableIntValue)
SELECT #StaffVarID = ID FROM #StaffVarIDTbl
END
END
-- Cutting out the section where I deal with other variable types besides int here - not relevant to this problem
END TRY
BEGIN CATCH
DECLARE #ErrorMessage NVARCHAR(4000);
DECLARE #ErrorSeverity INT;
DECLARE #ErrorState INT;
SELECT
#ErrorMessage = ERROR_MESSAGE(),
#ErrorSeverity = ERROR_SEVERITY(),
#ErrorState = ERROR_STATE();
-- Use RAISERROR inside the CATCH block to return error
-- information about the original error that caused
-- execution to jump to the CATCH block.
RAISERROR (#ErrorMessage, -- Message text.
#ErrorSeverity, -- Severity.
#ErrorState -- State.
);
END CATCH;
END
Here's the test procedure run code:
DECLARE #return_value int
EXEC #return_value = [dbo].[udp_AddUpdateStaffVariable]
#VariableTypeID = 1,
#VariableIntValue = 10
SELECT 'Return Value' = #return_value
GO
...and here's the response:
Msg 50000, Level 16, State 2, Procedure dbo.udp_AddUpdateStaffVariable, Line 130 [Batch Start Line 2]
Cannot insert the value NULL into column 'StaffID', table 'SnippingDbName.dbo.StaffVariables'; column does not allow nulls. INSERT fails.
(1 row affected)
Completion time: 2020-06-01T21:17:08.2049072-05:00
So... here's the question. The error seems to indicate that it either never ran the whole, if #StaffID = NULL portion of the code, or it did, and didn't find the StaffID and set the #StaffID variable. But if that were the case, why can't I see the results of my earlier RAISERROR statements?
I initially tried PRINT and switched to RAISERROR when PRINT wasn't working.
SQL Server 2017 Developer Edition, SSMS 15.0.18183.0
It was a syntax error, that people commenting on the question figured out. IF (#StaffID = NULL) should have been, IF (#StaffID IS NULL) Fixing that in all places in the procedure fixed the problem, and altering my test Staff record so UserName doesn't match SUSER_SNAME() resulted in the expected error.
I have to check if a specific value of an insert is null. If it is null, I want to give it a value from a stored procedure. This procedure returns a number like a sequence, but because I have SQL Server 2008 I had to create it myself:
CREATE PROCEDURE dbo.Get_BAV_PERSONALARCHIV_SEQUENCE ( #value BIGINT OUTPUT)
AS
BEGIN TRANSACTION;
INSERT dbo.BAV_Personalarchiv_Sequence WITH (TABLOCKX) DEFAULT VALUES;
ROLLBACK TRANSACTION;
SELECT #value = SCOPE_IDENTITY();
GO
I want to use the created value in my Insert Trigger if 'SYSROWID' is null (if it is null, it should be the only record with it) :
ALTER TRIGGER [dbo].[NT_BAV_PERSONALARCHIV_MITARBEITER_INSERT]
ON [dbo].[NT_BAV_PERSONALARCHIV_MITARBEITER]
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON;
If (SELECT SYSROWID FROM INSERTED) IS NULL
Begin
DECLARE #value BIGINT;
EXECUTE dbo.Get_BAV_PERSONALARCHIV_SEQUENCE #value OUTPUT;
Update dbo.NT_BAV_PERSONALARCHIV_MITARBEITER
SET SYSROWID = #value
where SYSROWID IS NULL
End
END
But sadly it returns with an 3609 error and rolls back the transaction.
Testing only the Execute and Update works without a problem:
DECLARE #value BIGINT;
EXECUTE dbo.Get_BAV_PERSONALARCHIV_SEQUENCE #value OUTPUT;
Update dbo.NT_BAV_PERSONALARCHIV_MITARBEITER
SET SYSROWID = #value
where SYSROWID IS NULL
What am I missing? Thank you for your help!
Ok, I found out that the rollback in the procedure causes the trigger to end with the error. My workaround is to delete the values of the "sequence"-table before I create a new value:
ALTER PROCEDURE dbo.Get_BAV_PERSONALARCHIV_SEQUENCE ( #value BIGINT OUTPUT)
AS
delete from dbo.BAV_Personalarchiv_Sequence
INSERT dbo.BAV_Personalarchiv_Sequence WITH (TABLOCKX) DEFAULT VALUES;
SELECT #value = SCOPE_IDENTITY();
GO
Is it possible to create a trigger on SQL Server that will execute when a column value is updated to a specific value. How can I do this?
Try with following
CREATE TRIGGER trgAfterUpdate ON [dbo].[Employee_Test]
FOR UPDATE
AS
declare #empid int;
declare #empname varchar(100);
declare #empsal decimal(10,2);
declare #audit_action varchar(100);
select #empid=i.Emp_ID from inserted i;
select #empname=i.Emp_Name from inserted i;
select #empsal=i.Emp_Sal from inserted i;
if update(Emp_Name)
set #audit_action='Updated Record -- After Update Trigger.';
if update(Emp_Sal)
set #audit_action='Updated Record -- After Update Trigger.';
insert into Employee_Test_Audit(Emp_ID,Emp_Name,Emp_Sal,Audit_Action,Audit_Timestamp)
values(#empid,#empname,#empsal,#audit_action,getdate());
PRINT 'AFTER UPDATE Trigger fired.'
GO
I write a simple trigger and hoping that it helps you.
CREATE TRIGGER [FUND].[TRU_FUND_FUND_PRICE] ON [FUND].[FUND_PRICE]
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON;
DECLARE #PRICE_ID BIGINT
DECLARE #UNIT_PRICE DECIMAL(18,7)
DECLARE #UNIT_PRICE_BEFORE_UPDATE DECIMAL(18,7)
DECLARE #TOTAL_SHARE_BEFORE_UPDATE DECIMAL(18,7)
DECLARE #TOTAL_SHARE DECIMAL(18,7)
DECLARE #RECORD_STATUS_BEFORE_UPDATE CHAR(1)
DECLARE #RECORD_STATUS CHAR(1)
SELECT #UNIT_PRICE_BEFORE_UPDATE=UNIT_PRICE , #TOTAL_SHARE_BEFORE_UPDATE = TOTAL_SHARE, #RECORD_STATUS_BEFORE_UPDATE =RECORD_STATUS FROM DELETED
SELECT #PRICE_ID = PRICE_ID,#UNIT_PRICE=UNIT_PRICE,#TOTAL_SHARE=TOTAL_SHARE,#RECORD_STATUS=RECORD_STATUS FROM INSERTED
--You can compare to specific values instead of deleted data
IF #UNIT_PRICE=#UNIT_PRICE_BEFORE_UPDATE AND #TOTAL_SHARE =#TOTAL_SHARE_BEFORE_UPDATE AND #RECORD_STATUS_BEFORE_UPDATE = #RECORD_STATUS
BEGIN
RETURN
END
INSERT INTO FUND.FUND_PRICE_LOG (
[PRICE_ID],
[FUND_ID],
[PRICE_DATE],
[UNIT_PRICE],
[UPDATE_USER_CODE],
[UPDATE_PROG_CODE],
[UPDATE_DATE],
[OPERATION],
[RECORD_STATUS],
[TOTAL_SHARE],
[LIMIT_CHANGE_AMOUNT]
)
SELECT [PRICE_ID], [FUND_ID], [PRICE_DATE],
[UNIT_PRICE],
[UPDATE_USER_CODE],
[UPDATE_PROG_CODE],
GETDATE(),
'U',
[RECORD_STATUS],
[TOTAL_SHARE] ,
([TOTAL_SHARE] - #TOTAL_SHARE_BEFORE_UPDATE)
FROM INSERTED
END
GO
I have the following SQL Server stored procedure :
BEGIN TRAN
CREATE TABLE #TempTable (
SampleOrderID int,
SampleOrderNo varchar(512),
ChallanNoAndChallanDate varchar(MAX)
)
CREATE NONCLUSTERED INDEX #IX_Temp2_1 ON #TempTable(SampleOrderID)
DECLARE
#SQL as varchar(MAX)
SET #SQL=' SELECT SampleOrderID, SampleOrderNo FROM SampleOrder WHERE SampleOrderID IN (37808,37805,37767,37571,37745,37772,37843,37394,37909,37905,37903) '
INSERT INTO #TempTable (SampleOrderID, SampleOrderNo)
EXEC (#SQL)
DECLARE
#SampleOrderID as int,
#ChallanNoAndChallanDate as varchar(max)
DECLARE Cur_AB1 CURSOR GLOBAL FORWARD_ONLY KEYSET FOR
SELECT SampleOrderID FROM #TempTable
OPEN Cur_AB1
FETCH NEXT FROM Cur_AB1 INTO #SampleOrderID
WHILE(##Fetch_Status <> -1)
BEGIN--2
SET #ChallanNoAndChallanDate=''
SELECT #ChallanNoAndChallanDate= COALESCE(#ChallanNoAndChallanDate+ ',', '') + CONVERT(VARCHAR(12),ChallanDate,106)+':'+ChallanNo FROM Challan WHERE OrderID =#SampleOrderID AND OrderType=2
UPDATE #TempTable SET ChallanNoAndChallanDate=#ChallanNoAndChallanDate WHERE SampleOrderID=#SampleOrderID
FETCH NEXT FROM Cur_AB1 INTO #SampleOrderID
END--2
CLOSE Cur_AB1
DEALLOCATE Cur_AB1
SELECT * FROM #TempTable
DROP TABLE #TempTable
COMMIT TRAN
Output :
SamID SamNo ChallanNoAndDaet
37394 37394 ,31 May 2012:151592
37571 37571 ,31 May 2012:151580
37745 37745 ,31 May 2012:151582
37767 37767 ,30 May 2012:151507,31 May 2012:151576
37772 37772 ,31 May 2012:151587
37805 37805 ,31 May 2012:151574
37808 37808 ,31 May 2012:151573
37843 37843 ,31 May 2012:151588
37903 37903 ,31 May 2012:151597
37905 37905 ,31 May 2012:151596
37909 37909 ,31 May 2012:151593
It works successfully for small volume of data but When i try to execute it on a Large volume (i.e. more then 500,000 record) my C# interface throws the time out exception.
Can anyone help me edit my stored procedure to avoid the cursor?
Thanks for response.
I use this to avoid cursor in everywhere I need
DECLARE #num_rows int
DECLARE #cnt int
DECLARE #selected int
DECLARE #table1 TABLE (Id int not null primary key identity(1,1), col1 int )
INSERT into #table1 (col1) SELECT col1 FROM table2
SET #num_rows=##ROWCOUNT
SET #cnt=0
WHILE #cnt<#num_rows
BEGIN
SET #cnt=#cnt+1
SELECT
#selected=col1
FROM #table1
WHERE Id=#cnt
--do your stuff here--
END
I usually use something like the following:
SELECT #SampleOrderID = MIN (SampleOrderID) FROM #TempTable
WHILE #SampleOrderID IS NOT NULL
BEGIN
SET #ChallanNoAndChallanDate=''
SELECT #ChallanNoAndChallanDate= COALESCE(#ChallanNoAndChallanDate+ ',', '') + CONVERT(VARCHAR(12),ChallanDate,106)+':'+ChallanNo FROM Challan WHERE OrderID =#SampleOrderID AND OrderType=2
UPDATE #TempTable SET ChallanNoAndChallanDate=#ChallanNoAndChallanDate WHERE SampleOrderID=#SampleOrderID
SELECT #SampleOrderID = MIN (SampleOrderID) FROM #TempTable WHERE SampleOrderID > #SampleOrderID
END
This code would replace the cursor stuff you have.