Thread: Using function returning multiple values in a select

Using function returning multiple values in a select

From
Lele Gaifax
Date:
Hi all,

I have a function that returns multiple values, computing them from the input
parameters, and I need to use it within an existing query.

Say I have the following table:

  CREATE TABLE products (
    id SERIAL NOT NULL,
    description VARCHAR(64),
    PRIMARY KEY (id)
  )

and the following simplified function:

  CREATE OR REPLACE FUNCTION price_and_discount(
      in p_idproduct integer, p_idcustomer integer,
      out price numeric(15,4), out discount numeric(7,4)
  ) AS $$
  BEGIN
    -- determine price and discount for the given customer,
    -- just compute some value to exemplify
    price := 123.456 * p_idproduct;
    discount := 12.34;
    RETURN;
  END;
  $$ LANGUAGE plpgsql

I need to get the listing of products with their price and discount for a
given customer, and the following query seems doing the right thing:

  SELECT p.id, p.description, pad.price, pad.discount
  FROM products AS p
  LEFT JOIN price_and_discount(p.id, 123) AS pad ON true

that effectively outputs:

  # SELECT p.id, p.description, pad.price, pad.discount
    FROM products AS p
    LEFT JOIN price_and_discount(p.id, 123) AS pad ON true;
   id | description |  price  | discount
  ----+-------------+---------+----------
    1 | Foo         | 123.456 |    12.34
    2 | Bar         | 246.912 |    12.34

I used this kind of statement a lot under Firebird, years ago, even if I were
warned on its mailing list that it worked "by chance".

In this particular case

  SELECT p.id, p.description, pad.price, pad.discount
  FROM products AS p, price_and_discount(p.id, 123) AS pad

does produce the same result. However, I usually try to avoid the latter
syntax, that suggests a cross-product between the FROM-clauses.

Which alternative would you recommend?

Thanks in advance,
ciao, lele.
--
nickname: Lele Gaifax | Quando vivrò di quello che ho pensato ieri
real: Emanuele Gaifas | comincerò ad aver paura di chi mi copia.
lele@metapensiero.it  |                 -- Fortunato Depero, 1929.

Re: Using function returning multiple values in a select

From
Adrian Klaver
Date:
On 10/24/2015 10:56 AM, Lele Gaifax wrote:
> Hi all,
>
> I have a function that returns multiple values, computing them from the input
> parameters, and I need to use it within an existing query.
>
> Say I have the following table:
>
>    CREATE TABLE products (
>      id SERIAL NOT NULL,
>      description VARCHAR(64),
>      PRIMARY KEY (id)
>    )
>
> and the following simplified function:
>
>    CREATE OR REPLACE FUNCTION price_and_discount(
>        in p_idproduct integer, p_idcustomer integer,
>        out price numeric(15,4), out discount numeric(7,4)
>    ) AS $$
>    BEGIN
>      -- determine price and discount for the given customer,
>      -- just compute some value to exemplify
>      price := 123.456 * p_idproduct;
>      discount := 12.34;
>      RETURN;
>    END;
>    $$ LANGUAGE plpgsql
>
> I need to get the listing of products with their price and discount for a
> given customer, and the following query seems doing the right thing:
>
>    SELECT p.id, p.description, pad.price, pad.discount
>    FROM products AS p
>    LEFT JOIN price_and_discount(p.id, 123) AS pad ON true
>
> that effectively outputs:
>
>    # SELECT p.id, p.description, pad.price, pad.discount
>      FROM products AS p
>      LEFT JOIN price_and_discount(p.id, 123) AS pad ON true;
>     id | description |  price  | discount
>    ----+-------------+---------+----------
>      1 | Foo         | 123.456 |    12.34
>      2 | Bar         | 246.912 |    12.34
>
> I used this kind of statement a lot under Firebird, years ago, even if I were
> warned on its mailing list that it worked "by chance".
>
> In this particular case
>
>    SELECT p.id, p.description, pad.price, pad.discount
>    FROM products AS p, price_and_discount(p.id, 123) AS pad
>
> does produce the same result. However, I usually try to avoid the latter
> syntax, that suggests a cross-product between the FROM-clauses.
>
> Which alternative would you recommend?

An actual working example that shows exactly what you want to achieve
would help. Mainly where does the base price originate?  In the
meantime, if there is no direct relation between a  product and customer
discount I am not sure how you can avoid the above in the case you show
above. That being generating a discount table for all products for a
particular customer. For an order I could see the order being the
relation that connects the customer(and their discount) to the
particular products on the order.

>
> Thanks in advance,
> ciao, lele.
>


--
Adrian Klaver
adrian.klaver@aklaver.com


Re: Using function returning multiple values in a select

From
Lele Gaifax
Date:
Adrian Klaver <adrian.klaver@aklaver.com> writes:

> An actual working example that shows exactly what you want to achieve would
> help. Mainly where does the base price originate?

A product has a base price (and maybe a base discount), but that may be
overridden by particular rules based on the customer, on the product type and
on the period of the year (I omitted the latter condition from my example, to
simplify).

There is a table "discounts" that for particular product, or its type, or for
a particular customer, or for a particular period, or a combination of these,
may specify either a new fixed price or a special discount.

For example, consider a product P, of type T, with a base price of 100$: I
need to be able to specify that for customer C1 the price is fixed at 90$,
while for customer C2 its price is 92$; moreover, all products of type T, in
the period from December 1st to December 24th, enjoy a discount of 10% for
everybody.  It is obviously impractical to "explode" all these cases into a
flat table.

The function I mentioned is already taking all these details into account and
producing the expected results.

> In the meantime, if there is no direct relation between a product and
> customer discount I am not sure how you can avoid the above in the case you
> show above. That being generating a discount table for all products for a
> particular customer. For an order I could see the order being the relation
> that connects the customer(and their discount) to the particular products on
> the order.

The function is used to produce the listing of the products a customer *may*
buy in an online e-commerce, so the actual order has yet to come.

My doubt was about the better way to use that function from within the query
that produces the listing.

I hope this is clearer now,

thank you,
ciao, lele.
--
nickname: Lele Gaifax | Quando vivrò di quello che ho pensato ieri
real: Emanuele Gaifas | comincerò ad aver paura di chi mi copia.
lele@metapensiero.it  |                 -- Fortunato Depero, 1929.

Re: Using function returning multiple values in a select

From
Adrian Klaver
Date:
On 10/24/2015 12:37 PM, Lele Gaifax wrote:
> Adrian Klaver <adrian.klaver@aklaver.com> writes:
>
>> An actual working example that shows exactly what you want to achieve would
>> help. Mainly where does the base price originate?
>
> A product has a base price (and maybe a base discount), but that may be
> overridden by particular rules based on the customer, on the product type and
> on the period of the year (I omitted the latter condition from my example, to
> simplify).
>
> There is a table "discounts" that for particular product, or its type, or for
> a particular customer, or for a particular period, or a combination of these,
> may specify either a new fixed price or a special discount.
>
> For example, consider a product P, of type T, with a base price of 100$: I
> need to be able to specify that for customer C1 the price is fixed at 90$,
> while for customer C2 its price is 92$; moreover, all products of type T, in
> the period from December 1st to December 24th, enjoy a discount of 10% for
> everybody.  It is obviously impractical to "explode" all these cases into a
> flat table.
>
> The function I mentioned is already taking all these details into account and
> producing the expected results.
>
>> In the meantime, if there is no direct relation between a product and
>> customer discount I am not sure how you can avoid the above in the case you
>> show above. That being generating a discount table for all products for a
>> particular customer. For an order I could see the order being the relation
>> that connects the customer(and their discount) to the particular products on
>> the order.
>
> The function is used to produce the listing of the products a customer *may*
> buy in an online e-commerce, so the actual order has yet to come.
>
> My doubt was about the better way to use that function from within the query
> that produces the listing.

Well, if I am following the above correctly you have a table 'discounts'
that relates customers(and their discounts) to products. Given that then
it should be possible to do explicit joins between products and a
customer provided your function returns the product id with the
associated discounted price. The question being is '*may*' for a single
item at time or the entire list of products associated with a customer?
If it is for the entire list then SETOF might come in handy:

http://www.postgresql.org/docs/9.3/interactive/plpgsql-control-structures.html#PLPGSQL-STATEMENTS-RETURNING

40.6.1.2. RETURN NEXT and RETURN QUERY

>
> I hope this is clearer now,
>
> thank you,
> ciao, lele.
>


--
Adrian Klaver
adrian.klaver@aklaver.com