How to insert with coalesce multiple columns into my log table - database

I'm creating a log table from "users" and I don't want null values to be insert as empty. Btw this is de function i've try:
CREATE TABLE users_log(
codigo_id SERIAL PRIMARY KEY,
fecha_operacion TIMESTAMP,
llave TEXT,
operacion TEXT,
datos TEXT
)
CREATE OR REPLACE FUNCTION logger_users()
RETURNS TRIGGER AS
$$
BEGIN
CASE TG_OP
WHEN 'INSERT' THEN
INSERT INTO users_log VALUES(DEFAULT,current_timestamp,
NEW.id, 'Insert',CONCAT_WS(' ',COALESCE(NEW.*, 'NULL')));
WHEN 'DELETE' THEN
INSERT INTO users_log VALUES(DEFAULT,current_timestamp,
OLD.id, 'Delete', CONCAT_WS(' ',COALESCE(OLD.*, 'NULL')));
WHEN 'UPDATE' THEN
INSERT INTO users_log VALUES(DEFAULT,current_timestamp,
OLD.id, 'Update', (COALESCE(OLD.*,'NULL'))|| '-->' || (COALESCE(NEW.*, 'NULL')));
END CASE;
RETURN NEW;
END;
$$
LANGUAGE plpgsql;
So if i have null values it insert like this:
(3,"Angelo Cuellar",AngelCrox,angelo99#gmail.com,Estudiante,70034265,trabajando,,,,,t)
and i want something like this:
(3,"Angelo Cuellar",AngelCrox,angelo99#gmail.com,Estudiante,70034265,trabajando,'null','null','null','null',t)
thanks for ur help

Related

Postgres updatable View

in my application I created a view in order to let the users fill the data in relative tables. I've made view updatable by using a trigger function.
BEGIN
INSERT INTO evento_investimento (data, ora, rilevatore_id, strada_id, chilometro_strada,
latitudine, longitudine, barriera_sinistra, barriera_destra, vegetazione_primi_metri, sezione_trasversale,
curvilinearità, uso_suolo_prevalente_50m_destra, uso_suolo_prevalente_50m_sinistra, specie_id, stato_specie_id, sesso_id, classe_id)
VALUES (NEW.data,NEW.ora,
(SELECT rilevatore_id FROM rilevatore WHERE rilevatore.nome_cognome = NEW.nome_cognome),
(SELECT strada_id FROM strada WHERE strada.nome_strada = NEW.nome_strada),
NEW.chilometro_strada, NEW.latitudine, NEW.longitudine, NEW.barriera_sinistra, NEW.barriera_destra, NEW.vegetazione_primi_metri,
NEW.sezione_trasversale, NEW.curvilinearità, NEW.uso_suolo_prevalente_50m_destra, NEW.uso_suolo_prevalente_50m_sinistra,
(SELECT specie_id FROM specie WHERE specie.nome_comune = NEW.nome_comune),
(SELECT stato_specie_id FROM stato_specie WHERE stato_specie.stato = NEW.stato),
(SELECT sesso_id FROM sesso WHERE sesso.sesso_specie = NEW.sesso_specie),
(SELECT classe_id FROM classe_eta WHERE classe_eta.classe_eta = NEW.classe_eta)
);
if not exists(select * from rilevatore where rilevatore.nome_cognome=new.nome_cognome) then
INSERT INTO rilevatore (nome_cognome, telefono, email, ente_appartenenza)
VALUES (NEW.nome_cognome, NEW.telefono, NEW.email, NEW.ente_appartenenza);
end if;
if not exists(select * from specie where specie.nome_comune=new.nome_comune) then
INSERT INTO specie (nome_comune, nome_scientifico)
VALUES (NEW.nome_comune, NEW.nome_scientifico);
end if;
if not exists(select * from strada where strada.nome_strada=new.nome_strada) then
INSERT INTO strada (nome_strada)
VALUES (NEW.nome_strada);
end if;
if not exists(select * from sesso where sesso.sesso_specie=new.sesso_specie) then
INSERT INTO sesso (sesso_specie)
VALUES (NEW.sesso_specie);
end if;
if not exists(select * from stato_specie where stato_specie.stato=new.stato) then
INSERT INTO stato_specie (stato)
VALUES (NEW.stato);
end if;
if not exists(select * from classe_eta where classe_eta.classe_eta=classe_eta) then
INSERT INTO classe_eta (classe_eta)
VALUES (NEW.classe_eta);
end if;
RETURN NEW;
END;
While the function works fine in filling the data, the view doesn't update automatically, that is new data doesn't appear in the view. How can i solve this issue?
Thanks in advance.
[EDIT]
Actually, the view works fine. The issue concerns the evento_investimento table. If a new value is added to the view, i.e. a new specie.nome_comune that doesn't exist in the specie table, the function correctly update the specie table but the specie_id in the evento_investimento table doesn't appear. As a consequence the view won't update.
Here a simplified example.
create table specie
CREATE TABLE specie
(
specie_id INT PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
nome_comune TEXT UNIQUE,
nome_scientifico TEXT UNIQUE
);
fill the table with some values:
insert into specie
(nome_comune, nome_scientifico)
values
('lupo', 'Canis lupus'),
('lontra', 'Lutra lutra');
create table rilevatore
CREATE TABLE rilevatore
(
rilevatore_id INT PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
nome_cognome TEXT
);
fill the table with some values:
insert into rilevatore
(nome_cognome)
values
('mario'),
('luca');
create table evento_investimento
CREATE TABLE evento_investimento
(
evento_id INT PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
data DATE,
ora TIME WITHOUT TIME ZONE,
rilevatore_id INT REFERENCES rilevatore (rilevatore_id),
specie_id INT REFERENCES specie(specie_id)
);
Let fill some data in evento_investimento
insert into evento_investimento
(data, ora, rilevatore_id, specie_id)
values
('2019-12-31', '16:54:00',1,1)
Create a view to fill the data
CREATE VIEW inserimento_dati_vista AS
SELECT row_number() OVER ()::integer AS gid,
evento_investimento.ora,
evento_investimento.data,
rilevatore.nome_cognome,
specie.nome_comune,
specie.nome_scientifico
FROM evento_investimento
JOIN specie ON evento_investimento.specie_id = specie.specie_id
JOIN rilevatore ON evento_investimento.rilevatore_id = rilevatore.rilevatore_id;
Now create the trigger function
CREATE OR REPLACE FUNCTION inserimento_dati_fun_2() RETURNS trigger AS $$
BEGIN
INSERT INTO evento_investimento (data, ora, rilevatore_id, specie_id)
VALUES (NEW.data,NEW.ora,
(SELECT rilevatore_id FROM rilevatore WHERE rilevatore.nome_cognome = NEW.nome_cognome),
(SELECT specie_id FROM specie WHERE specie.nome_comune = NEW.nome_comune)
);
if not exists(select * from rilevatore where rilevatore.nome_cognome=new.nome_cognome) then
INSERT INTO rilevatore (nome_cognome)
VALUES (NEW.nome_cognome);
end if;
if not exists(select * from specie where specie.nome_comune=new.nome_comune) then
INSERT INTO specie (nome_comune, nome_scientifico)
VALUES (NEW.nome_comune, NEW.nome_scientifico);
end if;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
create trigger inserimento_dati_fun_trg
instead of insert on inserimento_dati_vista for each row EXECUTE procedure inserimento_dati_fun_2();
Running this code give me correct results:
INSERT INTO inserimento_dati_vista
(data, ora, nome_cognome, nome_comune, nome_scientifico)
VALUES
('2020-01-01', '16:54:00','mario', 'lupo', 'Canis lupus'),
('2020-01-02', '13:54:00','luca', 'lontra', 'Lutra lutra')
When I insert a new nome_cognome or nome_comune
INSERT INTO inserimento _dati_vista
(data, ora, nome_cognome, nome_comune, nome_scientifico)
VALUES
('2020-01-01', '16:54:00','piero', 'orso', 'Ursus arctos')
the tables rilevatore and specie correctly update but evento_investimento doesn't.
When function works with filling data in tables and only views are problem, you have to recreate views after insert, they dont update automatically.

TSQL trigger instead of insert

I need to replace, after an insert in a certain table, one of the column value if it match with a banned value.
example:
INSERT INTO [dbo].[TEST_A]
([COL_A]
,[COL_B])
VALUES
('TEST'
,'TEST1')
TEST1 is a banned value and I want to replace it in 'BANNED'.
This is the trigger I wrote but it seems to working properly well:
CREATE TRIGGER [dbo].[TEST_NAME_INS] ON [dbo].[TEST_A]
INSTEAD OF INSERT
AS
BEGIN
INSERT INTO TEST_A
(COL_A
,COL_B)
SELECT
COL_A
,REPLACE(COL_B, 'TEST1', 'BANNED')
FROM inserted
WHERE INSERTED.COL_B IN ('TEST1')
The error is that if I insert a different value in COL_B no rows are inserted.
Can you give me a solution?
thanks in advance
If you have more values than you want to put in a case statement, you can try using a table to store the banned words and the replacement for them. Then join to that in the insert, something like:
CREATE TRIGGER [dbo].[TEST_NAME_INS] ON [dbo].[TEST_A]
INSTEAD OF INSERT
AS
BEGIN
INSERT INTO TEST_A
(COL_A
,COL_B)
SELECT
COL_A
,CASE WHEN b.banned is null then i.col_b
ELSE b.replacement
END
FROM inserted i
LEFT JOIN Banned b on i.col_B = b.banned
You need to modify your trigger.
CREATE TRIGGER [dbo].[TEST_NAME_INS] ON [dbo].[TEST_A]
INSTEAD OF INSERT
AS
BEGIN
INSERT INTO TEST_A
(COL_A
,COL_B)
SELECT
COL_A
,CASE WHEN COL_B ='TEST1' THEN 'BANNED'
WHEN COL_B ='TEST12' THEN 'BANNED34'
ELSE COL_B END
FROM inserted

sql server how to default nvarhcar column to GUID if it's null or empty

If I have a nvarchar column in sql server 2012, how do I default the value to a GUID when the value is null or empty space?
To be more clear:
Not only it should work when people do:
INSERT INTO table(second_column) VALUES('test')
It should also work if people do:
INSERT INTO table(column_with_default, second_column) VALUES('', 'test')
I tried to set the default to
LEFT(NEWID(), 36)
but it doesn't work...
When I insert to the table with 'NULL' for that column, it's just NULL (same with '').
EDIT 2: According to your comments I change my answer
CREATE TABLE dbo.Test(ID NVARCHAR(MAX) NOT NULL DEFAULT NEWID()
,TestData NVARCHAR(MAX));
GO
CREATE TRIGGER dbo.TestTrigger ON dbo.Test
INSTEAD OF INSERT
AS
BEGIN
INSERT INTO dbo.Test(ID,TestData)
SELECT CASE WHEN i.ID IS NULL OR i.ID='' THEN CAST(NEWID() AS NVARCHAR(MAX)) ELSE i.ID END,i.TestData
FROM inserted AS i;
END
GO
INSERT INTO dbo.Test(ID,TestData) VALUES(NULL,'test with NULL');
INSERT INTO dbo.Test(ID,TestData) VALUES('','test with EMPTY');
INSERT INTO dbo.Test(ID,TestData) VALUES('abc','test with real data');
SELECT * FROM Test;
GO
DROP TABLE dbo.Test;
The result
69604546-47BD-4E0D-9924-FAD39054BFFD test with NULL
D9F38DB0-1155-464B-89C7-43C2CE8381BF test with EMPTY
abc test with real data
You can use NEWID() in a Default Value constraint. If the field is not large enough then use LEFT(NEWID(),<field-length>) as the constraint expression.

PostgreSQL trigger won't inset audit value

I'm working with PostgreSQL 8.1 and I created a trigger for a table, so if something gets updated or inserted it will be also registered in a log table, but I'm having issues with NULL values:
If I update a column with a NULL value, then it won't insert any value to the log, but with char to char it will be fine, so maybe I'm missing something.
The table was created like this:
CREATE TABLE log_test(
id_test integer NOT NULL DEFAULT nextval('id_test_seq'),
type char(3),
product char(10),
table char(15),
field char(10),
old_val char(10),
new_val char(10),
user char(10),
event char(10),
date timestamp with time zone NOT NULL DEFAULT now(),
CONSTRAINT "log_test_prim" PRIMARY KEY ("id_test")
);
The trigger was created like:
CREATE FUNCTION "public"."log_test_trigger" () RETURNS "trigger" AS 'BEGIN
...
IF (TG_OP = ''UPDATE'') THEN
IF (NEW.name <> OLD.name) THEN
INSERT INTO log_test (type, table, field, old_val, new_val, user, event) VALUES (NEW.type, TG_RELNAME, ''name'', OLD.name, NEW.name, NEW.user, ''UPDATE'');
END IF;
...
END;' LANGUAGE "plpgsql"
VOLATILE
CALLED ON NULL INPUT
SECURITY INVOKER
Any help?
You cannot compare NULL to a value, i.e. both NULL = 2 and NULL <> 2 are NULL. Add ISNULL:
...
IF (TG_OP = ''UPDATE'') THEN
IF (NEW.name <> OLD.name OR NEW.name ISNULL) THEN
INSERT INTO log_test (type, table, field, old_val, new_val, user, event) VALUES (NEW.type, TG_RELNAME, ''name'', OLD.name, NEW.name, NEW.user, ''UPDATE'');
END IF;
...
or (probably better) coalesce() for both NEW and OLD records:
...
IF (TG_OP = ''UPDATE'') THEN
IF (coalesce(NEW.name, '') <> coalesce(OLD.name, '')) THEN
INSERT INTO log_test (type, table, field, old_val, new_val, user, event) VALUES (NEW.type, TG_RELNAME, ''name'', OLD.name, NEW.name, NEW.user, ''UPDATE'');
END IF;
...

Create #TableVariable based on an existing database table?

I want to use table variables in stored procedures but here is an issue. My tables are very large and declaring a table variable need a long code to write and debug as well.
Kindly advice me some way to declare table variables quickly, is it possible to create table variable based on an existing table ?
Or please share any tip to create code for creating table variable.
Thanks
Right click the table, choose Script As Create.
Replace create table xxx with declare #xxx table.
As discussed in this SO Question you can't select into a table variable.
When you say "large", if you mean a lot of columns, the best approach for you would probably be to script that table as create and save the definition and use that in your Declare statement.
If you mean large as far as the number of rows you'll have in the table variable, you may want to consider using a temporary table which you could then do a SELECT INTO statement to create it based off of the original.
SELECT * INTO #tmpTable FROM srcTable
The simple answer is "No you cannot create a variable table based on other table"
But, you can generalise a bit by using a type table.
For example (note: you can add documentation to the type table and columns, which can be useful for future reference):
PRINT 'type: [dbo].[foo_type]'
PRINT ' - Check if [dbo].[foo_type] TYPE exists (and drop it if it does).'
GO
IF EXISTS (SELECT 1 FROM sys.types WHERE name = 'foo_type' AND is_table_type = 1 AND SCHEMA_ID('dbo') = schema_id)
BEGIN
-- Create the proc
PRINT ' - Drop TYPE [dbo].[foo_type]';
DROP TYPE [dbo].[foo_type];
END;
GO
PRINT ' - create [dbo].[foo_type] TYPE.'
GO
CREATE type [dbo].[foo_type] as Table
(
[id] int identity(1,1) PRIMARY KEY
, [name] varchar(255) NOT NULL
, [description] varchar(255)
, numeric_data numeric(26, 6)
, datetimestamp datetime default getdate()
, Unique_Indicator float unique not null default cast(getdate() as float)
, CHECK (Unique_Indicator > 0)
);
GO
PRINT ' - done.'
GO
-- Adding the descriptions
PRINT ' - Adding Type level Description'
EXEC sys.sp_addextendedproperty #name=N'MS_Description', #value=N'describe the usage of this type.' , #level0type=N'SCHEMA',#level0name=N'dbo', #level1type=N'TYPE',#level1name=N'foo_type'
GO
PRINT ' - Adding Column level Descriptions'
PRINT ' - column: id'
EXEC sys.sp_addextendedproperty #name=N'MS_Description', #value=N'ID of the record...' , #level0type=N'SCHEMA',#level0name=N'dbo', #level1type=N'TYPE',#level1name=N'foo_type', #level2type=N'COLUMN',#level2name=N'ID';
GO
------------------------------------------------------------------------------------------------
-- use the type defined above to manipulate the variable table:
declare #foo_table foo_type;
--insert using the default value for the for the unique indicator.
insert into #foo_table (name, [description], numeric_data, datetimestamp)
values('babar', 'this is the king of the elephants', 12.5, '1931-01-01')
;
-- insert the records one by one to use the scope_identity() for the unique indicator.
insert into #foo_table (name, [description], numeric_data, datetimestamp, Unique_Indicator )
values('zephyr', 'Babar''s monkey friend', 5.5, '1932-01-01', scope_identity())
;
insert into #foo_table (name, [description], numeric_data, datetimestamp, Unique_Indicator )
values ('Celeste', 'Babar''s sister', 19.5, '1932-01-01', scope_identity())
;
-- insert using a list of values
insert into #foo_table (name, [description], numeric_data, datetimestamp, Unique_Indicator )
values('Babur', 'Not Babar!!!', 1483, '1983-02-14', 10)
, ('Mephistopheles', 'Not Babar either...', 666, '1866-01-01',11)
;
-- insert using a select
insert into #foo_table (name, [description], numeric_data, datetimestamp, Unique_Indicator)
(select 'Conan', 'The Cimmerian barbarian', 850, '1932-12-01',99 union
select 'Robert E. Howard', 'Conan''s creator', 30, '1906-01-22', 100
);
-- check the data we inserted in the variable table.
select * from #foo_table;
-- Clean up the example type
DROP TYPE [dbo].[foo_type];

Resources