Error in SQL Server trigger - sql-server

I am writing a trigger for the first time. I want to check that if value =4 then Exon N, Date Fin,Date début must not be null
CREATE TRIGGER tgr_suspTVA
ON dbo.F_COMPTET
AFTER INSERT
AS
BEGIN
DECLARE #N_CatCompta int, #ExonN varchar(69),
#dateFin datetime, #dateDeb datetime
SELECT #N_CatCompta = N_CatCompta,
#ExonN = [Exon N°],
#dateFin = [Date Fin],
#dateDeb = [Date début]
IF (#N_CatCompta=4) AND (#ExonN IS NULL OR #dateFin IS NULL OR #dateDeb IS NULL)
BEGIN
RAISERROR('error',16,1);
END
END;
Here is the error that I get:
Msg 207, Niveau 16, État 1, Procédure tgr_suspTVA, Ligne 13
Nom de colonne non valide : 'N_CatCompta'.
Msg 207, Niveau 16, État 1, Procédure tgr_suspTVA, Ligne 13
Nom de colonne non valide : 'Exon N°'.
Msg 207, Niveau 16, État 1, Procédure tgr_suspTVA, Ligne 13
Nom de colonne non valide : 'Date Fin'.
Msg 207, Niveau 16, État 1, Procédure tgr_suspTVA, Ligne 14
Nom de colonne non valide : 'Date début'.

Triggers are not the best way to constrain your data and raise errors when an insert tries to break your rules.
The best way to do this is with constraints. Even a complex rule like this can be handled with a simple CHECK constraint:
ALTER TABLE dbo.F_COMPTET
ADD CONSTRAINT chkCompta4 CHECK (N_CatCompta<>4 OR
([Exon N°] IS NOT NULL
AND [Date Fin] IS NOT NULL
AND [Date début] IS NOT NULL
));
Add this constraint to your table, and any insert that tries to break this rule will raise a constraint violation error.
A trigger would be useful if, when a row breaks your rule, you wanted to do an insert into a different table. But as long as you are only dealing with the one table, there is no need for a trigger.

Although I think the check constraint that Tab Alleman posted is a better approach you might be stuck using a trigger.
If that is the case, you have two MAJOR problems here. The select statement for populating your variables doesn't have a FROM clause. The bigger issue is that you are using scalar variables in your trigger. Triggers fire once per operation in sql server not once per row. You need to reference the inserted virtual table and handle your code appropriately. Most likely an exists.

Related

prevent duplicate value to submit using stored procedure in sql server

I want to prevent the same #coupon_value in sp to submit and return
any message for validation using csharp but I am not able to how to
make changes in stored procedure.
CREATE PROCEDURE [dbo].[USP_REBATE_CAMPAIGN_RULE_DETAIL_VALIDATE]
#rebate_campaign_seq INT,
#coupon_value Varchar(50)='',
#Type varchar(50)='SERIES'
AS
SET NOCOUNT ON
BEGIN
SELECT rcrd.rebate_campaign_rule_detail_seq AS id,Type_value AS NAME,
'SERIES' AS type,
rcrd.amount_per_range AS Amount
FROM rebate_campaign_rule_detail rcrd (nolock)
INNER JOIN rebate_campaign_rule rcr
ON rcr.rebate_campaign_rule_seq = rcrd.rebate_campaign_rule_seq
INNER JOIN rebate_campaign rc
ON rc.rebate_campaign_seq = rcr.rebate_campaign_seq
WHERE rc.rebate_campaign_seq = #rebate_campaign_seq
AND rcrd.active_flag = 'Y' AND rcrd.type = #Type
AND rcrd.type_value=#coupon_value
End
The only way to prevent duplicate in database data is to add a UNIQUE CONSTRAINT. Everything else will fail, especially any solution coded with a procedural program, because of concurrency (imagine for a moment that two users launch the same procedure with the same values at the same time...).
To have a NULLbale UNIQUE constraint, you can add a UNIQUE filtered INDEX like this one :
CREATE INDEX X_UNIQUE_COUPON_RCRD
ON rebate_campaign_rule_detail (type_value)
WHERE type_value IS NOT NULL;

Control password attribute with CHECK in SQL Server

PD:Some names are in Spanish, sorry.
I have a table containing columns Usuario and Contraseña.
For each one it will be necessary to determine: login user (unique in the system and identification) and access password (which must contain 5 letters and 2 numbers - exact length 7). Consider that the login name must have exactly 10 characters
The table created is as follows:
CREATE TABLE Empleado
(
Usuario varchar(10) NOT NULL UNIQUE
CHECK(LEN([Usuario]) = 10),
Contraseña varchar(7) NOT NULL
CHECK (LEN([Contraseña]) = (7) AND [Contraseña] LIKE '%[0-9]%' AND [Contraseña] LIKE '%[A-Z]%')
)
And the data is inserted like this:
INSERT INTO Empleado (Usuario, Contraseña)
VALUES ('santiago21', 'qwerty1')
INSERT INTO Empleado (Usuario, Contraseña)
VALUES ('FaaacuuUwU', 'qwertY1')
The problem is that the second insert should take it, but the first one should not, which it DOES NOT DO.
Does anyone know how I should do it? Thank you.
We can take advantage of SQL Server's enhanced LIKE operator here:
CREATE TABLE Empleado (
Usuario varchar(10) NOT NULL UNIQUE CHECK(LEN([Usuario]) = 10),
Contraseña varchar(7) NOT NULL CHECK (
LEN([Contraseña]) = 7 AND
[Contraseña] LIKE '%[0-9]%[0-9]%' AND -- 2 numbers
[Contraseña] LIKE '%[A-Za-z]%[A-Za-z]%[A-Za-z]%[A-Za-z]%[A-Za-z]%') -- 5 letters
)
);
Edit:
In general you should not be storing clear text passwords in your database. The reason for this is that should anyone (internal or external) gain access to your Empleado table, they would get access to every credential in your entire system.
Instead, a much safer approach would be to first irreversibly hash every password and then store the hash. Then, in the unlikely event that someone undesirable might gain access to your table, they wouldn't get passwords, just usernames with some gibberish password hashes that they couldn't easily back out to the original passwords.

Alterar una columna, para cambiar "nulo" a "no nulo", cuando no se conoce el tipo de datos

I need to allow nulls in a column, but I don't know its data type. In oracle its possible to do this:
alter table MyTable modify MyField null;
the SQL server homologous statement requires the data type (in this example INT):
alter table MyTable alter column MyField int null;
Is there a way to do it like Oracle does?. The idea is not to have to go to the data dictionary to consult the types and build the sentence
Thanks,

SymmetricDS update error duplicate key

I use SymetricDS and sometimes symmetric raise an error :
INFO [slave] [DefaultDatabaseWriter] [slave-data-loader-1] Failed to process update event in batch 298578.
ERROR [slave] [DataLoaderService] [slave-data-loader-1] Failed to load batch 000-298578 StackTraceKey [UniqueKeyException:4114584735]
ERROR [master][AcknowledgeService] [master-push-default-5] The outgoing batch 001-298578 failed: ERREUR: la valeur d'une clé dupliquée rompt la contrainte unique « primary_key_constaint_name » Détail : La clé existe déjà.
I have tried to detect and correct conflicts by insert in sym_conflict this row :
insert into sym_conflict(conflict_id, source_node_group_id, target_node_group_id, detect_type resolve_type, ping_back)
values('master-win', 'master', 'slave', 'USE_CHANGED_DATA', 'INGORE', 'SINGLE_ROW');
but this not working. On documentation have found this :
USE_CHANGED_DATA : Indicates that the primary key plus any data that has changed on the source system will be used to detect a conflict. If a row exists with the same old values on the target system as they were on the source system for the columns that have changed on the source system, then no conflict is detected during an update or a delete. If a row already exists during an insert then a conflict has been detected.
How symmetric can detect PK conflicts on a update and ignore it ? Thanks.
No conflict detection, neither resolution is required in such cases. By default symmetricDs on insert if there's already a row with the same primary key on the target node will fall back to update and vice versa for an update falling back to an insert: http://www.symmetricds.org/doc/3.6/user-guide/html-single/user-guide.html#d4e104

How can I copy records between tables only if they are valid according to check constraints in Oracle?

I don't know if that is possible, but I want to copy a bunch of records from a temp table to a normal table. The problem is that some records may violate check constraints so I want to insert everything that is possible and generate error logs somewhere else for the invalid records.
If I execute:
INSERT INTO normal_table
SELECT ... FROM temp_table
nothing would be inserted if any record violates any constraint. I could make a loop and manually insert one by one, but I think the performance would be lower.
Ps: if possible, I'd like a solution that works with Oracle 9
From Oracle 10gR2, you can use the log errors clause:
EXECUTE DBMS_ERRLOG.CREATE_ERROR_LOG('NORMAL_TABLE');
INSERT INTO normal_table
SELECT ... FROM temp_table
LOG ERRORS REJECT LIMIT UNLIMITED;
In its simplest form. You can then see what errors you got:
SELECT ora_err_mesg$
FROM err$_normal_table;
More on the CREATE_ERROR_LOG step here.
I think this approach works from 9i, but don't have an instance available to test on, so this is actually run on 11gR2
Update: tested and tweaked (to avoid PLS-00436) in 9i:
declare
type t_temp_table is table of temp_table%rowtype;
l_temp_table t_temp_table;
l_err_code err_table.err_code%type;
l_err_msg err_table.err_msg%type;
l_id err_table.id%type;
cursor c is select * from temp_table;
error_array exception;
pragma exception_init(error_array, -24381);
begin
open c;
loop
fetch c bulk collect into l_temp_table limit 100;
exit when l_temp_table.count = 0;
begin
forall i in 1..l_temp_table.count save exceptions
insert into normal_table
values l_temp_table(i);
exception
when error_array then
for j in 1..sql%bulk_exceptions.count loop
l_id := l_temp_table(sql%bulk_exceptions(j).error_index).id;
l_err_code := sql%bulk_exceptions(j).error_code;
l_err_msg := sqlerrm(-1 * sql%bulk_exceptions(j).error_code);
insert into err_table(id, err_code, err_msg)
values (l_id, l_err_code, l_err_msg);
end loop;
end;
end loop;
end;
/
With all your real columns instead of just id, which I've done just for demo purposes:
create table normal_table(id number primary key);
create table temp_table(id number);
create table err_table(id number, err_code number, err_msg varchar2(2000));
insert into temp_table values(42);
insert into temp_table values(42);
Then run the anonymous block above...
select * from normal_table;
ID
----------
42
column err_msg format a50
select * from err_table;
ID ERR_CODE ERR_MSG
---------- ---------- --------------------------------------------------
42 1 ORA-00001: unique constraint (.) violated
This is less satisfactory on a few levels - more coding, slower if you have a lot of exceptions (because of the individual inserts for those), doesn't show which constraint was violated (or any other error details), and won't retain the errors if you rollback - though you could call an autonomous transaction to log it if that was an issue, which I doubt here.
If you have a small enough volume of data to not want to worry about the limit clause you can simplify it a bit:
declare
type t_temp_table is table of temp_table%rowtype;
l_temp_table t_temp_table;
l_err_code err_table.err_code%type;
l_err_msg err_table.err_msg%type;
l_id err_table.id%type;
error_array exception;
pragma exception_init(error_array, -24381);
begin
select * bulk collect into l_temp_table from temp_table;
forall i in 1..l_temp_table.count save exceptions
insert into normal_table
values l_temp_table(i);
exception
when error_array then
for j in 1..sql%bulk_exceptions.count loop
l_id := l_temp_table(sql%bulk_exceptions(j).error_index).id;
l_err_code := sql%bulk_exceptions(j).error_code;
l_err_msg := sqlerrm(-1 * sql%bulk_exceptions(j).error_code);
insert into err_table(id, err_code, err_msg)
values (l_id, l_err_code, l_err_msg);
end loop;
end;
/
The 9i documentation doesn't seem to be online any more, but this is in a new-features document, and lots of people have written about it - it's been asked about here before too.
If you're specifically interested only in check constraints then one method to think about is to read the definitions of the target check constraints from the data dictionary and apply them as predicates to the query that extracts data from the source table using dynamic sql.
Given:
create table t1 (
col1 number check (col1 between 3 and 10))
You can:
select constraint_name,
search_condition
from user_constraints
where constraint_type = 'C' and
table_name = 'T1'
The result being:
"SYS_C00226681", "col1 between 3 and 10"
From there it's "a simple matter of coding", as they say, and the method will work on just about any version of Oracle. The most efficient method would probably be to use a multitable insert to direct rows to either the intended target table or to an error logging table based on the result of a CASE statement that applies the check constraint predicates.

Resources