201

How to assign the result of a query to a variable in PL/pgSQL, the procedural language of PostgreSQL?

I have a function:

CREATE OR REPLACE FUNCTION test(x numeric)
RETURNS character varying AS
$BODY$
DECLARE
name   character varying(255);
begin
 name ='SELECT name FROM test_table where id='||x;

 if(name='test')then
  --do somthing
 else
  --do the else part
 end if;
end;
return -- return my process result here
$BODY$
LANGUAGE plpgsql VOLATILE

In the above function I need to store the result of this query:

'SELECT name FROM test_table where id='||x;

to the variable name.

How to process this?

Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228
Sathish
  • 4,403
  • 7
  • 31
  • 53

7 Answers7

286

I think you're looking for SELECT select_expressions INTO:

select test_table.name into name from test_table where id = x;

That will pull the name from test_table where id is your function's argument and leave it in the name variable. Don't leave out the table name prefix on test_table.name or you'll get complaints about an ambiguous reference.

mu is too short
  • 426,620
  • 70
  • 833
  • 800
  • 5
    What if I need multiple variables. Like select test_table.name, test_table.id, test_table.ssn? – Dao Lam Mar 05 '15 at 20:00
  • 3
    @DaoLam: From the documentation I liked to: "The result of a SQL command yielding a single row (possibly of multiple columns) can be assigned to a record variable, row-type variable, or list of scalar variables." – mu is too short Mar 05 '15 at 20:35
  • @muistooshort so you're saying i can do the same and i can use name.id, name.ssn to retrieve? I tried it with IF EXISTS but didn't work: IF EXISTS (select * into name from test_table...)) – Dao Lam Mar 05 '15 at 20:55
  • @DaoLam Why are you combining INTO with IF EXISTS? Maybe you should ask a new question so that you can explain what you're trying to do. – mu is too short Mar 05 '15 at 21:19
  • @muistooshort Thank you! My question is here: http://stackoverflow.com/questions/28886830/how-to-store-values-in-trigger-postgresql – Dao Lam Mar 06 '15 at 02:40
  • @muistooshort this way is not working when the query is returning an array. Is there an alternative to this in case of arrays? Here is my question on the same : http://stackoverflow.com/questions/34194818/array-not-being-returned-by-select-query-inside-plpgsql-test-case – Mitaksh Gupta Dec 10 '15 at 07:36
  • Note the ability to add `TEMPORARY` before the table name to create a temporary table. (Else, one may want to use a `DROP TABLE IF EXISTS test_table;` and keep an eye out for unnecessary tables.) – Fabien Snauwaert Mar 09 '19 at 10:04
  • 19
    There is no example in documentation (or I missed it), but as @muistooshort noted, you can select into multiple variables with single select: `SELECT test_table.column1, test_table.column2 INTO variable1, variable2 FROM test_table WHERE id = x;` – Grengas Apr 12 '19 at 07:40
  • Just FYI I just found out that in my old version of Postgres that NONE of these solutions work if followed by a Dynamic Query. If your main query is dynamic, you have to make the first query dynamic also using EXECUTE .... INTO variable. This took me a looong time to figure out. – blissweb Jun 13 '20 at 15:14
  • 3
    The Postgres docs refer to this syntax as `SELECT select_expressions INTO` (which does not create a table), not `SELECT INTO` (which does). [More info](https://stackoverflow.com/a/67107979/145173) – Edward Brey Apr 15 '21 at 11:57
141

To assign a single variable, you can also use plain assignment in a PL/pgSQL code block, with a scalar subquery to the right:

name := (SELECT t.name from test_table t where t.id = x);

Effectively the same as SELECT INTO like @mu already provided, with subtle differences:

  • SELECT INTO is slightly faster in my tests on Postgres 14.
    (Plain assignment of a constant, without involving SELECT, is 10x faster, still.)
  • SELECT INTO also sets the special variable FOUND, while plain assignment does not. You may want one or the other.
  • SELECT INTO can also assign multiple variables at once. See:

Notably, this works, too:

name := t.name from test_table t where t.id = x;

A SELECT statement without leading SELECT. But I would not use this hybrid. Better use one of the first two, clearer, documented methods, as @Pavel commented.

Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228
23

The usual pattern is EXISTS(subselect):

BEGIN
  IF EXISTS(SELECT name
              FROM test_table t
             WHERE t.id = x
               AND t.name = 'test')
  THEN
     ---
  ELSE
     ---
  END IF;

This pattern is used in PL/SQL, PL/pgSQL, SQL/PSM, ...

Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228
Pavel Stehule
  • 42,331
  • 5
  • 91
  • 94
3

Create Learning Table:

CREATE TABLE "public"."learning" (
    "api_id" int4 DEFAULT nextval('share_api_api_id_seq'::regclass) NOT NULL,
    "title" varchar(255) COLLATE "default"
);

Insert Data Learning Table:

INSERT INTO "public"."learning" VALUES ('1', 'Google AI-01');
INSERT INTO "public"."learning" VALUES ('2', 'Google AI-02');
INSERT INTO "public"."learning" VALUES ('3', 'Google AI-01');

Step: 01

CREATE OR REPLACE FUNCTION get_all (pattern VARCHAR) RETURNS TABLE (
        learn_id INT,
        learn_title VARCHAR
) AS $$
BEGIN
    RETURN QUERY SELECT
        api_id,
        title
    FROM
        learning
    WHERE
        title = pattern ;
END ; $$ LANGUAGE 'plpgsql';

Step: 02

SELECT * FROM get_all('Google AI-01');

Step: 03

DROP FUNCTION get_all();

Demo: enter image description here

Ram Pukar
  • 1,583
  • 15
  • 17
2

Per Executing a Query with a Single-Row Result, use this syntax:

SELECT select_expressions INTO [STRICT] target FROM ...

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

Unlike the SELECT INTO, SELECT select_expressions INTO does not create a table.

In your example, you have a single simple variable name, so the select statement would be:

SELECT test_table.name INTO name FROM test_table WHERE test_table.id = x;
Edward Brey
  • 40,302
  • 20
  • 199
  • 253
1

Many answers here omit important parts of using functions, and given the popularity I think many arrive here looking for a quick overall primer on using functions.

Here's an example of using functions in postgres (including declaration, variables, args, return values, and running). Below is an over-baked way of updating the tweet on the bottom right "blurb" with "hello world".

id (serial) pub_id (text) tweet (text)
1 abc hello world
2 def blurb
-- Optional drop if replace fails below.
drop function if exists sync_tweets(text, text);

create or replace function sync_tweets(
    src_pub_id text, -- function arguments
    dst_pub_id text
) returns setof tweets as -- i.e. rows. int, text work too
$$
declare
    src_id    int; -- temp function variables (not args)
    dest_id   int;
    src_tweet text;
begin
    -- query result into a temp variable
    src_id := (select id from tweets where pub_id = src_pub_id);

    -- query result into a temp variable (another way)
    select tweet into src_tweet from tweets where id = src_id;

    dest_id := (select id from tweets where pub_id = dst_pub_id);
    update tweets set tweet=src_tweet where id = dest_id;

    return query -- i.e. rows, return 0 with return int above works too
        select * from tweets where pub_id in (src_pub_id, dst_pub_id);
end
$$ language plpgsql; -- need the language to avoid ERROR 42P13

-- Run it!
select * from sync_tweets('abc', 'def');

/*
  Outputs
   __________________________________________________ 
  |  id (serial)  |  pub_id (text)  |  tweet (text)  |
  |---------------|-----------------|----------------|
  |  1            |  abc            |  hello world   |
  |  2            |  def            |  blurb         |
  --------------------------------------------------
*/
Josh Hibschman
  • 3,148
  • 1
  • 25
  • 27
-2

You can use the following example to store a query result in a variable using PL/pgSQL:

 select * into demo from maintenanceactivitytrack ; 
    raise notice'p_maintenanceid:%',demo;
Grant Miller
  • 27,532
  • 16
  • 147
  • 165
Rinku Choudhary
  • 1,529
  • 1
  • 13
  • 22