0

ᕼello! I think I have a somewhat tricky postgres situation:

parents have children. children have an age, and a flag that they are the appreciated.

The rule: a parent can't appreciate two children of the same age!

My question is: how to enforce this rule?

Current schema:

CREATE TABLE parent (
  id SERIAL PRIMARY KEY,
  name VARCHAR(50) NOT NULL
);

CREATE TABLE child (
  id SERIAL PRIMARY KEY,
  parent INTEGER REFERENCES parent(id) NOT NULL,
  name VARCHAR(50) NOT NULL,
  age INTEGER NOT NULL,
  appreciated BOOLEAN NOT NULL
);

Put some values in:

INSERT INTO parent(name) VALUES
  ('bob'),    -- assume bob's id = 0
  ('mary');   -- assume mary's id = 1

INSERT INTO child(parent, name, age, appreciated) VALUES
  (0, 'child1', 10, FALSE), -- Bob has children 1, 2, 3
  (0, 'child2', 10, FALSE),
  (0, 'child3', 15, FALSE),
  (1, 'child4', 20, FALSE), -- Mary has children 4, 5, 6
  (1, 'child5', 20, FALSE),
  (1, 'child6', 10, FALSE);

All fine so far. No child is appreciated, which is always valid.

Mary is allowed to appreciate child6:

UPDATE child SET appreciated=TRUE WHERE name='child6';

Bob is allowed to appreciate child2. child2 is the same age as child6 (who is already appreciated), but child6 is not Bob's child.

UPDATE child SET appreciated=TRUE WHERE name='child2';

Bob now cannot appreciate child1. This child1 is the same age as child2, and child2 is already appreciated.

UPDATE child SET appreciated=TRUE WHERE name='child2'; -- This needs to FAIL!

How do I enforce such a constraint? I'm open to all kinds of solutions, but modifying the general schema is not an option.

Thanks in advance!

2
  • Are you able to limit upserts to only be accomplished by a procedure that you define? Commented Mar 20, 2018 at 22:26
  • Unfortunately no! Commented Mar 20, 2018 at 22:27

3 Answers 3

4

How about a UNIQUE partial index, like so:

CREATE UNIQUE INDEX ON child(parent,age) WHERE appreciated;

So every pair of parent,age has to be unique, but only when appreciated children are considered.

Sign up to request clarification or add additional context in comments.

3 Comments

Holy moly postgres is insane and this is exactly the answer I was looking for!
I have a quick followup question: can I achieve this with ALTER TABLE syntax?
No. If it wasn't for the WHERE part it would have been possible, but being a partial index, it has to be created with CREATE INDEX.
2

You might want to use a trigger that activates BEFORE the insert/update and that fails if the constraint you create is not satisfied. I suppose it should be like

create trigger <trigger_name>
before insert or update on <table_name>
for each row
declare
dummy number;
begin
select count(*)
into dummy
from <table_name>
where (appreciated=TRUE and :new.child = child and :new.age = age);

if dummy > 0 then
  raise_application_error(-20001,'Too many appreciated children');
end if;
end;

Some documentation

3 Comments

This sounds promising - but how do I see that multiple children of the same age are appreciated BEFORE both those children are appreciated? It sounds like I would have to have some notion of the future intent of an incoming SQL query... I'd really appreciate some more detailed example code!
in the <code> section you have access to the mutating table and to the row that is going to be inserted or updated :)
Ah that sounds like it could work! I'd really appreciate a more detailed answer, this only really shows me how to define a trigger.
0

The simplest thing I would think to do is add a flag grateful(?) == false to the parent model and when child.appreciated == true { parent.grateful == true }

Check the value of parent.grateful in the function that acts on child.appreciated. If parent.grateful == true return "sorry this parent has already shown their appreciation." LOL this is an interesting concept though. Good Luck. :)

1 Comment

Unfortunately parents can appreciate many children! Just not two children of the same age.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.