How to locate the problem row while using insert into select syntax - sql

It is a SQL Server procedure. Some error happens while using insert into xxxx select xxx from syntax. There are around thousands of rows in this action. Some field in one row in this batch is too long for the table's column capacity. So, the whole action just stopped and rolled back without telling which row is not suitable.
Is there any way to locate this problem row? I try to use try catch, and it doesn't tell the right number.
ALTER PROCEDURE [dbo].[MigratePriceHistory]
AS
BEGIN
BEGIN TRY
insert into xxxxx select xxx from xxx
END TRY
BEGIN CATCH
PRINT ERROR_MESSAGE()
PRINT ERROR_NUMBER()
END CATCH
END

Related

How to use the rollback command in SQL

I am trying to use rollback command in sql but it is not working.
USE MFF
BEGIN TRANSACTION
BEGIN TRY
INSERT INTO dbo.people
VALES ('Nick', 1)
COMMIT TRANSACTION
END TRY
BEGIN CATCH
/* OcurriĆ³ un error, deshacemos los cambios*/
ROLLBACK TRANSACTION
PRINT 'An error has occurred!'
END CATCH
I misspelled the word "vales" on purpose so that it goes through the catch part, but it comes out earlier. Do I have to enable something in the database?
My need is to rollback a larger script and I want to see how it works in case of an error.
You're trying to use a try/catch on a syntax error. That won't work because your query won't even parse correctly.
You need to do the try/catch on a valid query. Here is an example of using your logic with a slightly different people table:
create table #people (firstname varchar(100), ID INT PRIMARY KEY)
GO
--run this twice to see it succeed the first time, and throw your print error on the 2nd run.
BEGIN TRANSACTION
BEGIN TRY
insert into #people
values
('Nick',1)
COMMIT TRANSACTION
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION
PRINT 'An error has occurred!'
END CATCH

Transaction doesn't rollback even there was an error?

I created this transaction (my first transaction), there is no row with Id = 111 in Teacher table, however it works on the Subject table and inserted a new row. Shouldn't it rollback all the changes?
BEGIN TRANSACTION
INSERT INTO Subject (Name, SupervisorId) VALUES('Statistics', 4)
UPDATE Teacher SET Name ='Hady' WHERE Id=111
COMMIT TRANSACTION
I then add some more logic to it, but still not working:
Begin Try
BEGIN TRANSACTION
INSERT INTO Subject (Name, SupervisorId) VALUES('Statistics', 4)
UPDATE TeacherO SET Name ='Hady' WHERE Id=111
COMMIT TRANSACTION
End Try
Begin Catch
ROLLBACK TRANSACTION
End Catch
If I understand this correctly, you assume, that the attempt to update a row in teacher table when there is no row with Id = 111 in Teacher table was an error...
An error occurs, when you do something forbidden or impossible. If you try to add a number to a string without casts. Or if you try to get hands on a not exisiting object. Maybe you want to convert 30.02.2016 to date. All will be an error.
But if you tell the database to update all rows where the id=111, exactly this happens: All rows with id=111 will be updated. The count of affected rows will be zero in your case. But this is not an error...
You have to use XACT_ABORT set option to ensure it rolls back. Without it some errors will not cause a roll back.
SET XACT_ABORT ON;
BEGIN TRY
BEGIN TRANSACTION;
INSERT INTO Subject (Name, SupervisorId) VALUES('Statistics', 4);
UPDATE TeacherO SET Name ='Hady' WHERE Id=111;
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION;
END CATCH

How can I check whether a stored procedure that deletes all rows from multiple tables succeeded?

I want a stored procedure that does the equivalent of the following
CREATE PROCEDURE Reset
AS
BEGIN
DELETE * FROM SomeTable;
DELETE * FROM SomeOtherTable;
END
and also returns some indicator of success or failure. How would I do that? Only way I can think of is pre-calculating the number of rows that should be affected, but that seems so shoddy.
The following will produce a success/failure indicator based on the question, 'did the table reset complete without failure?'. It's wrapped in a transaction so that both deletes happen or none happen, which keeps your data clean.
BEGIN TRY
BEGIN TRANSACTION;
DELETE FROM SomeTable;
DELETE FROM SomeOtherTable;
COMMIT TRANSACTION;
-- success indicator
SELECT 1 AS Result
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0
ROLLBACK TRANSACTION;
-- failure indicator
SELECT 0 AS Result
END CATCH

T-SQL could not rollback

I have some code that has a purely sequential flow, without transaction.
I sandwich them with a begin transaction and commit transaction
begin transaction
......--My code here......
......
......--code to create Table1
......
ALTER TABLE [dbo].[Table1] WITH CHECK ADD CONSTRAINT [FK_constraint] FOREIGN KEY([field1], [field2])
REFERENCES [dbo].[Table2] ([field3], [field4])
GO
....
......--End of My code here......
rollback transaction
commit transaction
when i run the script until just above "rollback transaction" in management studio, if a simple error occurs such as division by zero, I run "rollback transaction", all changes are rolledback without problem.
But if the alter table statement fails because Table2 doesn't exist, it then triggers further errors.
Msg 1767, Level 16, State 0, Line 2
Foreign key 'FK_Constraint references invalid table 'dbo.Table2'.
Msg 1750, Level 16, State 0, Line 2
Could not create constraint. See previous errors.
Msg 1767, Level 16, State 0, Line 2
Foreign key 'FK_xxxxxx' references invalid table 'Table1'.
When I run "rollback transaction", I got this error message "The ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION." which is silly, because I DO HAVE a begin transaction on top!
Please tell me what went wrong. Any help would be much appreciated. Using SQL-Server 2008.
EDIT:
I added
SELECT ##TRANCOUNT;
before and after "ALTER TABLE [dbo].[Table1] WITH CHECK ADD CONSTRAINT"
....
SELECT ##TRANCOUNT;
ALTER TABLE [dbo].[Table1] WITH CHECK ADD CONSTRAINT [FK_constraint] FOREIGN KEY([field1], [field2]) REFERENCES [dbo].[Table2] ([field3], [field4])
GO
SELECT ##TRANCOUNT;
....
The results are 1 and 0 respectively. The alter table automatically rollbacks my transaction on error!? I can't understand this.
I think there's nothing you can do about Sql Server treatment with DDL error severity handling, some of it are handled automatically (forcibly rolling back transaction for example) by Sql Server itself.
What you can just do is make your script code cope around it and provide script users with descriptive error.
An example:
-- drop table thetransformersmorethanmeetstheeye
-- select * from thetransformersmorethanmeetstheeye
-- first batch begins here
begin tran
create table thetransformersmorethanmeetstheeye(i int); -- non-erring if not yet existing
-- even there's an error here, ##ERROR will be 0 on next batch
ALTER TABLE [dbo].[Table1] WITH CHECK ADD CONSTRAINT [FK_constraint] FOREIGN KEY([field1], [field2])
REFERENCES [dbo].[Table2] ([field3], [field4]);
go -- first batch ends here
-- second batch begins here
if ##TRANCOUNT > 0 begin
PRINT 'I have a control here if things needed be committed or rolled back';
-- ##ERROR is always zero here, even there's an error before the GO batch.
-- ##ERROR cannot span two batches, it's always gets reset to zero on next batch
PRINT ##ERROR;
-- But you can choose whether to COMMIT or ROLLBACK non-erring things here
-- COMMIT TRAN;
-- ROLLBACK TRAN;
end
else if ##TRANCOUNT = 0 begin
PRINT 'Sql Server automatically rollback the transaction. Nothing can do about it';
end
else begin
PRINT 'Anomaly occured, ##TRANCOUNT cannot be -1, report this to Microsoft!';
end
-- second batch implicitly ends here
The only way this happens is if there is no open transaction in that SPID.
That's it. And the only way there's no open transaction is that either:
You never started a new transaction after the old one committed or rolled back
You have another commit or rollback somewhere you didn't notice
Something killed your connection or forced a rollback from outside your spid (like a kill command from another session)
You don't provide much code. Is there any error trapping or any other conditional logic in your query that's not shown?
As far as I know, the ALTER TABLE command will create its own new transaction, and when it fails, will rollback that transaction. A single rollback within a proc will cause all the open transactions within that proc to be rolled back. So you're seeing the error because the failure of the ALTER TABLE statement is implicitly rolling back your transaction before you try to do it..
You can confirm this easily enough by checking the #TRANCOUNT within your code, and only calling rollback when it is not zero
The error from the ALTER TABLE statement is a compile error rather than a runtime error - and so the whole batch in which that statement occurs is never executed. I'm guessing that there's no GO between BEGIN TRANSACTION and ALTER TABLE - hence the BEGIN TRANSACTION never executed, and what SQL Server is telling you is perfectly true.
Try adding a GO immediately after the BEGIN TRANSACTION.
Given this:
create table z
(
i int identity(1,1) not null,
zzz int not null
);
When you try the following..
begin try
begin transaction
alter table z drop column aaa;
commit tran;
end try
begin catch
print 'hello';
SELECT
ERROR_NUMBER() as ErrorNumber,
ERROR_MESSAGE() as ErrorMessage;
IF (XACT_STATE()) = -1
BEGIN
PRINT
N'The transaction is in an uncommittable state. ' +
'Rolling back transaction.'
ROLLBACK TRANSACTION;
END;
end catch
print 'reached';
..the error can be caught:
ErrorNumber ErrorMessage
4924 ALTER TABLE DROP COLUMN failed because column 'aaa' does not exist in table 'z'.
But try changing alter table z drop column aaa; to alter table z add zzz int;, Sql Server can catch the error..
Column names in each table must be unique. Column name 'zzz' in table
'z' is specified more than once.
..but won't yield back the control to you, CATCH block will not be triggered. Seems there's no hard and fast rules what errors are catch-able and which are not.
To illustrate the difference, here's the error catch-able by your code
Here's an error un-catch-able by your code, which is similar to your problem.
Notice that there's no grid there(via SELECT ERROR_NUMBER() as ErrorNumber, ERROR_MESSAGE() as ErrorMessage;). That means, Sql Server did not yield back the control to you after it detected an exception.
Maybe you can see other details here that might help: http://msdn.microsoft.com/en-us/library/ms179296.aspx
See this guideline for error handling ##ERROR and/or TRY - CATCH
By the way, on Postgresql all kind of DDL errors are catch-able by your code.
do $$
begin
-- alter table z drop column aaa;
alter table z add zzz int;
exception when others then
raise notice 'The transaction is in an uncommittable state. '
'Transaction was rolled back';
raise notice 'Yo this is good! --> % %', SQLERRM, SQLSTATE;
end;
$$ language 'plpgsql';
Here's the dev-rendered error message for alter table z drop column aaa; on Postgresql:
Here's the dev-rendered error message for alter table z add zzz int; on Postgresql; which by the way in Sql Server, when it has an error on this type of statement, it won't yield back the control to you, hence your CATCH sections are sometimes useful, sometimes useless.

MySQL Syntax Error

When executing:
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK;
SELECT 1;
END;
DECLARE EXIT HANDLER FOR SQLWARNING
BEGIN
ROLLBACK;
SELECT 1;
END;
-- delete all users in the main profile table that are in the MaineU18 by email address
DELETE FROM ap_form_1 WHERE element_5 IN (SELECT email FROM MaineU18);
-- delete all users from the MaineU18 table
DELETE from MaineU18;
COMMIT;
END;
I get:
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'e1:
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK' at line 2
Any ideas? Thanks.
UPDATE 2:
I have tried putting the script into a PROCEDURE:
DELIMITER |
DROP PROCEDURE IF EXISTS temp_clapro|
CREATE PROCEDURE temp_clapro()
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION, SQLWARNING ROLLBACK;
SET AUTOCOMMIT=0;
-- delete all users in the main profile table that are in the MaineU18 by email address
DELETE FROM ap_form_1 WHERE element_5 IN (SELECT email FROM MaineU18);
-- delete all users from the MaineU18 table
DELETE from MaineU18;
COMMIT;
SET AUTOCOMMIT=1;
END
|
DELIMITER ;
CALL temp_clapro();
I am still having issues:
Query OK, 0 rows affected (0.00 sec)
Query OK, 0 rows affected (2.40 sec)
Query OK, 0 rows affected (2.40 sec)
Query OK, 0 rows affected (2.40 sec)
Query OK, 0 rows affected (2.40 sec)
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'END;
|
DELIMITER ;
CALL temp_clapro()' at line 1
UPDATE 3:
It seems that many of my problems are coming from the fact that I am running the script from a file using the "SOURCE" command. If I only have the DROP and CREATE commands in the file and run the DELIMITER and CALL commands outside the file, everything works without error.
Is there away to run this from a single script file?
You seem to be using BEGIN as the opening of a block of ad hoc statements, as one would do in SQL Server.
MySQL doesn't support this. You can DECLARE only in the body of a stored procedure or stored function or trigger.
http://dev.mysql.com/doc/refman/en/declare.html:
DECLARE is allowed only inside a BEGIN ... END compound statement and must be
at its start, before any other
statements.
http://dev.mysql.com/doc/refman/en/begin-end.html:
BEGIN ... END syntax is used for
writing compound statements, which can
appear within stored programs.
Re your comments and updated question: I don't know why it's failing. I just tried it myself and it worked fine. What version of MySQL are you using?
you're using semicolons with the procedure for statement delimiters, so you have to change the delimiter in your client. see http://dev.mysql.com/doc/refman/5.1/en/stored-programs-defining.html