205

In MS SQL Server, I create my scripts to use customizable variables:

DECLARE @somevariable int 
SELECT @somevariable = -1
INSERT INTO foo VALUES ( @somevariable )

I'll then change the value of @somevariable at runtime, depending on the value that I want in the particular situation. Since it's at the top of the script it's easy to see and remember.

How do I do the same with the PostgreSQL client psql?

asked Aug 31, 2008 at 16:54
2
  • 8
    FWIW, the \set operator appears to be related to the psql command-line tool, not to the pgsql batch language. I could be wrong. Commented Mar 10, 2011 at 21:52
  • 1
    What version of Postgres are you on? Commented Nov 11, 2012 at 23:30

14 Answers 14

250

Postgres variables are created through the \set command, for example ...

\set myvariable value

... and can then be substituted, for example, as ...

SELECT * FROM :myvariable.table1;

... or ...

SELECT * FROM table1 WHERE :myvariable IS NULL;

edit: As of psql 9.1, variables can be expanded in quotes as in:

\set myvariable value 
SELECT * FROM table1 WHERE column1 = :'myvariable';

In older versions of the psql client:

... If you want to use the variable as the value in a conditional string query, such as ...

SELECT * FROM table1 WHERE column1 = ':myvariable';

... then you need to include the quotes in the variable itself as the above will not work. Instead define your variable as such ...

\set myvariable 'value'

However, if, like me, you ran into a situation in which you wanted to make a string from an existing variable, I found the trick to be this ...

\set quoted_myvariable '\'' :myvariable '\''

Now you have both a quoted and unquoted variable of the same string! And you can do something like this ....

INSERT INTO :myvariable.table1 SELECT * FROM table2 WHERE column1 = :quoted_myvariable;
Jeremy
1,2081 gold badge9 silver badges16 bronze badges
answered Aug 27, 2010 at 23:40
Sign up to request clarification or add additional context in comments.

14 Comments

\set is only for psql tool, you cannot use it in stored procedures!
@SorinSbarnea the OP asked about script, not procedure
This answer mixes psql meta-commands \set with PostgreSQL commands in a confusing fashion.
As of postgresql 9.1, in psql you can now use :'variable' to have it properly quoted as a value for you, or :"variable" to use it as an identifier.
@laurent: Or use :'myvar' like @HitScan suggests
|
77

You can try to use a WITH clause.

WITH vars AS (SELECT 42 AS answer, 3.14 AS appr_pi)
SELECT t.*, vars.answer, t.radius*vars.appr_pi
FROM table AS t, vars;
answered Mar 8, 2013 at 14:26

7 Comments

This way is mostly convenient when you are using same computed values few times in your query.
Contrary to Bryce's report, it seems to work fine for me. CREATE TABLE test (name VARCHAR, age INT); INSERT INTO test (name, age) VALUES ('Jack', 21), ('Jill', 20); WITH vars AS (SELECT N'Jack' AS name, 21 AS age) SELECT test.* FROM test, vars WHERE test.name = vars.name and test.age = vars.age; Ouputs Jack and Jack's age, as expected.
For a lot of uses, especially within the context of a web application framework like Python Flask, this is the best solution for reusing complex calculated values within a single query.
Can anyone suggest how this might work in an insert?
@Stoopkid create table t(x integer); insert into t(x) with sub as (select 999 as num) select num from sub; select * from t;
|
77

One final word on PSQL variables:

  1. They don't expand if you enclose them in single quotes in the SQL statement. Thus this doesn't work:

    SELECT * FROM foo WHERE bar = ':myvariable'
    
  2. To expand to a string literal in a SQL statement, you have to include the quotes in the variable set. However, the variable value already has to be enclosed in quotes, which means that you need a second set of quotes, and the inner set has to be escaped. Thus you need:

    \set myvariable '\'somestring\'' 
    SELECT * FROM foo WHERE bar = :myvariable
    

    EDIT: starting with PostgreSQL 9.1, you may write instead:

    \set myvariable somestring
    SELECT * FROM foo WHERE bar = :'myvariable'
    
Daniel Vérité
62.2k16 gold badges133 silver badges159 bronze badges
answered Aug 31, 2008 at 17:36

Comments

51

Specifically for psql, you can pass psql variables from the command line too; you can pass them with -v. Here's a usage example:

$ psql -v filepath=/path/to/my/directory/mydatafile.data regress
regress=> SELECT :'filepath';
 ?column? 
---------------------------------------
 /path/to/my/directory/mydatafile.data
(1 row)

Note that the colon is unquoted, then the variable name its self is quoted. Odd syntax, I know. This only works in psql but doesn't work with -c / --command; you have to send the command via stdin or via -f. It won't work in (say) PgAdmin-III too.

This substitution happens during input processing in psql, so you can't (say) define a function that uses :'filepath' and expect the value of :'filepath' to change from session to session. It'll be substituted once, when the function is defined, and then will be a constant after that. It's useful for scripting but not runtime use.

kirikaza
2,7492 gold badges15 silver badges14 bronze badges
answered Nov 10, 2012 at 3:12

6 Comments

psql variables, e.g. :'filepath', you pointed out: "Note that the colon is unquoted, then the variable name its self is quoted." Thank! You! I already put a bunch of forehead-shaped dents into my desk trying to make this work, and you just saved me a ton more. Exactly what I needed for some scripting.
This doesn't seem to work with --command="SELECT :'filepath';", which is unfortunate. How can this be useful if it seems to require interactive terminal sessions?
However echo "\set filepath /path/to/my/directory/mydatafile.data \\\\\\\\ SELECT :'filepath';" | psql does work. Note the excessive number of escaping slashes to achieve two sequential \ characters, as per the advice under the command arg here, with a Bash shell anyway.
@alphabetasoup Yep, you can use psql -f - etc. Like many things, psql isn't exactly pretty, but it gets the job done. It's easier to use -v than to compose a \set command in stdin though.
@shadowtalker Very likely, there are all sorts of weird limitations in it. For exmple psql's conditionals cannot be used to guard the COPY data stream because the parser for \endif is confused by the \. to end the copy-stream...
|
15

FWIW, the real problem was that I had included a semicolon at the end of my \set command:

\set owner_password 'thepassword';

The semicolon was interpreted as an actual character in the variable:

\echo :owner_password thepassword;

So when I tried to use it:

CREATE ROLE myrole LOGIN UNENCRYPTED PASSWORD :owner_password NOINHERIT CREATEDB CREATEROLE VALID UNTIL 'infinity';

...I got this:

CREATE ROLE myrole LOGIN UNENCRYPTED PASSWORD thepassword; NOINHERIT CREATEDB CREATEROLE VALID UNTIL 'infinity';

That not only failed to set the quotes around the literal, but split the command into 2 parts (the second of which was invalid as it started with "NOINHERIT").

The moral of this story: PostgreSQL "variables" are really macros used in text expansion, not true values. I'm sure that comes in handy, but it's tricky at first.

answered Aug 31, 2008 at 17:14

1 Comment

They can be used as macros for full text expansion, but for something like a decade or more now you can use :'varname' to use the variable value as an SQL literal or :"varname" to use the variable value as an SQL identifier. (Probably wasn't around in 2008 when you wrote this answer.)
13

postgres (since version 9.0) allows anonymous blocks in any of the supported server-side scripting languages

DO '
DECLARE somevariable int = -1;
BEGIN
INSERT INTO foo VALUES ( somevariable );
END
' ;

http://www.postgresql.org/docs/current/static/sql-do.html

As everything is inside a string, external string variables being substituted in will need to be escaped and quoted twice. Using dollar quoting instead will not give full protection against SQL injection.

answered May 10, 2016 at 23:02

Comments

11

You need to use one of the procedural languages such as PL/pgSQL not the SQL proc language. In PL/pgSQL you can use vars right in SQL statements. For single quotes you can use the quote literal function.

answered Sep 22, 2008 at 5:57

2 Comments

It cannot be done in postgres itself, but it can be done in the PSQL client application.
plpgsql can (now) be used in postgres (since version 9.0) )postgresql.org/docs/9.0/static/sql-do.html
10

I solved it with a temp table.

CREATE TEMP TABLE temp_session_variables (
 "sessionSalt" TEXT
);
INSERT INTO temp_session_variables ("sessionSalt") VALUES (current_timestamp || RANDOM()::TEXT);

This way, I had a "variable" I could use over multiple queries, that is unique for the session. I needed it to generate unique "usernames" while still not having collisions if importing users with the same user name.

answered Oct 27, 2014 at 13:07

1 Comment

This seem to be the only working way in visual tools like Heidi SQL.
5

Another approach is to (ab)use the PostgreSQL GUC mechanism to create variables. See this prior answer for details and examples.

You declare the GUC in postgresql.conf, then change its value at runtime with SET commands and get its value with current_setting(...).

I don't recommend this for general use, but it could be useful in narrow cases like the one mentioned in the linked question, where the poster wanted a way to provide the application-level username to triggers and functions.

answered Nov 10, 2012 at 3:13

Comments

5

I really miss that feature. Only way to achieve something similar is to use functions.

I have used it in two ways:

  • perl functions that use $_SHARED variable
  • store your variables in table

Perl version:

 CREATE FUNCTION var(name text, val text) RETURNS void AS $$
 $_SHARED{$_[0]} = $_[1];
 $$ LANGUAGE plperl;
 CREATE FUNCTION var(name text) RETURNS text AS $$
 return $_SHARED{$_[0]};
 $$ LANGUAGE plperl;

Table version:

CREATE TABLE var (
 sess bigint NOT NULL,
 key varchar NOT NULL,
 val varchar,
 CONSTRAINT var_pkey PRIMARY KEY (sess, key)
);
CREATE FUNCTION var(key varchar, val anyelement) RETURNS void AS $$
 DELETE FROM var WHERE sess = pg_backend_pid() AND key = 1ドル;
 INSERT INTO var (sess, key, val) VALUES (sessid(), 1,ドル 2ドル::varchar);
$$ LANGUAGE 'sql';
CREATE FUNCTION var(varname varchar) RETURNS varchar AS $$
 SELECT val FROM var WHERE sess = pg_backend_pid() AND key = 1ドル;
$$ LANGUAGE 'sql';

Notes:

  • plperlu is faster than perl
  • pg_backend_pid is not best session identification, consider using pid combined with backend_start from pg_stat_activity
  • this table version is also bad because you have to clear this is up occasionally (and not delete currently working session variables)
answered Dec 19, 2012 at 21:54

Comments

3

Variables in psql suck. If you want to declare an integer, you have to enter the integer, then do a carriage return, then end the statement in a semicolon. Observe:

Let's say I want to declare an integer variable my_var and insert it into a table test:

Example table test:

thedatabase=# \d test;
 Table "public.test"
 Column | Type | Modifiers 
--------+---------+---------------------------------------------------
 id | integer | not null default nextval('test_id_seq'::regclass)
Indexes:
 "test_pkey" PRIMARY KEY, btree (id)

Clearly, nothing in this table yet:

thedatabase=# select * from test;
 id 
----
(0 rows)

We declare a variable. Notice how the semicolon is on the next line!

thedatabase=# \set my_var 999
thedatabase=# ;

Now we can insert. We have to use this weird ":''" looking syntax:

thedatabase=# insert into test(id) values (:'my_var');
INSERT 0 1

It worked!

thedatabase=# select * from test;
 id 
-----
 999
(1 row)

Explanation:

So... what happens if we don't have the semicolon on the next line? The variable? Have a look:

We declare my_var without the new line.

thedatabase=# \set my_var 999;

Let's select my_var.

thedatabase=# select :'my_var';
 ?column? 
----------
 999;
(1 row)

WTF is that? It's not an integer, it's a string 999;!

thedatabase=# select 999;
 ?column? 
----------
 999
(1 row)
answered Nov 10, 2018 at 2:27

2 Comments

The reason the semicolon does unexpected things for you is that a semicolon terminates a SQL statement, but you're typing a psql command, \set, which isn't SQL and does NOT take a terminating semicolon. Putting a semicolon on the next line won't hurt, but does absolutely nothing. It's an empty statement.
Also, you shouldn't use the :'my_var' syntax for an integer value. :my_var works just fine.
2

I've found this question and the answers extremely useful, but also confusing. I had lots of trouble getting quoted variables to work, so here is the way I got it working:

\set deployment_user username -- username
\set deployment_pass '\'string_password\''
ALTER USER :deployment_user WITH PASSWORD :deployment_pass;

This way you can define the variable in one statement. When you use it, single quotes will be embedded into the variable.

NOTE! When I put a comment after the quoted variable it got sucked in as part of the variable when I tried some of the methods in other answers. That was really screwing me up for a while. With this method comments appear to be treated as you'd expect.

answered Mar 28, 2012 at 15:23

2 Comments

\set deployment_pass 'string_password' ALTER USER :deployment_user WITH PASSWORD :'deployment_pass';
\set is not SQL it's a psql built-in command sql comments are not supported.
2

I've posted a new solution for this on another thread.

It uses a table to store variables, and can be updated at any time. A static immutable getter function is dynamically created (by another function), triggered by update to your table. You get nice table storage, plus the blazing fast speeds of an immutable getter.

answered Jan 23, 2019 at 8:30

Comments

0

My use case was needing to delete all records from multiple tables where the datetime in a created column was greater than a certain datetime. For this I didn't want to keep repeating the same datetime in each DELETE FROM... statement.

The top answer didn't work in a SQL script. I got the following error:

SQL Error [42601]: ERROR: syntax error at or near "\"

The solution using WITH also didn't work for me as this only enabled me to use the constant within a single query.

The other solutions were a bit complicated for me to follow!

In the end this worked for me:

DO $$
DECLARE
 created_threshold TIMESTAMP := '2025-01-01 00:00:00';
BEGIN
 EXECUTE 'DELETE FROM table1 WHERE created > 1ドル' USING created_threshold;
 EXECUTE 'DELETE FROM table2 WHERE created > 1ドル' USING created_threshold;
 EXECUTE 'DELETE FROM table3 WHERE created > 1ドル' USING created_threshold;
END $$;
answered Feb 19 at 13:02

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

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.