Postgres trigger that affects rows with certain values - sql

I'm postgres newbie and i have a question regarding triggers. Lets say we have 2 tables. One with cities and one with people.
CITIES:
PEOPLE:
I need a trigger that updates 'processed' value in table cities from 0 to 1 when 'processed' value in table people updates from 0 to 1 for all people from that city.
For example: John, Ian and Claire are from Berlin and when 'processed' value is updated to 1 for each of them, then the trigger updates processed value for Berlin in table cities to 1.
What is the best way to do this?

This will work:
CREATE OR REPLACE FUNCTION mytrigger() RETURNS TRIGGER AS $mytrigger$
BEGIN
IF (SELECT EXISTS (SELECT 1 FROM PEOPLE WHERE city_id = NEW.city_id AND processed = '0')) THEN
UPDATE CITIES SET processed = '0' WHERE id = NEW.city_id;
RETURN NEW;
ELSE
UPDATE CITIES SET processed = '1' WHERE id = NEW.city_id;
RETURN NEW;
END IF;
END;
$mytrigger$ LANGUAGE plpgsql;
CREATE TRIGGER mytrigger
AFTER UPDATE ON PEOPLE
FOR EACH ROW EXECUTE PROCEDURE mytrigger();

try this query:
with c as (
select city_id
from people
where name = NEW.name
)
, a as (
select avg(processed)
from people
join c on c.city_id = people.city_id
)
update cities u
set processed = 1
from a
where u.id = a.city_id
and avg = 1
alias c gets city id, then a aggregates processed for that sict id in people and lastly update updates only when all names are processed.

Related

How to write a sql update query for below condition?

I have 2 tables. First is employee table and has (id,emp_num,name,address_id,text) columns.
Second is address table with (adress_id,emp_num,adress,state) columns.
Now when I am inserting/updating a new record into employee table, I also want to update TEXT column for all other entries for that emp_num who are from the same state.
EMPLOYEE table:
enter image description here
Address table:
enter image description here
I am trying to do a update like below:
update employee set id_text='ABCABC' where id = 'ID1';
some other update query
after that employee table should look like:
enter image description here
(note: text field is updated for ID1 and ID4)
Thanks
To me, it looks as if you need a row-level trigger which fires whenever you insert or update a row in employee table; using :new.address_id, fetch state from address table and put it into employee's text column:
create or replace trigger trg_biu_emp
before insert or update on employee
for each row
begin
select a.state
into :new.text
from address a
where a.address_id = :new.address_id;
end;
/
UPDATE employee e
SET e.text = (SELECT a.state FROM address a WHERE a.address_id = e.address_id)
WHERE e.emp_num IN (SELECT a.emp_num FROM address a WHERE a.state = (SELECT a.state FROM address a WHERE a.address_id = e.address_id));

Can someone help me implement the following trigger in PL/SQL Oracle?

So,
I have 4 tables:
Suppliers( id_sup, name, city)
Products (id_prod, name, city)
Companies (id_co, name, city)
Deliveries (id_sup, id_prod, id_co)
I need a trigger so that if I want to update the city of a Supplier, I am not allowed if that supplier has a delivery where the product it delivers and the company it delivers to have the same city as it.
This is what I've tried so far, but it's not working:
CREATE OR REPLACE TRIGGER secure_suppliers
BEFORE UPDATE ON Suppliers
BEGIN
IF UPDATING ('city') THEN
IF (suppliers.id_sup IN (SELECT id_sup FROM Deliveries) AND suppliers.city = (Select p.city From Products p INNER JOIN Deliveries d ON (p.id_prod = d.id_prod)) AND suppliers.city = (Select c.city From Companies c INNER JOIN Deliveries d ON (c.id_co = d.id_co))) THEN
RAISE_APPLICATION_ERROR(-20500, 'Can't update city!');
End if;
End;
You can use one query to find if product, supplier and company are in one city with new value of the supplier then proceed with error handling in the trigger as following:
CREATE OR REPLACE TRIGGER secure_suppliers
For each row -- row level trigger
BEFORE UPDATE OF city ON Suppliers -- trigger will execute on update of column city only
DECLARE
cnt NUMBER := 0;
BEGIN
select count(1)
Into cnt
From deliveries d
Join companies c on d.id_co = c.id_co
Join products p on d.id_prod = p.id_prod
Where p.city = c.city
And p.city = :new.city;
If cnt > 0 then
RAISE_APPLICATION_ERROR(-20500, 'Can''t update city!');
End if;
End;
/
Cheers!!

Update all column rows with random values from another table in Snowflake

I need to update all rows of a column with random values selected from another table. I am trying following query -
UPDATE TEST_CITY
SET "CITY" = (SELECT NAME FROM CITY SAMPLE (1 rows))
The subquery gives me a random city when executed separately, but in above case all rows are updated with same value.
I have also tried to select random records by id like following but this also updates all rows with same value -
UPDATE TEST_CITY
SET "CITY" = (select c.name
from city c
where c.id = (SELECT uniform(1, 50, random()))
)
This query for example updates all rows with different random values-
UPDATE TEST_CITY
SET "name" = to_varchar(ABS(MOD(RANDOM(1), 1000000)))
Can I have something equivalent to this when random values are strings and should come from a separate table ?
I don't know specifically for Snowflake, but other databases sometimes optimize subqueries with a volatile function, resulting in a single value.
One solution that I've seen work is to use a correlated subquery:
UPDATE TEST_CITY
SET "CITY" = (select c.name
from city c
where c.id = (SELECT uniform(1, 50, random())) AND
test_city.city is not null -- any condition should do
);
Although the performance is likely to be worse, perhaps order by will work:
UPDATE TEST_CITY
SET "CITY" = (select c.name
from city c
order by random()
limit 1
);
Following query worked for me. I have used hash on column name to make the update work on all rows of my column -
UPDATE "TEST_CITY" SET "CITY" = C."NAME" FROM CITY C WHERE C."ID" =
ABS(HASH("CITY")%16917) + 1 ;
16197 are the number of rows I have in City table.
Thanks
Below code run for me
UPDATE TEST_CITY a SET a.CITY = b.NAME FROM (
SELECT NAME ,row_number() over (order by random()) AS id from CITY) b;

Microsoft SQL query multiple condition

I have an Attributes table which has a set of attributes identified by a unique number and then followed by their description - An example schema is
ID - AttributeName
with some sample data below
1 = FirstName
2 = LastName
3 = Phone
I then have an employees table which for the sake of simplicity has the following schema
ID - PersonID
AttribueID - INT Foreign key to the above attributes table
I need to create a stored proc that given a condition will return records based on one of the following conditions
If I pass in a 1 to the stored procedure the proc should return all records from the Person table who match the attribute ID 1 (First name)
If I pass in a 2 to the stored procedure the proc should return all records from the Person table who * DO NOT * match the attribute ID 1 (First name)
If I pass in a 3 to the stored procedure the proc should return all records from the Person table
I could do the following but feel it is not the best way this could be performed
DECLARE #IntID INT = 1 -- Set as 1 just for exmple
IF #IntID =1
BEGIN
SELECT * FROM Person where AttributeID IN (SELECT ID from Attributes Where ID =1) -- match on attribute 1
END
ELSE IF #IntID = 2
BEGIN
SELECT * FROM Person where AttributeID NOT IN (SELECT ID from Attributes Where ID =1) -- do not match on attribute 1
END
ELSE
BEGIN
SELECT * FROM Person where AttributeID IN (SELECT ID from Attributes) -- return match on all attributes
END
The above example has an extremely simple SELECT statement - in the real SQL there is a much larger set
Thanks in advance
If you don't wanna use dynamic sql then you may try this. And your query is too simple that for performance optimization, you may look to the table indexes for that.
DECLARE #IntID INT = 1 -- Set as 1 just for exmple
IF #IntID = 1 OR #IntID = 2
BEGIN
SELECT A.*
FROM Person A
LEFT JOIN Attributes B ON A.AttributeID = B.ID AND B.ID = 1
WHERE
A.AttributeID IS CASE #IntID WHEN 1 THEN NOT NULL WHEN 2 THEN NULL END
END
ELSE
BEGIN
SELECT A.*
FROM Person A
INNER JOIN Attributes B ON A.AttributeID = B.ID
END

Trigger for 2 Tables

this is a homework question, just to make it clear.
This is the relational schema:
PaperInvolvement (paperNr, academicId, paperRole)
Academic (academicId, acadName, employer)
So (academicID) is the primary key for Academic and (paperNr, academicId) is the primary key for PaperInvolvement table.
Here is the trigger that I am asked to do:
On PaperInvolvement after insert, update
On Academic after update
Prevent any 2 academics who work for the same company are involved in the same paper in the opposite roles.
Use stored procedure or cover it completely in trigger
There are only 2 roles available in this table, which is Reviewer and Author
This is what I have done so far:
CREATE TRIGGER TR_PaperInvolvement_1
ON PaperInvolvement
AFTER INSERT, UPDATE
AS
IF EXISTS
(
SELECT a.academicId, paperRole, paperNr
FROM
(SELECT academicId
FROM Academic
GROUP BY employer, academicId) AS a
JOIN
(SELECT academicId, paperRole, paperNr
FROM PaperInvolvement
GROUP BY paperNr, academicId, paperRole) AS p_inv
ON a.academicId = p_inv.academicId
WHERE paperRole = 'Author' AND paperRole = 'Reviewer'
)
BEGIN
RAISERROR('Cannot have 2 Academics from the same company to work on
different roles for this paper.',16,1)
ROLLBACK TRANSACTION
END
GO
My question is, based on the requirements (what I have listed on the bullet-lists), is this the correct way to answer the question?
Try this
CREATE TRIGGER TR_PaperInvolvement_Modify
ON PaperInvolvement
AFTER INSERT, UPDATE
AS
begin
if exists
(
select P.paperNr, A.employer
from PaperInvolvement as P
inner join Academic as A on A.academicID = P.academicID
where P.paperNr in (select i.paperNr from inserted as i)
group by P.paperNr, A.employer
having
count(case when P.paperRole = 'Author' then 1 end) > 0 and
count(case when P.paperRole = 'Reviewer' then 1 end) > 0
)
begin
raiserror('Cannot have 2 Academics from the same company to work on different roles for this paper.', 16, 1)
rollback transaction
end
end