I use BULK INSERT WITH BATCHSIZE OPTION.
How can I Get Committed Count When the BULK INSERT fail in processing.
like:
BEGIN TRY
BULK INSERT t1 FROM "C:\temp\temp.dat" WITH(BATCHSIZE=1000)
END TRY
BEGIN CATCH
PRINT CONVERT(VARCHAR, ##rowcount)
END CATCH
the ##rowcount returned 0
You could count the table rows first into a variable.
DECLARE #cntBefore bigint;
SELECT #cntBefore = COUNT(*) FROM t1;
BEGIN TRY
BULK INSERT t1 FROM "C:\temp\temp.dat" WITH(BATCHSIZE=1000)
END TRY
BEGIN CATCH
DECLARE #cntAfter bigint;
SELECT #cntAfter = COUNT(*) FROM t1;
PRINT 'Imported ' + CONVERT(VARCHAR, #cntAfter-#cntBefore)
END CATCH
Alternatively, use ROWS_PER_BATCH to optimise the import, then all the rows will be rolled back.
Related
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.
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
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
SET XACT_ABORT ON
BEGIN TRY
BEGIN TRANSACTION
UPDATE [Members] SET [Count] = [Count] - 1 WHERE [Count] > 0;
**return 0 here if this statement updated 0 rows**
INSERT INTO [Sessions] ([Name], [Etc]) VALUES ('Test', 'Other')
COMMIT TRANSACTION
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0
ROLLBACK TRANSACTION;
RETURN 0
END CATCH
SELECT SCOPE_IDENTITY(); **only return the identity if the first statement updated 1 row and no exceptions occurred.**
With the code above, is there a way to return 0 if the first UPDATE updated no rows? I only want the INDENTITY() if the first UPDATE edits a row and no errors occur. Is this possible?
You can use ##ROWCOUNT to get the number changed. This should work:
UPDATE [Members] SET [Count] = [Count] - 1 WHERE [Count] > 0;
IF ##ROWCOUNT = 0
BEGIN
COMMIT TRANSACTION;
RETURN;
END;
Because no rows are updated, I suppose rolling back the transaction would be equivalent.
To get the identify values inserted, I would recommend that you learn to use the OUTPUT clause in INSERT (documented here). This is a good habit to get into, although the syntax is a bit more cumbersome (you have to define a table/table variable to store the inserted values). It has no race conditions and it allows you return multiple identity values.
Only read SCOPE_IDENTITY if you actually INSERT
This way, you have one exit point only
BEGIN TRY
BEGIN TRANSACTION
UPDATE [Members] SET [Count] = [Count] - 1 WHERE [Count] > 0;
IF ##ROWCOUNT = 1
BEGIN
INSERT INTO [Sessions] ([Name], [Etc]) VALUES ('Test', 'Other')
SET #rtn = SCOPE_IDENTITY();
END
SET #rtn = 0;
COMMIT TRANSACTION;
SELECT #rtn
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0 ROLLBACK TRANSACTION;
END CATCH
I have 2 tables. I need to check before insertion in one table if the value exist in other table.
I suggest that, you should first check the records that is going to be inserted in each request.
Create Proc Testing
as
Set NoCount ON
Set XACT_ABORT ON
Begin Try
Begin Tran
IF Not Exists(SELECT 1 FROM Table2 i JOIN Table1 t ON i.key = t.key)
Begin
//Your insert statement
END
Commit Tran
End Try
Begin Catch
Rollback Tran
End Catch
IF NOT EXISTS ( SELECT * FROM TableA WHERE Col1 = #Value)
INSERT INTO TableB(Col1) SELECT #Value
Using an INSERT trigger perhaps?
I'm not very sure about the syntax.
CREATE TRIGGER InsertTableTrigger ON Table1 FOR INSERT
AS
BEGIN
IF EXISTS ( SELECT 1 FROM Inserted i JOIN Table1 t ON i.key = t.key )
BEGIN
RAISERROR('Transaction Failed.',16,1)
ROLLBACK TRAN "insert on Table1"
END
END
GO
Inserted is used to access the inserting values.