Reputation: 2985
I use PGSQL and try add the index below.
CREATE UNIQUE INDEX fk_client ON user_client (fk_client) WHERE fk_client NOT IN(SELECT fk_client FROM legal_entity);
But ... It is not possible, because there is allowed run a sub query in the creation of an index.
I get the following error:
ERROR: cannot use subquery in index predicate
Is there any way to solve this problem?
The above model Represents the situation of the case.
With index does not, but is there any way to solve this problem?...
Script generate tables:
-- user is a special word, then renamed to users
CREATE TABLE users (
id_user INT,
name VARCHAR(50) NOT NULL,
CONSTRAINT user_pkey PRIMARY KEY (id_user)
);
CREATE TABLE client (
id_client INT,
CONSTRAINT client_pkey PRIMARY KEY (id_client)
);
CREATE TABLE legal_entity (
fk_client INT,
federal_id VARCHAR(14) NOT NULL,
CONSTRAINT legal_entity_pkey PRIMARY KEY (fk_client),
CONSTRAINT legal_entity_fkey FOREIGN KEY (fk_client) REFERENCES client (id_client)
);
CREATE TABLE user_client (
fk_client INT,
fk_user INT,
CONSTRAINT user_client_pkey PRIMARY KEY (fk_client, fk_user),
CONSTRAINT user_client_fkey_1 FOREIGN KEY (fk_client) REFERENCES client (id_client),
CONSTRAINT user_client_fkey_2 FOREIGN KEY (fk_user) REFERENCES users (id_user)
);
Upvotes: 4
Views: 5483
Reputation: 771
The downside to using a rules is that rules simply rewrite the query after it is parsed so if the data is added through a trigger, it will not fire. It's safer to add a CHECK constraint that calls a function with your logic. If I follow your logic correctly, it should be something like:
CREATE OR REPLACE FUNCTION check_user_client(fkc int)
RETURNS boolean AS
$$
DECLARE
i int;
BEGIN
SELECT count(*) INTO i FROM legal_entity WHERE fk_client = fkc;
IF (i > 0) THEN
RETURN true;
END IF;
SELECT count(*) INTO i FROM user_client WHERE fk_client = fkc;
IF (i = 0) THEN
RETURN true;
END IF;
RETURN false;
END
$$ LANGUAGE plpgsql;
ALTER TABLE user_client ADD CONSTRAINT unique_user CHECK (check_user_client(fk_client));
Upvotes: 4
Reputation: 2985
I solved my problem by adding a rule table:
CREATE OR REPLACE RULE rule_test AS ON INSERT
TO user_client WHERE (
(SELECT fk_client FROM legal_entity WHERE fk_client = new.fk_client) IS NULL) AND (
(SELECT fk_client FROM user_client WHERE fk_client = new.fk_client) IS NOT NULL)
DO INSTEAD NOTHING;
Upvotes: 1
Reputation: 44250
DROP SCHEMA tmp CASCADE;
CREATE SCHEMA tmp ;
SET search_path=tmp;
CREATE TABLE foo
( id INTEGER NOT NULL PRIMARY KEY
);
CREATE TABLE tbl_relation
( id INTEGER NOT NULL PRIMARY KEY
, foo_id INTEGER REFERENCES foo(id)
, fk_1 INTEGER
);
CREATE UNIQUE INDEX fk_1 ON tbl_relation (fk_1)
WHERE foo_id IS NULL;
UPDATE: this is the new situation after the modification of the data-model. The point is: legal_entity and client seem to share a key domain (which doe not seen right) Giving legal_entity its own key-domain seems more appropiate.
DROP SCHEMA tmp CASCADE;
CREATE SCHEMA tmp ;
SET search_path=tmp;
-- user is a special word, then renamed to users
CREATE TABLE users
( id INTEGER PRIMARY KEY
-- name is a special word
, zname VARCHAR(50) NOT NULL
);
CREATE TABLE client
(id INTEGER PRIMARY KEY
);
CREATE TABLE legal_entity
( id INTEGER PRIMARY KEY
, client_id INTEGER REFERENCES client(id)
, federal_id VARCHAR(14) NOT NULL
);
CREATE TABLE user_client
( client_id INTEGER REFERENCES client (id)
, user_id INTEGER REFERENCES users (id)
, legal_id INTEGER REFERENCES legal_entity(id)
, CONSTRAINT user_client_pkey PRIMARY KEY (client_id, user_id)
);
CREATE INDEX tres_stupide ON user_client (client_id) WHERE legal_id IS NULL;
Upvotes: -2