THROW ERROR Still Executes Query And Inserts Data Into Table - sql

Basically, I am trying to compare a date the user will enter and if it is greater than todays date (GETDATE()), then it throws the error and doesn't enter the data. The query throws the error but it still enters the data with the result at the bottom.
USE EMR
GO
IF EXISTS (SELECT DB_ID('CheckDate'))
DROP TRIGGER CheckDate
GO
CREATE TRIGGER CheckDate
ON VISIT
AFTER INSERT, UPDATE
AS
BEGIN TRAN
DECLARE #ErrorMessage VARCHAR(200)
DECLARE #Date VARCHAR(20) = CAST ((SELECT CONVERT (DATE, GETDATE())) AS VARCHAR(20))
SET #ErrorMessage = 'Date Must Be On Or Before ' + #Date + '';
DECLARE #CheckDate DATE = (SELECT Date_Of_Service FROM inserted);
IF CAST((#CheckDate) AS DATE) <= CAST(GETDATE() AS DATE)
COMMIT TRAN
ELSE
RAISERROR(#ErrorMessage, 1, 1)
This is my insert statement:
INSERT INTO VISIT (PK_VISIT_ID, Date_Of_Service)
VALUES (02913, '2018-12-03')
And get this:
Date Must Be On Or Before 2016-02-17
Msg 50000, Level 1, State 1
(1 row(s) affected)

You are raising error with severity = 1 which means to server that it's just info message.Check out replies to this post: TSQL: Prevent trigger suppressing error but rolling back transactionThere is also a link to severity table on msdn.

Your code is giving doing insert even after raiserror because trigger for insert runs the trigger after the rows are inserted.You can use check constraint or instead of insert as follows:
alter trigger tr2 on bb
instead of insert
as
begin
begin try
begin transaction
if exists(select * from inserted where date1 > getdate())
begin
raiserror('date greater then today''s date',16,1)
end
else
begin
insert into bb
select * from inserted
if ##trancount > 0
commit transaction
end
end try
begin catch
declare #msg varchar(100) = error_message()
if ##trancount > 0
begin
raiserror(#msg,16,1)
rollback transaction
end
end catch
end

Related

How to union multiple select statements while they are not near together?

I have an sql query which check for existence of some records, if those records exist rise error for them otherwise insert them to database. In my query as I need to return error messages for every record, I need to select some custom texts, problem is that they are showing as separate tables, not in one table, which I want (as I am calling this query from nodejs app and it returns an array for me so it only returns first table (error message) for me).
I searched and reach these two options:
1- Use UNION (which is not solving my case)
2- Insert all records in another table and then get all it's record (which isn't beautiful! :) )
DECLARE #errorCOUNT int
SET #errorCOUNT = 0
BEGIN TRANSACTION [Tran1]
IF EXISTS (SELECT * FROM Categories WHERE CategoryName = 'myCat1')
BEGIN
SELECT 'This is error for is = 4' As err
SET #errorCOUNT = #errorCOUNT + 1
END
ELSE
BEGIN
INSERT INTO Categories VALUES ('myCat1')
END
----------------------------
IF EXISTS (SELECT * FROM Categories WHERE CategoryName = 'myCat2')
BEGIN
SELECT 'This is error for is = 5' AS err
SET #errorCOUNT = #errorCOUNT + 1
END
ELSE
BEGIN
INSERT INTO Categories VALUES ('myCat2')
END
----------------------------
IF #errorCOUNT > 0
BEGIN
ROLLBACK TRANSACTION [Tran1]
END
ELSE
BEGIN
COMMIT TRANSACTION [Tran1]
END
As I mentioned I want all these select statements to be shown in one table so they return to my server as one array.
I just think it is good to mention that my query completes in a loop, so it may have different amount of IF...ELSE (between --- lines).
I hope I was clear. Thanks in advance.
Try this one, would work:
BEGIN TRANSACTION [Tran1]
DECLARE #err AS TABLE ( msg NVARCHAR(MAX) NOT NULL )
DECLARE #errorCOUNT AS INT = 0
IF EXISTS (SELECT * FROM Categories WHERE CategoryName = 'myCat1')
BEGIN
INSERT INTO #err (msg) VALUES ('This is error for is = 4')
SET #errorCOUNT = #errorCOUNT + 1
END
ELSE
BEGIN
INSERT INTO Categories VALUES ('myCat1')
END
IF EXISTS (SELECT * FROM Categories WHERE CategoryName = 'myCat2')
BEGIN
INSERT INTO #err (msg) VALUES ('This is error for is = 5')
SET #errorCOUNT = #errorCOUNT + 1
END
ELSE
BEGIN
INSERT INTO Categories VALUES ('myCat2')
END
IF #errorCOUNT > 0
BEGIN
SELECT * FROM #err
ROLLBACK TRANSACTION [Tran1]
END
ELSE
BEGIN
COMMIT TRANSACTION [Tran1]
END
I don't understand what you're really want to do there, but here is a tip using MERGE statement and OUTPUT clause maybe it's what you're after
DECLARE #T TABLE(CategoryName VARCHAR(45));
MERGE INTO T
USING (VALUES('MyCat1'), ('MyCat2')) TT(CategoryName)
ON T.CategoryName = TT.CategoryName -- Or <> instead of =
WHEN NOT MATCHED THEN
INSERT VALUES(TT.CategoryName)
OUTPUT TT.CategoryName INTO #T;
SELECT CASE WHEN CategoryName = 'MyCat1'
THEN 'This is error for is = 4'
WHEN CategoryName = 'MyCat2'
THEN 'This is error for is = 5'
END Res
FROM #T;
Also, I don't think you need to the #ErrorCount variable, since you already have ##ROWCOUNT which you can use it instead.
Here is a db<>fiddle where you can see how it's working.

catching error in loop sql query

I have the below insert query which selects records from the OriginalData table where everything is nvarchar(max) and inserts it into the Temp table which has specific field definitions i.e MainAccount is INT.
I am doing a row by row insert because if there is a record in OriginalData table where the MainAccount value is 'Test' the it will obviously cause a conversion error and the insert will fail.
I want to be able to capture this error. There is a field on the originalData table called "error" which I want to populate. However I want this to run thru the entire table as oppose to fail on the first error and stop.
DECLARE #RowId INT
, #MaxRowId INT
Set #RowId = 1
Select #MaxRowId = 60
WHILE(#RowId <= #MaxRowId)
BEGIN
INSERT INTO [Temp] (ExtractSource, MainAccount,RecordLevel1Code, RecordLevel2Code, RecordTypeNo, TransDate, Amount, PeriodCode, CompanyCode)
SELECT ExtractSource, MainAccount,RecordLevel1Code, RecordLevel2Code,RecordTypeNo,TransDate, Amount, PeriodCode, DataAreaId
FROM [OriginalData]
WHERE RowId = #RowId
PRINT #RowId
SET #RowId = #RowId + 1
END
select * from [Temp]
You should use TRY..CATCH block:
WHILE(#RowId <= #MaxRowId)
BEGIN
BEGIN TRY
INSERT INTO [Temp] (ExtractSource, MainAccount,RecordLevel1Code,
RecordLevel2Code, RecordTypeNo, TransDate, Amount, PeriodCode, CompanyCode)
SELECT ExtractSource, MainAccount,RecordLevel1Code, RecordLevel2Code,
RecordTypeNo,TransDate, Amount, PeriodCode, DataAreaId
FROM [OriginalData]
WHERE RowId = #RowId;
PRINT #RowId;
END TRY
BEGIN CATCH
-- error handlingg
END CATCH
SET #RowId += 1;
END

Unable to insert into table with Identity Column

I have this procedure:
create proc insertfactors_pf
(
#FactorID int,
#CustomersID int,
#Number int,
#TotalPrice decimal(18, 0),
#PaidPrice decimal(18, 0),
#Date Date,
#ProductID int,
#QTY int
)
AS
BEGIN TRANSACTION
SET IDENTITY_INSERT facetors on
INSERT INTO Factor VALUES (#FactorID, #CustomersID, #Number, #TotalPrice, #PaidPrice,#Date)
SET IDENTITY_INSERT factors off
IF ##ERROR <> 0
BEGIN
ROLLBACK
RETURN
END
SET IDENTITY_INSERT Product_Factor on
INSERT INTO Produc_Factor values(#FactorID,#ProductID,#QTY)
SET IDENTITY_INSERT Product_Factor off
IF ##ERROR <> 0
BEGIN
ROLLBACK
RETURN
END
COMMIT
But when I run it I get this error:
Msg 8101, Level 16, State 1, Procedure insertfactors_pf, Line 20 [Batch Start Line 0]
An explicit value for the identity column in table 'Factor' can only be specified when a column list is used and IDENTITY_INSERT is ON.
What am I doing wrong?
The error message seems pretty clear: FactorId is an identity column. You shouldn't set the value for FactorID yourself. Sql Server will set it for you. But if you really want to set it for some insane reason, you need to include a column list in the query like this:
SET IDENTITY_INSERT facetors on
INSERT INTO Factor
(FactorID, CustomerID, Number, TotalPrice, PaidPrice, Date)
VALUES
(#FactorID, #CustomersID, #Number, #TotalPrice, #PaidPrice,#Date)
SET IDENTITY_INSERT factors off
Even better, you should do something more like this, where you don't have to mess with identity insert issues:
create proc insertfactors_pf
(
#CustomersID int,
#Number int,
#TotalPrice decimal(18, 0),
#PaidPrice decimal(18, 0),
#Date Date,
#ProductID int,
#QTY int
)
AS
--Move this to inside the procedure definition. Don't ask for it as an argument
DECLARE #FactorID int
BEGIN TRANSACTION
--Don't mention FactorID anywhere here. Sql Server will take care of it
INSERT INTO Factor
(CustomersID, Number, TotalPrice, PaidPrice, Date)
VALUES
(#CustomersID, #Number, #TotalPrice, #PaidPrice,#Date);
IF ##ERROR <> 0
BEGIN
ROLLBACK
RETURN
END
--use scope_idenity() to get the FactorID value Sql Server just created
SELECT #FactorID = scope_identity();
INSERT INTO Produc_Factor
(FactorID, ProductID, Qty)
VALUES
(#FactorID,#ProductID,#QTY)
IF ##ERROR <> 0
BEGIN
ROLLBACK
RETURN
END
COMMIT

Insertion of records based on some condition

I'm trying to insert few records from the temporary table using a SQL Server stored procedure. There is a percentage column in the temporary table and a PQ number column. In a table there may exists more than 1 row with the same PQ number. But for insertion to happen the sum of percentage for the same PQ number should be 100%. I couldn't write the where clause for this situation.
CREATE PROCEDURE [dbo].[Upsert_DebitSheet]
#filename VARCHAR(250)
AS
BEGIN
SET XACT_ABORT ON
RETRY: -- Label RETRY
BEGIN TRANSACTION
BEGIN TRY
SET NOCOUNT ON;
INSERT INTO [dbo].[DebitSheet]([Date], [RMMName], [Invoice],[PQNumber], [CAF],
[Percentage], [Amount], [FileName])
SELECT
*, #filename
FROM
(SELECT
[Date], [RMMName], [Invoice], [PQNumber], [CAF],
[Percentage], [Amount]
FROM
[dbo].[TempDebitSheet]
WHERE) result
SELECT ##ROWCOUNT
TRUNCATE TABLE [dbo].[TempDebitSheet]
COMMIT TRANSACTION
END TRY
BEGIN CATCH
PRINT ERROR_MESSAGE()
ROLLBACK TRANSACTION
IF ERROR_NUMBER() = 1205 -- Deadlock Error Number
BEGIN
WAITFOR DELAY '00:00:00.05' -- Wait for 5 ms
GOTO RETRY -- Go to Label RETRY
END
END CATCH
SET ROWCOUNT 0;
END
Temporary Table
MainTable(Expected Result)
You can use subquery in the WHERE
INSERT INTO [dbo].[DebitSheet]
([Date]
,[RMMName]
,[Invoice]
,[PQNumber]
,[CAF]
,[Percentage]
,[Amount]
,[FileName])
SELECT [Date]
,[RMMName]
,[Invoice]
,[PQNumber]
,[CAF]
,[Percentage]
,[Amount]
FROM [dbo].[TempDebitSheet]
WHERE EXISTS (
SELECT tmp.[PQNumber]
FROM [dbo].[TempDebitSheet] tmp
WHERE tmp.[PQNumber] = [TempDebitSheet].[PQNumber]
GROUP BY tmp.[PQNumber]
HAVING SUM(tmp.[Percentage]) = 100
)
Modify your query like this
Insert into ...
Select result.*, #filename from (....) result

How can I create a stored procedure for a log table

I am looking for an answer to create a stored procedure for a log table. Below is the scenario for which I need to write that SP.
Whenever user logs in or logs out store LOG-IN and LOG-OUT time with user's unique LOGIN-ID.
Also I have to restrict the user from logging in if users previous session is terminated abnormally or user closed the application without proper log out then user needs to wait for 10 mins before next login.
This is what I tried:
ALTER PROCEDURE USP_CHECKER(#LOGINID VARCHAR(70))
AS
BEGIN
DECLARE #LASTID INT,#CURRENTTIME DATETIME,#TEMP INT,#HAS VARCHAR(30)
SELECT TOP 1 SLNO, #TEMP=(DATEDIFF(MI,LOGINDATETIME,GETDATE())) FROM TBL_LOGINDETAILS ORDER BY SlNo DESC
SET #HAS=(SELECT LoginID FROM TBL_LOGINDETAILS WHERE LogoutDateTime IS NULL)
IF #HAS IS NOT NULL
BEGIN
IF(#TEMP >= '10' )
BEGIN
SET #CURRENTTIME = (SELECT DATEADD(MI, -10, GETDATE()))
UPDATE TBL_LOGINDETAILS SET LogoutDateTime=#CURRENTTIME WHERE SLNO=#LASTID
END
ELSE
RETURN #LASTID
--PREVENT FROM LOGIN
END
ELSE
INSERT INTO TBL_LOGINDETAILS (LoginID,LoginDateTime,isLogged) VALUES(#LOGINID,GETDATE(),'1')
END
The one thing that jumps out is the fact you have a variable called #TEMP of type INT - but you're comparing as if it were a string:
IF(#TEMP >= '10' ) -- unnecessary single quotes! You're comparing against a string literal...
BEGIN
SET #CURRENTTIME = (SELECT DATEADD(MI, -10, GETDATE()))
UPDATE TBL_LOGINDETAILS SET LogoutDateTime=#CURRENTTIME WHERE SLNO=#LASTID
END
It's an INT - so you need to compare it against a numeric value:
IF(#TEMP >= 10) -- **NO** unnecessary single quotes....
BEGIN
UPDATE dbo.TBL_LOGINDETAILS
SET LogoutDateTime = DATEADD(MI, -10, GETDATE())
WHERE SLNO = #LASTID
END
Also - I'm sure you can find a more suitable, more meaningful and more obvious name for this variable than #TEMP ....
this is what i tried.
USE [DB]
GO
/****** Object: StoredProcedure [dbo].[USP_SetLoginStatus] Script Date: 11/28/2011 10:58:47 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
ALTER PROCEDURE [dbo].[USP_SetLogStatus]
-- Add the parameters for the stored procedure here
#PLoginName Varchar(75)
AS
BEGIN
DECLARE #SNO INT --VARIABLE TO HOLD THE SERAIL NUMBER
,#ISLOGGED BIT --HOLDING IS LOGGED STATUS
,#TIMEDIFF VARCHAR(50) --VARIABLE TO HOLD TIME DIFFERENCE BETWEEN CURRENT LOGIN AND LAST LOG OUT TIME
-- SELECT QUERY FOR GETTING #TIMEDIFF AND SERIAL-NO VALUES FOR USER
SELECT #SNO=[SlNo],#ISLOGGED=[isLogged] ,#TIMEDIFF =DATEDIFF(MI,
(SELECT TOP (1) [LoginDateTime] FROM TBL_LOGINDETAILS WHERE [LogoutDateTime] IS NULL ORDER BY [SlNo] ),
GETDATE()) FROM TBL_LOGINDETAILS WHERE [LoginID]=#PLoginName
--IF USER IS LOGIN FIRST TIME
IF NOT EXISTS( SELECT isLogged FROM TBL_LOGINDETAILS WHERE [LoginID]=#PLoginName)
INSERT INTO TBL_LOGINDETAILS(LoginID,isLogged,LoginDateTime) VALUES(#PLoginName,'1',GETDATE())
ELSE
BEGIN
IF (#TIMEDIFF >=10) --LOGOUT TIME DIFF IS MORE THAN OR EQUALS TO 10 MINUTE
BEGIN
----- UPDATING LOGOUT AND IS LOGGED FIELD
IF(#ISLOGGED =1 )
BEGIN
UPDATE TBL_LOGINDETAILS SET [LOGOUTDATETIME]= (SELECT DATEADD(MI, -10, GETDATE()) ), [isLogged]=0 WHERE [LoginID]=#PLoginName
AND [SlNo]=#SNO
----- INSERTING NEW LOG FOR CURRENT USER
INSERT INTO TBL_LOGINDETAILS([LoginID],[LoginDateTime],[isLogged]) VALUES(#PLoginName,GETDATE(),1)
END
ELSE
UPDATE TBL_LOGINDETAILS SET [LOGOUTDATETIME]= GETDATE() , [isLogged]=0 WHERE [LoginID]=#PLoginName AND [SlNo]=#SNO
END
ELSE
RETURN '0'
END
END