问题
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.
- The client Can Be either an ordinary person, or a company
- If an ordinary person is, she will not have FK in "legal_entity" table.
- If an ordinary person is, she Should only one record in "user_client" table.
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)
);
回答1:
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));
回答2:
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;
回答3:
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;
来源:https://stackoverflow.com/questions/11766074/how-to-create-a-unique-index-with-conditional-and-subquery-in-postgresql