NickNick
NickNick

Reputation: 171

How to add a foreign key constraint to same table using ALTER TABLE in PostgreSQL

To create table I use:

CREATE TABLE category
(
  cat_id serial NOT NULL,
  cat_name character varying NOT NULL,
  parent_id integer NOT NULL,
  CONSTRAINT cat_id PRIMARY KEY (cat_id)
)
WITH (
  OIDS=FALSE
);
ALTER TABLE category
  OWNER TO pgsql;

parent_id is a id to another category. Now I have a problem: how to cascade delete record with its children? I need to set parent_id as foreign key to cat_id. I try this:

  ALTER TABLE category 
ADD CONSTRAINT cat_cat_id_fkey FOREIGN KEY (parent_id)
      REFERENCES category (cat_id) MATCH SIMPLE
      ON UPDATE CASCADE ON DELETE CASCADE

But it falls with:

ERROR:  insert or update on table "category" violates foreign key constraint "cat_cat_id_fkey"
DETAIL:  Key (parent_id)=(0) is not present in table "category".

Upvotes: 5

Views: 22496

Answers (3)

CodZilla
CodZilla

Reputation: 151

This is quite simple.
Here the foreign key parent_id refers to cat_id.
Here a record with parent_id=0 exists but not a record with cat_id=0.

Upvotes: 1

wildplasser
wildplasser

Reputation: 44250

-- create some fake data for testing
--
DROP SCHEMA tmp CASCADE;
CREATE SCHEMA tmp ;
SET search_path=tmp;
CREATE TABLE category

(
  cat_id serial NOT NULL,
  cat_name character varying NOT NULL,
  parent_id integer NOT NULL,
  CONSTRAINT cat_id PRIMARY KEY (cat_id)
);

INSERT INTO category(cat_name,parent_id)
SELECT 'Name_' || gs::text
        , gs % 3
FROM generate_series(0,9) gs
        ;

        -- find the records with the non-existing parents
SELECT ca.parent_id , COUNT(*)
FROM category ca
WHERE NOT EXISTS (
        SELECT *
        FROM category nx
        WHERE nx.cat_id = ca.parent_id
        )
GROUP BY ca.parent_id
        ;

        -- if all is well: proceed
        -- make parent pointer nullable
ALTER TABLE category
        ALTER COLUMN parent_id DROP NOT NULL
        ;

        -- set non-existing parent pointers to NULL
UPDATE category ca
SET parent_id = NULL
WHERE NOT EXISTS (
        SELECT *
        FROM category nx
        WHERE nx.cat_id = ca.parent_id
        )
        ;

        -- Finally, add the FK constraint
ALTER TABLE category
        ADD CONSTRAINT cat_cat_id_fkey FOREIGN KEY (parent_id)
              REFERENCES category (cat_id) MATCH SIMPLE
              ON UPDATE CASCADE ON DELETE CASCADE
        ;

Upvotes: 3

Ihor Romanchenko
Ihor Romanchenko

Reputation: 28641

The problem you have - what would be the parent_id of a category at the top of the hierarchy?

If it will be null - it will break the NOT NULL constratint.

If it will be some arbitrary number like 0 - it will break the foreign key (like in your example).

The common solution - drop the NOT NULL constratint on the parent_id and set parent_id to null for top categories.

Upvotes: 6

Related Questions