1

This is a second iteration to a question I previously asked. I'm creating the following function.

CREATE FUNCTION public.getpogstats(IN symbol character varying, IN pogtypeid integer, OUT closehi numeric, OUT closelo numeric, OUT dayhi numeric, OUT daylo numeric, OUT s7dhi numeric, OUT s7dlo numeric, OUT t13hi numeric, OUT t13lo numeric, OUT close numeric, OUT firstdate timestamp without time zone)
    RETURNS record
    LANGUAGE 'sql'

AS $function$
SELECT ROUND(MAX(closeprice), 2), ROUND(MIN(closeprice), 2), ROUND(MAX(dayhigh), 2), ROUND(MIN(daylow), 2), ROUND(MAX(sevendaydp), 2), ROUND(MIN(sevendaydp), 2),
    ROUND(MAX(thirteendaydp), 2), ROUND(MIN(thirteendaydp), 2), MIN(datadate) 
    INTO closehi, closelo, dayhi, daylo, s7dhi, s7dlo, t13hi, t13lo, firstdate
FROM pogdata
JOIN symbol ON pogdata.symbolid = symbol.symbolid AND pogdata.pogtypeid = pogtypeid
WHERE symbol.symbol = symbol;

SELECT ROUND(closeprice, 2) INTO close FROM pogdata
JOIN symbol ON pogdata.symbolid = symbol.symbolid
WHERE datadate = (SELECT MAX(datadate) 
                  FROM pogdata JOIN symbol ON pogdata.symbolid = symbol.symbolid AND pogdata.pogtypeid = pogtypeid 
                  WHERE symbol.symbol = symbol)
AND symbol.symbol = symbol;

$function$;

ALTER FUNCTION public.getpogstats(character varying, integer)
    OWNER TO postgres;

When I execute to create this function, I get the following message:

ERROR:  syntax error at or near ","
LINE 8:     INTO closehi, closelo, dayhi, daylo, s7dhi, s7dlo, t13hi...
                        ^
********** Error **********

ERROR: syntax error at or near ","
SQL state: 42601
Character: 620

I am trying to follow the documentation on PostgreSQL where it says

...where target can be a record variable, a row variable, or a comma-separated list of simple variables and record/row fields.

do the OUT parameters count as "simple variables"?

3
  • 1
    select ... into can only be used with PL/pgSQL, not SQL (also - unrelated - the language name is an identifier it should not be put between single quotes, so it should be language sql or language plpgsql) Commented May 9, 2017 at 6:21
  • Thanks for the feedback. I changed the LANGUAGE to 'plpgsql' but now the error is: Syntax error at or near "SELECT". LINE 6: SELECT ROUND(MAX(closeprice), 2), ROUND(MIN(closeprice), 2),... Commented May 9, 2017 at 6:30
  • 1
    You also need to change the body of the function to comply with the syntax rules of PL/pgSQL: postgresql.org/docs/current/static/plpgsql.html Commented May 9, 2017 at 6:32

3 Answers 3

1

The OUT parameters are variables that you can use in a SELECT ... INTO statement.

You are, however, mixing up SQL functions and PL/pgSQL functions.

The way you declared the function (LANGUAGE 'sql'), the following apply:

  • It can only contain regular SQL statements (for example, no SELECT ... INTO), and the result of the last statement is the result of the functions (see the documentation).

  • Output parameters only provide names for the result columns of the last SELECT statement.

What you need is a PL/pgSQL function (LANGUAGE 'plpgsql').

Then you can use SELECT ... INTO, but you will have to arrange your code into PL/pgSQL blocks:

[DECLARE
   <variable> <type>;
   ...]
BEGIN
   <statement>;
   ...
[EXCEPTION
    WHEN <exception> THEN
       <statement>;
       ...
    ...]
END;
Sign up to request clarification or add additional context in comments.

Comments

1

You can keep that as a SQL function if you want to. SQL functions can return multiple rows by running multiple SELECT queries, but they can only return multiple columns from the same query. So in order to keep that a SQL function, you need to use a single query that returns all the columns. This could be achieved with a common table expression:

CREATE FUNCTION public.getpogstats(IN p_symbol character varying, IN p_pogtypeid integer, OUT closehi numeric, OUT closelo numeric, OUT dayhi numeric, OUT daylo numeric, OUT s7dhi numeric, OUT s7dlo numeric, OUT t13hi numeric, OUT t13lo numeric, OUT close numeric, OUT firstdate timestamp without time zone)
    RETURNS record
    LANGUAGE sql
AS $function$
  with data1 as (
    SELECT ROUND(MAX(closeprice), 2) as closehi, 
           ROUND(MIN(closeprice), 2) as closelo, 
           ROUND(MAX(dayhigh), 2) as dayhi, 
           ROUND(MIN(daylow), 2) as daylo, 
           ROUND(MAX(sevendaydp), 2) as s7dhi, 
           ROUND(MIN(sevendaydp), 2) as s7dlo,
           ROUND(MAX(thirteendaydp), 2) as t13hi, 
           ROUND(MIN(thirteendaydp), 2) as t13lo, 
           MIN(datadate) as firstdate
    FROM pogdata
      JOIN symbol ON pogdata.symbolid = symbol.symbolid AND pogdata.pogtypeid = p_pogtypeid
    WHERE symbol.symbol = p_symbol
  ), data2 as (
    SELECT ROUND(closeprice, 2) as close
    FROM pogdata
      JOIN symbol ON pogdata.symbolid = symbol.symbolid
    WHERE datadate = (SELECT MAX(datadate) 
                      FROM pogdata 
                        JOIN symbol ON pogdata.symbolid = symbol.symbolid AND pogdata.pogtypeid = p_pogtypeid 
                      WHERE symbol.symbol = p_symbol)
    AND symbol.symbol = p_symbol
    LIMIT 1 -- just to be sure
  )
  select d1.closehi, d1.closelo, d1.dayhi, d1.daylo, d1.s7dhi, d1.s7dlo, d1.t13hi, d1.t13lo, d2.close, d1.firstdate
  from data1 as d1
    cross join data2 as d2;

$function$;

(I have the impression the two queries can be combined into one, but right now I can't think of a way to do that)


Note that if you want to have a result with multiple columns (rather then one column with multiple fields), you need to expand those explicitly.

The following select:

select getpogstats('foo', 1);

will return a single row with a single column containing multiple fields, something like:

getpogstats
-----------
(1,2,3,4,5,6,7,8,9,"2017-05-09 18:19:20")

because the function is declared as "RETURNS record".

However, if you want the result as individual columns, you need to use:

select (getpogstats('foo', 1)).*;

Then you'll get each column separately:

closehi | closelo | dayhi | ...
--------+---------+-------+----
      1 |       2 |     3 | ...

Typically, functions returning more then one column are easier to handle if you declare them as returns table (...):

CREATE FUNCTION public.getpogstats(p_symbol character varying, p_pogtypeid integer)
  returns table(closehi numeric, closelo numeric, dayhi numeric, daylo numeric, s7dhi numeric, s7dlo numeric, t13hi numeric, t13lo numeric, close numeric, firstdate timestamp without time zone)
  LANGUAGE sql
AS $function$
  with data1 as (
    SELECT ROUND(MAX(closeprice), 2) as closehi, 
           ROUND(MIN(closeprice), 2) as closelo, 
           ROUND(MAX(dayhigh), 2) as dayhi, 
           ROUND(MIN(daylow), 2) as daylo, 
           ROUND(MAX(sevendaydp), 2) as s7dhi, 
           ROUND(MIN(sevendaydp), 2) as s7dlo,
           ROUND(MAX(thirteendaydp), 2) as t13hi, 
           ROUND(MIN(thirteendaydp), 2) as t13lo, 
           MIN(datadate) as firstdate
    FROM pogdata
      JOIN symbol ON pogdata.symbolid = symbol.symbolid AND pogdata.pogtypeid = p_pogtypeid
    WHERE symbol.symbol = p_symbol
  ), data2 as (
    SELECT ROUND(closeprice, 2) as close
    FROM pogdata
      JOIN symbol ON pogdata.symbolid = symbol.symbolid
    WHERE datadate = (SELECT MAX(datadate) 
                      FROM pogdata 
                         JOIN symbol ON pogdata.symbolid = symbol.symbolid AND pogdata.pogtypeid = p_pogtypeid 
                      WHERE symbol.symbol = p_symbol)
    AND symbol.symbol = p_symbol
    LIMIT 1 -- just to be sure
  )
  select d1.closehi, d1.closelo, d1.dayhi, d1.daylo, d1.s7dhi, d1.s7dlo, d1.t13hi, d1.t13lo, d2.close, d1.firstdate
  from data1 as d1
    cross join data2 as d2;

$function$;

Then you can use:

select *
from getpogstats('foo', 1);

and the result will automatically have a "table like" structure.

Comments

0

I just had to change the language and add the BEGIN and END wrapper to the following and it all worked. Well, it worked to the point where I could execute the procedure. Thanks for the help.

CREATE FUNCTION public.getpogstats(IN symbol character varying, IN pogtypeid integer, OUT closehi numeric, OUT closelo numeric, OUT dayhi numeric, OUT daylo numeric, OUT s7dhi numeric, OUT s7dlo numeric, OUT t13hi numeric, OUT t13lo numeric, OUT close numeric, OUT firstdate timestamp without time zone)
    RETURNS record
    LANGUAGE 'plpgsql'

AS $function$
BEGIN
SELECT ROUND(MAX(closeprice), 2), ROUND(MIN(closeprice), 2), ROUND(MAX(dayhigh), 2), ROUND(MIN(daylow), 2), ROUND(MAX(sevendaydp), 2), ROUND(MIN(sevendaydp), 2),
    ROUND(MAX(thirteendaydp), 2), ROUND(MIN(thirteendaydp), 2), MIN(datadate) 
    INTO closehi, closelo, dayhi, daylo, s7dhi, s7dlo, t13hi, t13lo, firstdate
FROM pogdata
JOIN symbol ON pogdata.symbolid = symbol.symbolid AND pogdata.pogtypeid = pogtypeid
WHERE symbol.symbol = symbol;

SELECT ROUND(closeprice, 2) INTO close FROM pogdata
JOIN symbol ON pogdata.symbolid = symbol.symbolid
WHERE datadate = (SELECT MAX(datadate) 
                  FROM pogdata JOIN symbol ON pogdata.symbolid = symbol.symbolid AND pogdata.pogtypeid = pogtypeid 
                  WHERE symbol.symbol = symbol)
AND symbol.symbol = symbol;
END;
$function$;

ALTER FUNCTION public.getpogstats(character varying, integer)
    OWNER TO postgres;

1 Comment

Another thing: it is highly recommended to name parameters differently then columns in your queries to avoid any nameclashes. Many people simply prefix parameter names with p_ for that.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.