Postgres updatable View - database

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.

Related

how to insert a value to both tables using join codeigniter 4 sql server

I'm trying to insert values to both tables at the same time. I'm using a form in my application where I use the inserted values from the form to inert them into the db. But now I'm inserting values to one table (Users).
public function registerUser($formdata){
helper('global');// a heper for randomString().
//Asign value to columns
$db_data['Emailaddress'] = $formdata['emailaddress'];
$db_data['Password'] = password_hash($formdata['password'], PASSWORD_DEFAULT);
$db_data['Status'] = 'Free';
$db_data['Token'] = randomString(32);
$db_data['FirstLogin'] = 0;
$db_data['Users.UsersKey'] = $db_data['UsersSettings.UsersKey'];
//insert to db
$this->db->table('Users', 'UsersSettings')->join('UsersSettings','Users.UsersKey = UsersSettings.UsersKey', 'inner')->insert($db_data);
}
public function updateUserSetting_proccess(){
$formdata = $this->request->getPostGet();
return $this->SettingsModel->update_user_settings($formdata);
}
The content of the Users table is:
SELECT TOP (1000) [UsersKey]
,[UniqueID]
,[Token]
,[ResetToken]
,[Emailaddress]
,[Password]
,[Status]
,[DateTimeAdded]
,[DateTimeLastUpdated]
,[FirstLogin]
FROM [dbo].[Users]
The UsersKey is inserted automaticly because of the auto increment.
The second table I want to use is UsersSettings with content:
SELECT TOP (1000) [UsersSettingsKey]
,[UsersKey]
,[FirstName]
,[LastName]
,[Logo]
,[Organization]
,[Address]
,[Number]
,[Addition]
,[Postcode]
,[City]
,[Country]
,[Language]
,[Theme]
,[CalcPercentage]
,[CalcAdminFee]
,[ColorPrimary]
,[ColorSecondary]
,[DateTimeLastUpdated]
FROM [dbo].[UsersSettings]
I want the UsersKey from UsersSettings have the same value in Users UsersKey.
I tried this:
join('UsersSettings','Users.UsersKey = UsersSettings.UsersKey', 'inner')
but it didn't help. Can someong give me some suggestions?
You'll need to perform the insert into table Users first in order to get the generated UsersKey.
Explanation of why inserting into table Users first is required, may be shown with the SQL equivalent:
declare #lv_UsersKey int
-- insert into table Users (only essential parts shown)
insert into Users(....) values (...)
-- capture UsersKey for inserted record
select #lv_UsersKey = cast(SCOPE_IDENTITY() as int)
-- then insert into UsersSettings (only essential parts shown)
insert into UsersSettings (UsersKey, ....) values (#lv_UsersKey, ...)
Transferring the above SQL to codeigniter will look like this:
$this->db->table('Users')->insert($db_data);
$inserted_users_key = $this->db->insert_id();
$db_data2['UsersKey'] = $inserted_users_key;
// some more init of $db_data2 here
$this->db->table('UsersSettings')->insert($db_data2);

SQL Trigger - DML statement Output Insert Error [duplicate]

I have a table that I want to add / update when a new row is added to a view but I am struggling to make this work.
My target table is Course Learner Progress and my view is Quiz_Results_For_Course_Learner_Progress.
I have created the view from a table that records quiz scores and is populated by a Zapier zap so I can't add a trigger to that table - which is why I have created the view of that table.
My trigger is as follows:
create trigger Update_Course_Progress_Quiz_Scores
on Quiz_Results_For_Course_Learner_Progress
instead of insert
as
declare #CompanyID int = (select CompanyID
from LEARNERS.dbo.ILR
where LEARNERID = LearnerID)
Merge Course_Learner_Progress as t
using inserted as s on t.CourseID = s.CourseID
and t.ModuleID = s.ModuleID
and t.LearnerID = s.LearnerID
and t.ContentID = s.ContentID
when not matched by Target then
insert (CompanyID, LearnerID, CourseID, ModuleID, ContentID, ContentType, Passmark, Score, [Status])
values (#CompanyID, s.LearnerID, s.CourseID, s.ModuleID,
s.ContentID, 4, s.Passmark, s.Quiz_Score, s.Status)
when matched then
update
set t.Score = s.Quiz_Score,
t.Status = s.[Status]
;
I can create the trigger successfully, but it doesn't insert / update a row in the Course_Learner_Progress table.
I would really welcome some help on this

How can I use a trigger to allow an incremented, user-assigned ID?

I am moving a small database from MS Access into SQL Server. Each year, the users would create a new Access database and have clean data, but this change will put data across the years into one pot. The users have relied on the autonumber value in Access as a reference for records. That is very inaccurate if, say, 238 records are removed.
So I am trying to accommodate them with an id column they can control (somewhat). They will not see the real primary key in the SQL table, but I want to give them an ID they can edit, but still be unique.
I've been working with this trigger, but it has taken much longer than I expected.
Everything SEEMS TO work fine, except I don't understand why I have the same data in my INSERTED table as the table the trigger is on. (See note in code.)
ALTER TRIGGER [dbo].[trg_tblAppData]
ON [dbo].[tblAppData]
AFTER INSERT,UPDATE
AS
BEGIN
SET NOCOUNT ON;
DECLARE #NewUserEnteredId int = 0;
DECLARE #RowIdForUpdate int = 0;
DECLARE #CurrentUserEnteredId int = 0;
DECLARE #LoopCount int = 0;
--*** Loop through all records to be updated because the values will be incremented.
WHILE (1 = 1)
BEGIN
SET #LoopCount = #LoopCount + 1;
IF (#LoopCount > (SELECT Count(*) FROM INSERTED))
BREAK;
SELECT TOP 1 #RowIdForUpdate = ID, #CurrentUserEnteredId = UserEnteredId FROM INSERTED WHERE ID > #RowIdForUpdate ORDER BY ID DESC;
IF (#RowIdForUpdate IS NULL)
BREAK;
-- WHY IS THERE A MATCH HERE? HAS THE RECORD ALREADY BEEN INSERTED?
IF EXISTS (SELECT UserEnteredId FROM tblAppData WHERE UserEnteredId = #CurrentUserEnteredId)
BEGIN
SET #NewUserEnteredId = (SELECT Max(t1.UserEnteredId) + 1 FROM tblAppData t1);
END
ELSE
SET #NewUserEnteredId = #CurrentUserEnteredId;
UPDATE tblAppData
SET UserEnteredId = #NewUserEnteredId
FROM tblAppData a
WHERE a.ID = #RowIdForUpdate
END
END
Here is what I want to accomplish:
When new record(s) are added, it should increment values from the Max existing
When a user overrides a value, it should check to see the existence of that value. If found restore the existing value, otherwise allow the change.
This trigger allows for multiple rows being added at a time.
It is great for this to be efficient for future use, but in reality, they will only add 1,000 records a year.
I wouldn't use a trigger to accomplish this.
Here is a script you can use to create a sequence (op didn't tag version), create the primary key, use the sequence as your special id, and put a constraint on the column.
create table dbo.test (
testid int identity(1,1) not null primary key clustered
, myid int null constraint UQ_ unique
, somevalue nvarchar(255) null
);
create sequence dbo.myid
as int
start with 1
increment by 1;
alter table dbo.test
add default next value for dbo.myid for myid;
insert into dbo.test (somevalue)
select 'this' union all
select 'that' union all
select 'and' union all
select 'this';
insert into dbo.test (myid, somevalue)
select 33, 'oops';
select *
from dbo.test
insert into dbo.test (somevalue)
select 'oh the fun';
select *
from dbo.test
--| This should error
insert into dbo.test (myid, somevalue)
select 3, 'This is NO fun';
Here is the result set:
testid myid somevalue
1 1 this
2 2 that
3 3 and
4 4 this
5 33 oops
6 5 oh the fun
And at the very end a test, which will error.

SQL SERVER Select multiple fields from If Exists

I have to do an SQL Server Statement that have to return an empty row when is null, and data otherwhise.
I am trying to do a Select from (if exisits) but have an error on parent table.
I Simplify it. But the meaning, is to retrieve a couple of fields when condition is null and other fields when it is not null.
It Works fine when I do not clouse it in another select.... I need to retrieve it as a table to do an inner Join with other clouse.
How can i resolved it?
Here is my code..
select * from
(
if exists(select isnull(SECTOR_ID_DESTINO_BAD,-1)
from workflow_Compras_detalle w
where w.id=2)
begin
select null as Sector,null as sector_id_origen
end
else
begin
select top 1 isnull(ws.sector,'') sector, wd.sector_id_origen
from workflow_Compras_detalle wd
where orden < 10
end
)Table
you should try to insert the data into a temporary table or Table Variable, then get the data from that table, here is an example with a Table Variable, if you need something more persistent you may use a #Temp Table, i recommend you take a look to this: difference between var table and #Temp Table
DECLARE #VAR_TABLE AS TABLE(
Sector varchar(25),
sector_id_origen int
)
if exists(select isnull(SECTOR_ID_DESTINO_BAD,-1)
from workflow_Compras_detalle w
where w.id=2)
begin
INSERT INTO #VAR_TABLE
Select null as Sector,null as sector_id_origen
End
Else
begin
INSERT INTO #VAR_TABLE
select top 1 isnull(ws.sector,'') sector, wd.sector_id_origen
from workflow_Compras_detalle wd
where orden < 10
End
SELECT * FROM #VAR_TABLE

Using Instead-Of SQL triggers to insert or update a view

I want to write a trigger to the view, VW_BANKBRANCH:
If the inserted row contains a bankcode that exists in the table, then update the
bName column of bank table with the inserted data
If not, insert rows to bank table to reflect the new information.
But my trigger is not working..
My tables
CREATE TABLE bank(
code VARCHAR(30) PRIMARY KEY,
bName VARCHAR(50)
);
CREATE TABLE branch(
brNum INT PRIMARY KEY,
brName VARCHAR(50),
braddress VARCHAR(50),
bcode VARCHAR(30) REFERENCES bank(code)
);
CREATE VIEW VW_BANKBRANCH
AS
SELECT code,bname,brnum,brName
FROM bank ,branch
WHERE code=bcode
My trigger
CREATE TRIGGER tr_VW_BANKBRANCH_INSERT ON VW_BANKBRANCH
INSTEAD OF INSERT
AS
BEGIN
DECLARE #insertedBankCode INT
#insertedbname varchar
#insertedbrnum int
#insertedbrName varchar
SELECT #insertedBankCode = code
FROM INSERTED
IF(#insertedBankCode=code)
SET code=#insertedBankCode
bname=#insertedbname
brnum=#insertedbrnum
brName=#insertedbrName
ELSE
insert(code,bname,brnum,brName)
END
I've adapted the instead of trigger on the view below - I'm assuming you want to upsert both bank and branch accordingly (although note that the branch address is not currently in the view).
That said, I would be careful of (ab)using an instead of trigger on an INSERT to do upserts - this might not be entirely intuitive to the reader.
Also, remember that the INSERTED pseudo table could contain a SET of rows, so needs to be adjusted to set based approach accordingly.
CREATE TRIGGER tr_VW_BANKBRANCH_INSERT ON VW_BANKBRANCH
INSTEAD OF INSERT
AS
BEGIN
SET NOCOUNT ON;
UPDATE b
SET bname = i.bname
FROM bank b
INNER JOIN inserted i
ON i.code = b.code;
UPDATE br
SET
br.brName = i.brName,
br.braddress = NULL -- TODO add this to the view
FROM branch br
INNER JOIN inserted i
ON br.bcode = i.code
AND br.brNum = i.brNum;
INSERT INTO bank(code, bname)
SELECT code, bname
FROM inserted i
WHERE NOT EXISTS
(SELECT 1 FROM bank b WHERE b.code = i.Code);
INSERT INTO Branch(brNum, brName, braddress, bcode)
SELECT brNum, brName, NULL, code
FROM inserted i
WHERE NOT EXISTS
(SELECT 1
FROM branch br
WHERE br.bcode = i.Code AND br.brNum = i.brNum);
END;
GO
SqlFiddle here - I've also adjusted the view to use a JOIN, rather than the old style of WHERE joins.
If you have SqlServer 2008 or later, you could also use MERGE instead of separate inserts and updates.

Resources