171

For example, in MS-SQL, you can open up a query window and run the following:

DECLARE @List AS VARCHAR(8)

SELECT @List = 'foobar'

SELECT *
FROM   dbo.PubLists
WHERE  Name = @List

How is this done in PostgreSQL? Can it be done?

  • 1
    Related questions: http://stackoverflow.com/q/1490942/330315 and http://stackoverflow.com/q/13316773/330315 –  Apr 19 '15 at 09:36

11 Answers11

182

Complete answer is located in the official PostgreSQL documentation.

You can use new PG9.0 anonymous code block feature (http://www.postgresql.org/docs/9.1/static/sql-do.html )

DO $$
DECLARE v_List TEXT;
BEGIN
  v_List := 'foobar' ;
  SELECT *
  FROM   dbo.PubLists
  WHERE  Name = v_List;
  -- ...
END $$;

Also you can get the last insert id:

DO $$
DECLARE lastid bigint;
BEGIN
  INSERT INTO test (name) VALUES ('Test Name') 
  RETURNING id INTO lastid;

  SELECT * FROM test WHERE id = lastid;
END $$;
nad2000
  • 4,526
  • 1
  • 30
  • 25
  • 8
    (And don't forget the `;` after `END $$`, like so: `END $$;`.) – KajMagnus Oct 08 '12 at 08:26
  • 3
    DOESN'T WORK FOR ME ERROR NEAR DO, I have also some functions between the begin and end with plpgsql language. – Ash Oct 31 '16 at 00:12
  • 98
    the code in this example does not work. `ERROR: query has no destination for result data HINT: If you want to discard the results of a SELECT, use PERFORM instead. CONTEXT: PL/pgSQL function inline_code_block line 7 at SQL statement ` – Jasen Dec 31 '16 at 01:34
  • 3
    Being completely new to PostgreSQL this threw me for a while, here's some more tips: + Make sure you end your statements with a semicolon! + Because there is no variable identifier you may want to use an _ or something similar to avoid ambiguous column names. + You can set the variable to a value in line using like this DECLARE _accountid INT := 1; – The Coder Jul 26 '17 at 02:49
  • 2
    don't work for me. Using squirrel. Error: ERROR: unterminated dollar-quoted string at or near "$$ – Oliver Watkins Apr 05 '18 at 09:04
  • that only means that squirrel is broken, perhaops contact the vendor for a fix. – Jasen Jun 09 '20 at 00:03
73
DO $$
DECLARE  
   a integer := 10;  
   b integer := 20;  
   c integer;  
BEGIN  
   c := a + b;
    RAISE NOTICE'Value of c: %', c;
END $$;
Achilles Ram Nakirekanti
  • 3,623
  • 1
  • 21
  • 13
  • 5
    don't work for me. Using squirrel. Error: ERROR: unterminated dollar-quoted string at or near "$$ – Oliver Watkins Apr 05 '18 at 09:04
  • 1
    Took me a while to figure out that in order to use the variable you *must not* prefix it with a `:` as with other variables. @achilles-ram-nakirekanti you could add an example using this in a `select` statement to make this clearer? – exhuma Dec 03 '19 at 09:23
  • 1
    Can you elaborate on what this does? This worked great for me. I understand that $$ is just a replacement for quotes to execute a literal, but why do we need the `DO $$` block at all? – VSO Sep 30 '21 at 15:36
  • 2
    @VSO Unlike SQL Server and MySQL, PostgreSQL doesn’t support variables outside a code block. Typically that would mean a function block. The `DO` block creates an anonymous block to create enough of a programming environment for the above. See https://www.postgresql.org/docs/current/sql-do.html – Manngo Mar 13 '22 at 05:43
37

You can use:

\set list '''foobar'''
SELECT * FROM dbo.PubLists WHERE name = :list;

That will do

Karim de Alba
  • 427
  • 4
  • 2
  • 5
    ERROR: syntax error at or near "\" What am I missing? – scw Dec 07 '15 at 17:14
  • 25
    @scw This is only available from the `psql` console. You won't be able to write this in your app's SQL. – owensmartin Jan 07 '16 at 23:13
  • @owensmartin You'll be able to use this is anything that is piped through to psql.. or any script that psql reads... – Evan Carroll Nov 01 '16 at 17:59
  • 6
    This doesn't answer the question at all. In MS SQL you can define a var in a query and use it right there, in the same tool. I don't get why people keep proposing this as an answer, in every version of this question. – stone Nov 22 '16 at 23:01
  • 2
    @stone apparently because this is a huge "miss" in `postgresql` and it's the least-worst alternative. generally i've been quite pleased with `postgresql` : but this is a surprisingly big fail – WestCoastProjects Mar 26 '20 at 20:10
15

Here's an example of using a variable in plpgsql:

create table test (id int);
insert into test values (1);
insert into test values (2);
insert into test values (3);

create function test_fn() returns int as $$
    declare val int := 2;
    begin
        return (SELECT id FROM test WHERE id = val);
    end;
$$ LANGUAGE plpgsql;

SELECT * FROM test_fn();
 test_fn 
---------
       2

Have a look at the plpgsql docs for more information.

overthink
  • 23,985
  • 4
  • 69
  • 69
7

I've came across some other documents which they use \set to declare scripting variable but the value is seems to be like constant value and I'm finding for way that can be acts like a variable not a constant variable.

Ex:

\set Comm 150

select sal, sal+:Comm from emp

Here sal is the value that is present in the table 'emp' and comm is the constant value.

max taldykin
  • 12,459
  • 5
  • 45
  • 64
Vinodraj
  • 95
  • 1
  • 1
5

Building on @nad2000's answer and @Pavel's answer here, this is where I ended up for my Flyway migration scripts. Handling for scenarios where the database schema was manually modified.

DO $$
BEGIN
    IF NOT EXISTS(
        SELECT TRUE FROM pg_attribute 
        WHERE attrelid = (
            SELECT c.oid
            FROM pg_class c
            JOIN pg_namespace n ON n.oid = c.relnamespace
            WHERE 
                n.nspname = CURRENT_SCHEMA() 
                AND c.relname = 'device_ip_lookups'
            )
        AND attname = 'active_date'
        AND NOT attisdropped
        AND attnum > 0
        )
    THEN
        RAISE NOTICE 'ADDING COLUMN';        
        ALTER TABLE device_ip_lookups
            ADD COLUMN active_date TIMESTAMP;
    ELSE
        RAISE NOTICE 'SKIPPING, COLUMN ALREADY EXISTS';
    END IF;
END $$;
Shanerk
  • 5,175
  • 2
  • 40
  • 36
5

For use variables in for example alter table:

DO $$ 
DECLARE name_pk VARCHAR(200);
BEGIN
select constraint_name
from information_schema.table_constraints
where table_schema = 'schema_name'
      and table_name = 'table_name'
      and constraint_type = 'PRIMARY KEY' INTO name_pk;
IF (name_pk := '') THEN
EXECUTE 'ALTER TABLE schema_name.table_name DROP CONSTRAINT ' || name_pk;
davidleongz
  • 155
  • 2
  • 11
4

Postgresql does not have bare variables, you could use a temporary table. variables are only available in code blocks or as a user-interface feature.

If you need a bare variable you could use a temporary table:

CREATE TEMP TABLE list AS VALUES ('foobar');

SELECT dbo.PubLists.*
FROM   dbo.PubLists,list
WHERE  Name = list.column1;
Jasen
  • 11,837
  • 2
  • 30
  • 48
  • As a side-benefit, this approach is database agnostic, making your tests more portable across backend. – bishop Oct 25 '19 at 18:19
  • Note that this can't be used in read-only transactions (I just tried using it in Redash). – Henrik N Jun 10 '22 at 08:54
3

I had to do something like this

CREATE OR REPLACE FUNCTION MYFUNC()
RETURNS VOID AS $$
DO
$do$
BEGIN
DECLARE
 myvar int;
 ...
END
$do$
$$ LANGUAGE SQL;
Nick
  • 3,217
  • 5
  • 30
  • 42
2

You can also simply make a constant query that you use in the actual query:

WITH vars as (SELECT 'foobar' AS list) 
SELECT *
FROM   dbo.PubLists, vars
WHERE  Name = vars.list
DPF
  • 270
  • 1
  • 2
  • 12
  • Obviously this is a lot less appealing than some of the other answers using code blocks, because you can only use the "variable" in the very next statement after the CTE, and because you have to reference the "variable" in your FROM clause. However, creating an anonymous code block has its own set of issues (e.g. you'll get "query has no destination for result data" if you try to do a plain SELECT), so this may be the best we can do in some cases. – Robert N Dec 21 '22 at 00:42
  • 1
    As written, generates an error "type 'list' does not exist". I think you have the string literal and the column alias reversed; should be `WITH vars as (SELECT 'foobar' AS list)` (the AS being optional) – Robert N Dec 21 '22 at 00:49
  • 1
    You're absolutely right. I have corrected it. – DPF Jan 03 '23 at 13:01
2

Given the popularity, and somewhat incomplete answers I'll provide two solutions.

  1. A do block that won't return rows. You can return rows with a transaction cursor, but it's a bit messy.
  2. A function (that returns rows)

Below I'll use an over-baked example 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

A simple do block

do $$
declare
    src_pub_id text;
    dst_pub_id text;
    src_id    int; 
    dest_id   int;
    src_tweet text;
begin
    src_pub_id := 'abc';
    dst_pub_id := 'def';
    
    -- 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;
end $$ language plpgsql; -- need the language to avoid ERROR 42P13

A function

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');

-- Optional drop if you don't want the db to keep your function
drop function if exists sync_tweets(text, text);

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