Re: insert serial numbers - Mailing list pgsql-general

From Sven Willenberger
Subject Re: insert serial numbers
Date
Msg-id 43BC4EA7.1070605@dmv.com
Whole thread Raw
In response to Re: insert serial numbers  (Albert Vernon Smith <contact1@absentia.com>)
List pgsql-general

Albert Vernon Smith presumably uttered the following on 01/03/06 13:36:
> I figured it out myself.  Not TOO difficult.  I was just having a  hard
> time wading through the documentation before.  Giving the answer  out
> here, just in case any one else wants to see the solution (not  using
> reserved words ;-)).
>
> 1.  Made function:
>
> CREATE FUNCTION "return_one_id" () RETURNS "trigger" AS '
> DECLARE
>     my_id bigint;
> BEGIN
>     select into my_id one_id from one where one_text=NEW.one_text;
>     NEW.one_id := my_id;
>     return NEW;
>     END;
> ' LANGUAGE "plpgsql"
>
> 2. Made trigger:
>
> CREATE TRIGGER return_one_id BEFORE INSERT OR UPDATE ON two FOR EACH
> ROW EXECUTE PROCEDURE return_one_id()
>
> Voila!
>
> -albert
>
>
> On 3.1.2006, at 14:36, Albert Vernon Smith wrote:
>
>> I have two tables, listed as below.  I'm inserting values for  "text"
>> into table "two" (which must already exist as "text" values  in table
>> "one").  When I do that, I'd like to also insert the  associated
>> "one_id" value from table "one" into the field  "two.one_id".  How is
>> best to go about that?  I imagine this would  be best be done with a
>> trigger rather than a rule, but I don't know  enough on how to go
>> about that.  Can someone help point me in the  right direction.  (I
>> did try it with rules as listed below, but the  serial value
>> increments, so the approach doesn't work on a single  row.)
>>
>> --
>> My tables:
>>
>> CREATE TABLE "one" (
>>     "one_id" BIGSERIAL,
>>     "text" text NOT NULL,
>>     CONSTRAINT "iu_text" UNIQUE (text)
>> )
>>
>> CREATE TABLE "two" (
>>     "two_id" BIGSERIAL,
>>     "text" text NOT NULL,
>>     "one_id" bigint,
>>     CONSTRAINT "$1" FOREIGN KEY (text) REFERENCES one(text) ON  UPDATE
>> SET NULL
>> )
>>
>> --
>>
>> My failed rule approaches:
>>
>> CREATE RULE two_insert AS ON INSERT TO two DO UPDATE two SET one_id  =
>> (SELECT one.one_id FROM one WHERE (new.text = one.text)) WHERE
>> (new.two_id = two.two_id);
>>
>> The following does work, but it updates all rows with the same  text.
>> I'd rather be more efficient, and only work with the current  row.:
>>
>> CREATE RULE two_insert AS ON INSERT TO two DO UPDATE two SET one_id  =
>> (SELECT one.one_id FROM one WHERE (new.text = one.text)) WHERE
>> (new.text = two.text);
>>

The reason the rule failed, by the way is that the rule gets expanded
(like a macro). Whereas in a function "new.two_id" actually uses the
value of two_id that is about to be inserted into the table, in a rule
situation "new.two_id" gets expanded into its definition, namely
nextval(sequence name) so the comparison is between a two_id that really
doesn't exist in the table (its value is created by the rule after the
two_id that gets created on insert) which will always result in a non-match.

I had this same issue with a pair of table I had where I wanted to
update a customer information table with a live customer number that was
created in a different table. As in your case, a trigger solved my
situation.

Sven

pgsql-general by date:

Previous
From: Assad Jarrahian
Date:
Subject: pg admin III and primary keys (for backup/restore)
Next
From: Matthew Peter
Date:
Subject: plpgsql question