SQL Server insert after delete Trigger error - sql

I'm trying to insert a row into a new table when a row is deleted from the original table but I keep getting the below error when i run a delete on a record.
I read error message details and it says it has something to do with type of data cannot be inserted but I don't understand this since I copied exact table structure that it was deleted from to be inserted to.
Msg 8152, Level 16, State 13, Procedure DeleteEmpTR, Line 5
String or binary data would be truncated.
The statement has been terminated.
SELECT * INTO dbo.DeletedEmp
FROM Original.Employees
WHERE 1=2;
CREATE TRIGGER dbo.DeleteEmpTR ON dbo.Employees
AFTER DELETE
AS
INSERT INTO dbo.DeletedEmp
SELECT d.col1, d.col2, ...
FROM deleted d
WHERE empid not in (SELECT empid FROM inserted);
/*
--tests
DELETE FROM dbo.Employees
WHERE empid = 9
SELECT * FROM dbo.DeletedEmp;
*/

You should check both of your tables Employees and DeletedEmp columns have same column data-type. (data-definition)
Check any columns with char and varchar have same length. Char(n) and VarChar(n), where n is the length of string.
Error String or binary data would be truncated is generally when length of the string to be inserted is more than the target's table column can accommodate.

Related

Updating first 7 characters of string with another 3 characters using SQL, throws "Error 19 - UNIQUE constraint failed: MGOFile.File."

I have a rather simple DB with a column called File, and I need to remove the first 7 characters of each row, and replace with a new string. I thought I had the code sorted, but I am getting error "SQLite3 Error 19 - UNIQUE constraint failed: MGOFile.File."
My table name is MGOFile, and the column is File. This is a simple select statement on the first few rows, the left column is the raw data, the right is what I need the resultant rows to look like...
I query my table using this:
'''sql
SELECT
File,
'T:\'|| substr(File, 8,2000) as File
FROM
MGOFile
WHERE
file like 'M:\_TV%';
'''
I then tried updating using this:
UPDATE MGOFile
SET File = 'T:\' || substr(File, 8, 2000)
WHERE File like 'M:\_TV%';
But here is where my error comes in, this fails with an error:
I am sure I am doing something simple wrong, but I have done plenty of Googling but all responses are over my head, this is the most advanced SQL I have tried to do!
Any ideas on how to can update these strings with some simple SQLite?
As checking for duplicates doesn't appear to detect the issues. Perhaps getting values at the time of the issue may assist. Do you have Triggers by any-chance? These will sometimes propagate an error which will be reported as being with the table that triggered the trigger.
As such perhaps consider adding a table to log such data along with a BEFORE UPDATE TRIGGER to actually log the information at run time. To stop the data being rolled back and thus undoing the logged information OR FAIL needs to be used.
Important as the updates will not be rolled back updates will have been applied. It is suggested that the above is used on a test database.
-- The code
DROP TABLE IF EXISTS lastupdated;
-- Create the logging table
CREATE TABLE IF NOT EXISTS lastupdated (counter, lastfile_before, lastfile_after, id_of_the_row);
-- Initialise it so it's plain to see if nothing has been done
INSERT INTO lastupdated VALUES(0,'nothing','nothing',0);
-- Add the Trigger to record the debugging information BEFORE the update
CREATE TRIGGER IF NOT EXISTS monitorupdateprogress
BEFORE UPDATE ON MGOFile
BEGIN
UPDATE lastupdated SET counter = counter +1, lastfile_before = old.File, lastfile_after = new.File, id_of_the_row = old.rowid;
END
;
UPDATE OR FAIL MGOFile -- OR FAIL will halt but NOT ROLLBACK
SET File = 'T:\' || substr(File, 8, 2000)
WHERE File like 'M:\_TV%';
SELECT * FROM lastupdated; -- will not run if there is a fail but should be run after the fail
This would, assuming the fail, record
the nth update in the counter column
the value in the File column before the change in the lastfile_before column.
the value that the File column would be updated to in the **lastfile_after* columns.
the last rowid (failing) of the row in the MGOFile table (this does assume that the MGOFile table is not a table defined using WITHOUT ROWID).
If the table was defined with the WITHOUT ROWID then you could change , id_of_the_row = 0;. The value will then be meaningless.
Testing/Results the version of the above that was used to test the above is :-
-- Solely for testing the code below
DROP TABLE IF EXISTS MGOFile;
CREATE TABLE IF NOT EXISTS MGOFile (File TEXT PRIMARY KEY);
-- Some testing data
INSERT INTO MGOFile VALUES
('M:\_TV/9-1-1.so2e09.web.x264-tbs[eztv].mkv'),
('M:\_TV/9-1-1.so2e09.web.x265-tbs[eztv].mkv'),
('M:\_TV/9-1-1.so2e09.web.x266-tbs[eztv].mkv'),
('M:\_TV/9-1-1.so2e09.web.x266-tbs[eztv].mkv.so2e09.web.x266-tbs[eztv].mkv.so2e09.web.x266-tbs[eztv].mkv.so2e09.web.x266-tbs[eztv].mkv.so2e09.web.x266-tbs[eztv].mkv.so2e09.web.x266-tbs[eztv].mkv'),
('M:\_TV/9-1-1.so2e09.web.x266-tbs[eztv].mkv.so2e09.web.x266-tbs[eztv].mkv.so2e09.web.x266-tbs[eztv].mkv.so2e09.web.x266-tbs[eztv].mkv.so2e09.web.x266-tbs[eztv].mkv.so2e09.web.x277-tbs[eztv].mkv'),
('M:\_TV/9-1-1.so2e09.web.x266-tbs[eztv].mkv.so2e09.web.x266-tbs[eztv].mkv.so2e09.web.x266-tbs[eztv].mkv.so2e09.web.x266-tbs[eztv].mkv.so2e09.web.x266-tbs[eztv].mkv.so2e09.web.x278-tbs[eztv].mkv'),
('M:\_TV/9-1-1.so2e09.web.x266-tbs[eztv].mkv.so2e09.web.x266-tbs[eztv].mkv.so2e09.web.x266-tbs[eztv].mkv.so2e09.web.x266-tbs[eztv].mkv.so2e09.web.x266-tbs[eztv].mkv.so2e09.web.x279-tbs[eztv].mkv'),
('M:\_TV/9-1-1.so2e09.web.x266-tbs[eztv].mkv.so2e09.web.x266-tbs[eztv].mkv.so2e09.web.x266-tbs[eztv].mkv.so2e09.web.x266-tbs[eztv].mkv.so2e09.web.x266-tbs[eztv].mkv.so2e09.web.x280-tbs[eztv].mkv')
;
SELECT substr(File,170,8) FROM MGOFile GROUP BY Substr(File,8,170) HAVING count() > 1;
-- The code
DROP TABLE IF EXISTS lastupdated;
-- Create the logging table
CREATE TABLE IF NOT EXISTS lastupdated (counter, lastfile_before, lastfile_after, id_of_the_row);
-- Initialise it so it's plain to see if nothing has been done
INSERT INTO lastupdated VALUES(0,'nothing','nothing',0);
-- Add the Trigger to record the debugging information BEFORE the update
CREATE TRIGGER IF NOT EXISTS monitorupdateprogress
BEFORE UPDATE ON MGOFile
BEGIN
UPDATE lastupdated SET counter = counter +1, lastfile_before = old.File, lastfile_after = new.File, id_of_the_row = old.rowid;
END
;
SELECT * FROM MGOFile;
UPDATE OR FAIL MGOFile -- OR FAIL will halt but NOT ROLLBACK
SET File = 'T:\' || substr(File, 8, 170) -- <<<<<<<<<<<<<<<<<<<< truncate reduced to force UNIQUE constraint
WHERE File like 'M:\_TV%';
SELECT * FROM lastupdated; -- will not run if there is a fail
When the above is run then the message is :-
UPDATE OR FAIL MGOFile -- OR FAIL will halt but NOT ROLLBACK
SET File = 'T:\' || substr(File, 8, 170) -- <<<<<<<<<<<<<<<<<<<< truncate reduced to force UNIQUE constraint
WHERE File like 'M:\_TV%'
> UNIQUE constraint failed: MGOFile.File
> Time: 0.094s
Running SELECT * FROM lastupdated; returns :-
counter
6
lastfile_before =
M:_TV/9-1-1.so2e09.web.x266-tbs[eztv].mkv.so2e09.web.x266-tbs[eztv].mkv.so2e09.web.x266-tbs[eztv].mkv.so2e09.web.x266-tbs[eztv].mkv.so2e09.web.x266-tbs[eztv].mkv.so2e09.web.x278-tbs[eztv].mkv
lastfile_after
T:\9-1-1.so2e09.web.x266-tbs[eztv].mkv.so2e09.web.x266-tbs[eztv].mkv.so2e09.web.x266-tbs[eztv].mkv.so2e09.web.x266-tbs[eztv].mkv.so2e09.web.x266-tbs[eztv].mkv.so2e09.web.x27
id_of_the_row
6
In the above contrived example the issue can easily be determined (albeit that the duplicate search also found the same issue) as the error is on the 6th row and at the row that contains mkv.so2e09.web.x278-tbs[eztv] but was truncated by the update to .mkv.so2e09.web.x27 hence it is a duplicate of the 5th row which has .mkv.so2e09.web.x277-tbs[eztv] but was also truncated to .mkv.so2e09.web.x27.
P.S. Have you tried using just
UPDATE MGOFile
SET File = 'T:\' || substr(File, 8)
WHERE File like 'M:\_TV%';
i.e. removing the truncation.
The error seems quite clear to me. You are changing the file name to a name that is already in the table.
You can identify the duplicates by running:
SELECT f.*
FROM MGOFile f
WHERE EXISTS (SELECT 1
FROM MGOFile f2
WHERE f2.File = 'T:\'|| substr(File, 8,2000)
) AND
f.file LIKE 'M:\_TV%';
I don't know what you want to do about the duplicate.

SQL Code Evaluation stopping a valid transaction

As part of the company I am working for at the moment I need to create some database upgrade scripts to replace some work of a previous contractor.
The code before the following block runs, creates the new ID column, and then this script looks to populate the values and then drop some columns.
IF EXISTS (
SELECT *
FROM sys.columns
WHERE object_id = OBJECT_ID(N'[Central].[Core.Report].[ReportLessonComp]')
AND name = 'Name')
and
EXISTS (
SELECT *
FROM sys.columns
WHERE object_id = OBJECT_ID(N'[Central].[Core.Report].[ReportLessonComp]')
AND name = 'Code')
BEGIN
UPDATE
[Central].[Core.Report].[ReportLessonComp]
SET
CompetencyId = rc.Id
FROM
[Central].[Core.Report].[ReportLessonComp] rlc
INNER JOIN
[Core.Lookup].ReportCompetency rc
ON
rc.Code = rlc.Code and rc.Name = rlc.Name
ALTER TABLE [Central].[Core.Report].[ReportLessonComp] DROP COLUMN CODE
ALTER TABLE [Central].[Core.Report].[ReportLessonComp] DROP COLUMN [Name]
ALTER TABLE [Central].[Core.Report].[ReportLessonComp] DROP COLUMN [Description]
END
GO
When running the if exists \ not exists checks and then select getdate() this works perfeclty fine and gives me the result I expect.
However, when I run the code block above I get error
Msg 207, Level 16, State 1, Line 23
Invalid column name 'Code'.
Msg 207, Level 16, State 1, Line 23
Invalid column name 'Name'.
This script it part of a larger upgrade script and is used in a system calle RoundHouse https://github.com/chucknorris/roundhouse which is the system chosen by the company.
Prior to the above if exists check,
IF (SELECT COUNT(1) FROM sys.columns
WHERE OBJECT_ID = OBJECT_ID('[Central].[Core.Report].[ReportLessonComp]')
AND Name in ('Name','Code')) = 2
which also gave the same issue. I have five tables that I need to update and this is going to stop the team from working if I cant resolve this at my next PR
What can I do in order to stop this from causing the upgrade scripts to fail?
EDIT -- The reason I am linking on varchar fields also is because the previous developer did not create relationships between tables, and was just inserting strings into tables rather than relating by ID causing the potential for unlinked \ inconsistent data.
The table edit prior to this creates the new id column, and this script is getting the value and dropping columns that are no longer needed
SQL Server will parse the whole of the statement prior to execution, so the exists check does not protect you from the update being parsed. If the column has already been dropped, that makes the statement invalid and you get a parse error. The update statement would have to be executed as dynamic SQL, sp_execute basically so that the varchar of the update is not directly parsed.
For SQL Server 2016 and above the drop column can be protected a bit more as well:
ALTER TABLE [Central].[Core.Report].[ReportLessonComp] DROP COLUMN IF EXISTS CODE

Msg 512, Level 16, State 1, Procedure trg_pricebase, Line 13 Subquery returned more than 1 value

how to solve this kind of probe help me please:
error:
Msg 512, Level 16, State 1, Procedure trg_pricebase, Line 13 Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression. The statement has been terminated.
and my trigger is :
ALTER TRIGGER [dbo].[trg_pricebase]
ON [dbo].[tbl_model2]
AFTER UPDATE
AS
BEGIN
DECLARE #price_base NVARCHAR(50) = (SELECT tbl_model2.price_base FROM tbl_model2)
DECLARE #tipid int = (SELECT tbl_model2.id FROM tbl_model2)
INSERT INTO tbl_price_history (tbl_price_history.price_base,tbl_price_history.tipid)
VALUES (#price_base, #tipid )
END
My psychic powers tell me that you want
alter trigger[dbo].[trg_pricebase] on [dbo].[tbl_model2]after update as
begin
insert into dbo.tbl_price_history (
price_base,
tipid
) select
price_base,
id
from
inserted
end
You have two basic problems.
Firstly, to read only the rows affected by the update statement, use the inserted and deleted pseudo tables.
Secondly, a trigger can fire off multiple rows, you can't assume there will just be one affected row.
Execute following [sub]queries to see what query return more than one row:
SELECT tbl_model2.price_base FROM tbl_model2
SELECT tbl_model2.id FROM tbl_model2
I assume you want to insert into *history table the old values (table deleted) or the new values (table inserted):
ALTER TRIGGER [dbo].[trg_pricebase]
ON [dbo].[tbl_model2]
AFTER UPDATE
AS
BEGIN
INSERT INTO tbl_price_history (price_base, tipid)
SELECT price_base, tipid
FROM deleted -- for old values
-- or FROM inserted -- for new values
END
References: inserted and deleted table
you are not supposed to use the method what you had followed in your query when you assign a value to a variable.
DECLARE #price_base NVARCHAR(50) = (SELECT tbl_model2.price_base FROM tbl_model2)
This select statement within bracket returns a resultset, not a single value, which cannot be stored into a variable (except table data typed variable). Don't practice this method, this will always end up with errors.
You should always use
select top 1 #price_base = price_base FROM tbl_model2
when you insert multiple rows, assigning values into a variable inside a trigger, will lead you to lose data.

INSERTED table really exists

Why I can use this select:
SELECT 1 FROM INSERTED
into a trigger, but not run like another select?
I got this error:
Msg 208, Level 16, State 1, Line 1
Invalid object name 'INSERTED'.
It works in this statement (that's inside a trigger):
IF EXISTS(SELECT 1 FROM INSERTED) AND
NOT EXISTS(SELECT 1 FROM DELETED)
BEGIN
-- AFTER INSERT
UPDATE VEIC
SET
VEIC.FLAG = 'I'
FROM
DBVEICULO VEIC
INNER JOIN INSERTED INS ON INS.ID_VEICULO = VEIC.ID_VEICULO
Trigger > http://pastebin.com/9Dh4TUPc
As per the documentation - Create Trigger (Transact-SQL):
DML triggers use the deleted and inserted logical (conceptual) tables. They are structurally similar to the table on which the trigger is defined, that is, the table on which the user action is tried. The deleted and inserted tables hold the old values or new values of the rows that may be changed by the user action. For example, to retrieve all values in the deleted table, use:
So the two tables, INSERTED and DELETED, only exists in the context of a trigger.

Stored Procedure consist Add column, Update data for that column, and Select all data from that table

I've written a stored procedure as following:
CREATE PROC spSoNguoiThan
#SNT int
AS
begin
IF not exists (select column_name from INFORMATION_SCHEMA.columns where
table_name = 'NhanVien' and column_name = 'SoNguoiThan')
ALTER TABLE NhanVien ADD SoNguoiThan int
else
begin
UPDATE NhanVien
SET NhanVien.SoNguoiThan = (SELECT Count(MaNguoiThan)FROM NguoiThan
WHERE MaNV=NhanVien.MaNV
GROUP BY NhanVien.MaNV)
end
SELECT *
FROM NhanVien
WHERE SoNguoiThan>#SNT
end
GO
Then I get the error :
Server: Msg 207, Level 16, State 1, Procedure spSoNguoiThan, Line 12
Invalid column name 'SoNguoiThan'.
Server: Msg 207, Level 16, State 1, Procedure spSoNguoiThan, Line 15
Invalid column name 'SoNguoiThan'.
Who can help me?
Thanks!
When the stored proc is parsed during CREATE the column does not exist so you get an error.
Running the internal code line by line works because they are separate. The 2nd batch (UPDATE) runs because the column exists.
The only way around this would be to use dynamic SQL for the update and select so it's not parsed until EXECUTE time (not CREATE time like now).
However, this is something I really would not do: DDL and DML in the same bit of code
I ran into this same issue and found that in addition to using dynamic sql I could solve it by cross joining to a temp table that had only one row. That caused the script compiler to not try to resolve the renamed column at compile time. Below is an example of what I did to solve the issue without using dynamic SQL
select '1' as SomeText into #dummytable
update q set q.ValueTXT = convert(varchar(255), q.ValueTXTTMP) from [dbo].[SomeImportantTable] q cross join #dummytable p