Thread: Converting stored procedure from mySQL

Converting stored procedure from mySQL

From
Andreas Hjortsberg
Date:
Hello,

I'm trying to convert one stored procedure from mysql but I have some problems to get it runing.
It's quite basic but I haven't found the answer in the docs or on the mailing lists.

So here is my function

CREATE OR REPLACE FUNCTION  sp_order_amount_per_office(accountingid IN INTEGER, code OUT VARCHAR,officetotal OUT
NUMERIC)as $$ 

BEGIN

Select office.code as code, sum(transaction.countervaluecustomer) as officetotal

        FROM office

        LEFT OUTER JOIN receipt

        ON (receipt.officeid=office.officeid)

        LEFT OUTER JOIN transaction

        ON  (transaction.receiptid = Receipt.receiptid) and (receipt.accountingid=accountingid)

        GROUP BY code order by code;

END ;
$$ LANGUAGE plpgsql;

I can run the select query in pqAdmin and it give me the correct result, but then I try to run it inside my function as
Ialso can run independently I get the following error message 

ERROR:  syntax error at or near "$1" at character 25
QUERY:  SELECT  office.code as  $1 , sum(transaction.countervaluecustomer) as  $2  FROM office LEFT OUTER JOIN receipt
ON(receipt.officeid=office.officeid) LEFT OUTER JOIN transaction ON (transaction.receiptid = Receipt.receiptid) and
(receipt.accountingid=$3 ) GROUP BY  $1  order by  $1  
CONTEXT:  SQL statement in PL/PgSQL function "sp_order_amount_per_office" near line 16

It's my first day with postgre so it is probably something very simple

Regards

Andreas


Re: Converting stored procedure from mySQL

From
Tom Lane
Date:
Andreas Hjortsberg <andreas.hjortsberg@x-change.se> writes:
> So here is my function

> CREATE OR REPLACE FUNCTION  sp_order_amount_per_office(accountingid IN INTEGER, code OUT VARCHAR,officetotal OUT
NUMERIC)as $$ 
> BEGIN
> Select office.code as code, sum(transaction.countervaluecustomer) as officetotal
>         FROM office
>         LEFT OUTER JOIN receipt
>         ON (receipt.officeid=office.officeid)
>         LEFT OUTER JOIN transaction
>         ON  (transaction.receiptid = Receipt.receiptid) and (receipt.accountingid=accountingid)
>         GROUP BY code order by code;
> END ;
> $$ LANGUAGE plpgsql;

You're getting burnt by a standard beginner gotcha: avoid using plpgsql
variables (including named parameters) that are named the same as fields
in the queries within the function.  In the above example, plpgsql
replaces *every* standalone occurrence of "code" with a "$n" reference
to its "code" variable.  It knows not to replace "office.code", but
that's about the extent of its intelligence.

Another problem here is that you must use SELECT INTO if you want to
assign to a plpgsql variable.  As written, the above SELECT would just
discard its results.

Another problem is that the SELECT seems to be designed to return
multiple rows --- what are you expecting will happen with that?
A SELECT INTO would only store the first row's values into the
variables.

You could write the function like this, which'd fix the first two
problems, but I don't understand exactly what you're hoping to
accomplish so I don't know what to do about the third point:

CREATE OR REPLACE FUNCTION  sp_order_amount_per_office(p_accountingid IN INTEGER, p_code OUT VARCHAR, p_officetotal OUT
NUMERIC)as $$ 
BEGIN
Select office.code, sum(transaction.countervaluecustomer)
INTO p_code, p_officetotal
        FROM office
        LEFT OUTER JOIN receipt
        ON (receipt.officeid=office.officeid)
        LEFT OUTER JOIN transaction
        ON  (transaction.receiptid = Receipt.receiptid) and (receipt.accountingid=p_accountingid)
        GROUP BY code order by code;
END ;
$$ LANGUAGE plpgsql;

            regards, tom lane

Re: Converting stored procedure from mySQL

From
Andreas Hjortsberg
Date:
Thank you Tom for your quick reply!

Tom Lane wrote:
> Andreas Hjortsberg <andreas.hjortsberg@x-change.se> writes:
>
>>So here is my function
>
>
>>CREATE OR REPLACE FUNCTION  sp_order_amount_per_office(accountingid IN INTEGER, code OUT VARCHAR,officetotal OUT
NUMERIC)as $$ 
>>BEGIN
>>Select office.code as code, sum(transaction.countervaluecustomer) as officetotal
>>        FROM office
>>        LEFT OUTER JOIN receipt
>>        ON (receipt.officeid=office.officeid)
>>        LEFT OUTER JOIN transaction
>>        ON  (transaction.receiptid = Receipt.receiptid) and (receipt.accountingid=accountingid)
>>        GROUP BY code order by code;
>>END ;
>>$$ LANGUAGE plpgsql;
>
>
> You're getting burnt by a standard beginner gotcha: avoid using plpgsql
> variables (including named parameters) that are named the same as fields
> in the queries within the function.  In the above example, plpgsql
> replaces *every* standalone occurrence of "code" with a "$n" reference
> to its "code" variable.  It knows not to replace "office.code", but
> that's about the extent of its intelligence.
>
> Another problem here is that you must use SELECT INTO if you want to
> assign to a plpgsql variable.  As written, the above SELECT would just
> discard its results.
>
> Another problem is that the SELECT seems to be designed to return
> multiple rows --- what are you expecting will happen with that?
> A SELECT INTO would only store the first row's values into the
> variables.
>
> You could write the function like this, which'd fix the first two
> problems, but I don't understand exactly what you're hoping to
> accomplish so I don't know what to do about the third point:

Basicly I would like to return all the offices and the amount of their
orders even if they are null. Is it possible to add a temporary table to
the procedure and instert the data to it,  and then make a select * from
my_temp_table. Can a function return the result of a temporary table?

Regards
Andreas


>
> CREATE OR REPLACE FUNCTION  sp_order_amount_per_office(p_accountingid IN INTEGER, p_code OUT VARCHAR, p_officetotal
OUTNUMERIC) as $$ 
> BEGIN
> Select office.code, sum(transaction.countervaluecustomer)
> INTO p_code, p_officetotal
>         FROM office
>         LEFT OUTER JOIN receipt
>         ON (receipt.officeid=office.officeid)
>         LEFT OUTER JOIN transaction
>         ON  (transaction.receiptid = Receipt.receiptid) and (receipt.accountingid=p_accountingid)
>         GROUP BY code order by code;
> END ;
> $$ LANGUAGE plpgsql;
>
>             regards, tom lane
>
> ---------------------------(end of broadcast)---------------------------
> TIP 6: explain analyze is your friend
>


Re: Converting stored procedure from mySQL

From
Verena Ruff
Date:
Hi,

Am Freitag, 16. Juni 2006 17:39 schrieb Andreas Hjortsberg:
> Basicly I would like to return all the offices and the amount of their
> orders even if they are null. Is it possible to add a temporary table to
> the procedure and instert the data to it,  and then make a select * from
> my_temp_table. Can a function return the result of a temporary table?
Return to what? To the client application? In this case you should consider
using a view instead of this function. As you say you're migrating from MySQL
and MySQL didn't support views in older versions, maybe you came up with this
workaround.

Regards,
Verena

Re: Converting stored procedure from mySQL

From
Tom Lane
Date:
Verena Ruff <lists@triosolutions.at> writes:
> Am Freitag, 16. Juni 2006 17:39 schrieb Andreas Hjortsberg:
>> Basicly I would like to return all the offices and the amount of their
>> orders even if they are null.

> Return to what? To the client application? In this case you should
> consider using a view instead of this function. As you say you're
> migrating from MySQL and MySQL didn't support views in older versions,
> maybe you came up with this workaround.

A view would work nicely.  If you really want this to be a function,
it needs to return a set of rows not just one row, and ISTM you don't
even need plpgsql: a SQL-language function would be easier.

CREATE OR REPLACE FUNCTION sp_order_amount_per_office(accountingid IN INTEGER,
        code OUT VARCHAR,officetotal OUT NUMERIC)
        RETURNS SETOF RECORD
        as $$
Select office.code, sum(transaction.countervaluecustomer)
        FROM office
        LEFT OUTER JOIN receipt
        ON (receipt.officeid=office.officeid)
        LEFT OUTER JOIN transaction
        ON  (transaction.receiptid = Receipt.receiptid) and
            (receipt.accountingid= $1)
        GROUP BY code order by code;
$$ LANGUAGE sql;

A call would look like
SELECT * FROM sp_order_amount_per_office(42);

            regards, tom lane

Stored Procedure Question

From
"Greg Quinn"
Date:
I am creating a simple FUNCTION that returns a few columns of data from a
table.

Just a question, in order to get this working, is there a general type I can
return, that I can use as a generic type for all my different functions?

Or do I need to create a custom type for each FUNCTION?


Re: Stored Procedure Question

From
Michael Fuhr
Date:
On Sun, Jun 18, 2006 at 06:42:28PM +0200, Greg Quinn wrote:
> I am creating a simple FUNCTION that returns a few columns of data from a
> table.
>
> Just a question, in order to get this working, is there a general type I can
> return, that I can use as a generic type for all my different functions?
>
> Or do I need to create a custom type for each FUNCTION?

In 8.1 you can use OUT or INOUT parameters; in earlier versions you
can create a custom type or use "record".  If you return "record"
then you'll need to provide a column list when you call the function.

--
Michael Fuhr

Re: Stored Procedure Question

From
"Greg Quinn"
Date:
I am using 8.1. Now I am trying to create my function that returns multiple
columns. I have added an OUT parameter, but of what type must it be?

Any type I try to return it tells me that only one column can be returned.

Here is my function...

CREATE OR REPLACE FUNCTION "public"."sp_getcontacts" (out myrecord varchar)
RETURNS varchar AS
$body$
select firstname, lastname from contacts
$body$
LANGUAGE 'sql' VOLATILE CALLED ON NULL INPUT SECURITY INVOKER;


-----Original Message-----
From: Michael Fuhr [mailto:mike@fuhr.org]
Sent: Saturday, June 17, 2006 7:14 PM
To: Greg Quinn
Cc: pgsql-novice@postgresql.org
Subject: Re: [NOVICE] Stored Procedure Question

On Sun, Jun 18, 2006 at 06:42:28PM +0200, Greg Quinn wrote:
> I am creating a simple FUNCTION that returns a few columns of data from a
> table.
>
> Just a question, in order to get this working, is there a general type I
can
> return, that I can use as a generic type for all my different functions?
>
> Or do I need to create a custom type for each FUNCTION?

In 8.1 you can use OUT or INOUT parameters; in earlier versions you
can create a custom type or use "record".  If you return "record"
then you'll need to provide a column list when you call the function.

--
Michael Fuhr



--
No virus found in this incoming message.
Checked by AVG Free Edition.
Version: 7.1.394 / Virus Database: 268.9.0/368 - Release Date: 6/16/2006



Re: Stored Procedure Question

From
Michael Fuhr
Date:
On Sun, Jun 18, 2006 at 07:20:59PM +0200, Greg Quinn wrote:
> I am using 8.1. Now I am trying to create my function that returns multiple
> columns. I have added an OUT parameter, but of what type must it be?

Declare an OUT parameter for each column with that column's type.
If you want to return exactly one row then omit RETURNS; if you
want to return zero or more rows then return SETOF record.

> Any type I try to return it tells me that only one column can be returned.
>
> Here is my function...
>
> CREATE OR REPLACE FUNCTION "public"."sp_getcontacts" (out myrecord varchar)
> RETURNS varchar AS
> $body$
> select firstname, lastname from contacts
> $body$
> LANGUAGE 'sql' VOLATILE CALLED ON NULL INPUT SECURITY INVOKER;

I think you're after something like this:

CREATE OR REPLACE FUNCTION sp_getcontacts(OUT firstname varchar,
                                          OUT lastname varchar)
RETURNS SETOF record AS $$
  SELECT firstname, lastname FROM contacts
$$ LANGUAGE sql STABLE;

You'd call this function like so:

SELECT * FROM sp_getcontacts();

Is that what you're looking for?

--
Michael Fuhr