PostgreSQL Stored Procedures – Part 2

In a follow up to our initial Stored Procedures article, here we present some more complex examples based on real-world work we’ve completed on a PostgreSQL 8.3 database.

Fetching A Newly Inserted ID

Often times you will want to insert a record into a table that has a default ID string.   There is any easy method for doing this with the INSERT…RETURNING syntax.

In this stored procedure example below we add a new group name to our groups table and get back the auto-generated ID (primary key) in the newid variable.   We then use that to insert a record in our group members table AND return the newly created ID back from the stored procedure, making it easy for the calling program to catch the new ID:

  1. DECLARE
  2.     newid BIGINT;
  3. BEGIN
  4.     INSERT INTO groups (group_name) VALUES ($1) RETURNING group_id INTO newid;
  5.     INSERT INTO group_members (group_id, avatar_id,groupmember_admin) VALUES (newid,$2,‘t’);
  6.     RETURN newid;
  7. END

Select Loop With String Building

This first example will select a set of records in the child table of a one-to-many relationship (the many part) and build up semi-colon separated string comprised of the name field for each child record.  The name field contains the human-readable name of on item a person is wearing.

sp_what_am_i_wearing(<id>)

This stored procedure extracts all of the items a person is wearing as a string separated by semi-colons by simply passing in the user ID.

definition

  1. CREATE OR REPLACE FUNCTION sp_what_am_i_wearing(id bigint) RETURNS text AS $$
  2.     DECLARE
  3.         retval text = ;
  4.         row_data UserRow%ROWTYPE;
  5.     BEGIN
  6.         FOR row_data IN SELECT * FROM MyObjects WHERE id = $1
  7.            LOOP
  8.               retval := retval || row_data.object_name || ‘;’;
  9.           END LOOP;
  10.           RETURN retval;
  11.       END;
  12. $$ LANGUAGE plpgsql;

Usage and Output

game_data=# select sp_what_am_i_wearing(2);
avatariswearing
-----------------------------------------------------------------
hat_01;shirt_01;pants_22;shoes_32;

Insert/Update Loop With String Decomposition

This second example is the “setter” companion to our prior example’s “getter” functionality.  This version breaks down a human-readable string with names separated by colons and then updates the child table in the one-to-many relationship.  We not only have to add records to that table for any new objects we might be wearing but also remove any pre-existing records from our prior wardrobe.

First we select any object that is not in our character string but IS in the child objects table, loop through those records, and delte them.  In essence we are deleting anything we are no longer wearing.  This may be ALL of the items we were wearing previously or just a single item if we only changed something like our hat or shoes.

In the second part of the procedure we loop through all the objects in the string as we break it down based on the semicolon, and only select those items that are NOT left remaining in the child table of our one-to-many relationship.  Remember, that child table now contains only those items we were wearing before and did not change.  If that child set is empty we’ll end up adding new records for EVERY object in our string.   We fetch the ID of the objects we are wearing from the objects table and then insert our user ID, the object ID, and a flag in the child table saying that we are wearing the various objects.

sp_set_what_i_wear(<id>,<wearing_string>)

This stored procedure stores all of the items a user is wearing, breaking down a string separated by semi-colons and using that to add or delete objects from the avatar objects table.

Definition

  1. CREATE OR REPLACE FUNCTION sp_set_what_i_wear(id bigint, wearing text) RETURNS void AS $$
  2.     DECLARE
  3.        ao_id BIGINT;
  4.        obj_id BIGINT;
  5.     BEGIN
  6.        – Delete what I used to be wearing, but am no longer wearing
  7.        
  8.        FOR ao_id IN SELECT object_id FROM ObjectList
  9.               WHERE id=$1 AND name NOT IN (SELECT regexp_split_to_table($2,E‘;’))
  10.        LOOP
  11.               DELETE FROM MyObjects WHERE object_id = ao_id;
  12.        END LOOP;
  13.  
  14.        – Fetch the object IDs of things I am now wearing, but don’t show up in my object list
  15.        – Insert the object IDs into that child table, called "MyObjects" that links me to stuff I wear
  16.        
  17.        FOR obj_id IN SELECT object_id FROM Objects
  18.               WHERE name IN (SELECT regexp_split_to_table($2,E‘;’))
  19.                      AND name NOT IN (SELECT name FROM ObjectList WHERE id=$1)
  20.        LOOP
  21.               INSERT INTO MyObjects (id,object_id,onperson) VALUES ($1,obj_id,‘t’);
  22.        END LOOP;
  23. END;
  24. $$ LANGUAGE plpgsql;

Usage and Output

Note: current return type is VOID.

It may be wise to set a return value showing the items that were in the set string but don’t exist in the DB.  This can be done with a simple select from objects not in select from regexp_split_to_table.  This is a performance hit, so it was left out for this revision.


game_data=# select sp_set_what_i_wear(2,'hat_01;shirt_10;pants_11;shoes_33;');
sp_set_avatar_is_wearing
--------------------------

(1 row)

About the Author

Mr. Cleveland started his high-tech career in the early 80's, starting as a computer technician at Cognos, lead engineer at Recital Corporation, and on to ProActive Web Marketing - his first technology start up. During his career he has served as a consultant for Data General, Kimberly Clark, Kraft, Philip Morris, Rich Foods, and Telefonica. He has appeared as a keynote speaker for the USVI Economic Development Summit, been a lead interviewee for Microsoft's Clicktrade infomercials, and has been a cited expert in Wall Street journal on performance advertising, Internet retail, and cybercrime. His recent activities include launching several successful start up companies including Time Magazine award winner The Lobster Net. He currently serves as CTO of 3DChat, CTO and co-founder of Model Locate, and founder of Cyber Sprocket Labs. Mr. Cleveland can be reached at the Cyber Sprocket Labs office as well as on Linked In.