FOR loop on PLpgSQL function result

半世苍凉 提交于 2019-12-24 13:12:23

问题


I wrote a PLpgSQL function which should return SETOF products table:

CREATE OR REPLACE FUNCTION get_products_by_category
(selected_category_id smallint DEFAULT 1) RETURNS SETOF products AS
$BODY

$BEGIN
RETURN QUERY  (SELECT * FROM products WHERE CategoryID = selected_category_id);
END;

$BODY$
LANGUAGE plpgsql VOLATILE NOT LEAKPROOF
COST 100
ROWS 1000;

And next I want to iterate over results in another function (non-finished view, because I try to add in PgAdmin III and I have errors):

DECLARE
    R RECORD;
BEGIN
IF TG_TABLE_NAME != 'Categories' THEN
    RAISE 'This trigger function is for Categories, but was called for %', TG_TABLE_NAME;
FOR R IN get_products_by_category(1) LOOP
    UPDATE products SET CategoryID = NEW.id WHERE id = R.id;
RETURN NEW;

The idea is that I have some Products in database, which have default Category ID as 1. And then, when new Category is added, trigger is fired which update CategoryID (from freshly added object) for Products with default CategoryID - maybe it sounds stupid but I am learning triggers with Northwind Database and I had to create task for myself. :)

But I can't save it, because of errors near get_products_by_category(1). Is in PLpgSQL (I am using 9.3 version) any possibility to write something like in Java:

for (Product product: dao.getProductsByCategory(categoryId))

Updated code:

DECLARE
    selected_products products[];
    product products;
BEGIN
IF TG_TABLE_NAME != 'categories' THEN
    RAISE 'This trigger function is for Categories, but was called for %', TG_TABLE_NAME;
END IF;
selected_products := get_products_by_category(1);
FOR product IN selected_products LOOP
    UPDATE products SET CategoryID = NEW.id
        WHERE id = R.id;
END LOOP;
RETURN NEW;
END;

回答1:


Basically, you need to read this chapter in the manual: Looping Through Query Results.
And about plpgsql trigger functions.

CREATE OR REPLACE FUNCTION my_trigger_func()
   RETURNS trigger AS
$func$
DECLARE
    _prod products;
BEGIN
   IF TG_TABLE_NAME <> 'categories' THEN
      RAISE EXCEPTION 'Trigger func for "categories", not for %', TG_TABLE_NAME;
   END IF;

   FOR _prod IN 
      SELECT * FROM get_products_by_category(1)
   LOOP
      UPDATE products p
      SET    categoryid = NEW.id
      WHERE  p.id = _prod.id;
   END LOOP;
   RETURN NEW;
END
$func$  LANGUAGE plpgsql

Or:

...
DECLARE
    _id int;
BEGIN
   ...
   FOR _id IN 
      SELECT id FROM get_products_by_category(1)
   LOOP
      ...
      WHERE  p.id = _id;
...

Both just as proof of concept. Most of the time, there is a superior set-based solution around the corner. Like here:

UPDATE products p
SET    categoryid = NEW.id
FROM   get_products_by_category(1) x
WHERE  p.id = x.id;

You can use a set-returning function like get_products_by_category(1) just like you would use a table in most contexts.

Notes

  • You must understand dollar-quoting:

    • Insert text with single quotes in PostgreSQL
  • != is valid, too, but rather use the SQL standard operator <>.

  • Table names are not unique. to be sure you must check TG_TABLE_NAME and TG_TABLE_SCHEMA.

    • How to check if a table exists in a given schema


来源:https://stackoverflow.com/questions/29880469/for-loop-on-plpgsql-function-result

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!