Cómo declarar una variable en una consulta PostgreSQL

242

¿Cómo declaro una variable para usar en una consulta PostgreSQL 8.3?

En MS SQL Server puedo hacer esto:

DECLARE @myvar INT
SET @myvar = 5

SELECT *
FROM somewhere
WHERE something = @myvar

¿Cómo hago lo mismo en PostgreSQL? De acuerdo con la documentación, las variables se declaran simplemente como "tipo de nombre", pero esto me da un error de sintaxis:

myvar INTEGER;

¿Podría alguien darme un ejemplo de la sintaxis correcta?

EMP
fuente
2
Se puede hacer solo en PostgreSQL. Vea la respuesta a esta pregunta relacionada: stackoverflow.com/questions/766657/…
Sean the Bean
2
Esta respuesta relacionada tiene mejores respuestas: stackoverflow.com/questions/13316773/…
Erwin Brandstetter

Respuestas:

113

No existe tal característica en PostgreSQL. Puede hacerlo solo en pl / PgSQL (u otro pl / *), pero no en SQL simple.

Una excepción es la WITH ()consulta que puede funcionar como una variable, o incluso tuplede variables. Le permite devolver una tabla de valores temporales.

WITH master_user AS (
    SELECT
      login,
      registration_date
    FROM users
    WHERE ...
)

SELECT *
FROM users
WHERE master_login = (SELECT login
                      FROM master_user)
      AND (SELECT registration_date
           FROM master_user) > ...;
J.Wincewicz
fuente
Intenté usar este método de CTE como variables. Pero rápidamente me encontré con un problema en el que no se garantiza que las diferentes consultas de modificación de datos en CTE vean los efectos del otro. Tuve que usar varios CTE, ya que necesitaba usar esa variable en múltiples consultas.
Zia Ul Rehman Mughal
228

Logré el mismo objetivo usando una WITHcláusula , no es tan elegante pero puede hacer lo mismo. Aunque para este ejemplo es realmente exagerado. Tampoco lo recomiendo particularmente.

WITH myconstants (var1, var2) as (
   values (5, 'foo')
)
SELECT *
FROM somewhere, myconstants
WHERE something = var1
   OR something_else = var2;
fei0x
fuente
2
Esto funciona muy bien para la mayoría de los casos en los que desea variables. Sin embargo, si desea usar una variable para LIMIT (que no puede contener variables), entonces querría usar \setcomo se sugiere en la respuesta de Shahriar Aghajani.
cimmanon
1
Esto es ideal para cuando tengo un script de migración donde quiero importar algunos datos relacionales. Obviamente no sabré la secuencia de identificación que se da a los datos relacionales.
Relequestual
3
Acabo de probar este enfoque, y encontré una forma quizás mejor: JOIN myconstants ON truey luego no hay necesidad de hacer la sub-selección.
vektor
77
Esto solo funciona dentro de una sola consulta, no puede compartir un WITHCTE entre consultas en una transacción.
Daenyth
2
Vieja pregunta, pero aquí he aquí una variación: WITH constants AS (SELECT 5 AS var) SELECT * FROM somewhere CROSS JOIN constants WHERE someting=var;. CROSS JOIN, al ser una expresión de tabla de una sola fila, prácticamente duplica los datos de todas las filas de la tabla real y simplifica la expresión.
Manngo
83

También puedes probar esto en PLPGSQL:

DO $$
DECLARE myvar integer;
BEGIN
    SELECT 5 INTO myvar;

    DROP TABLE IF EXISTS tmp_table;
    CREATE TABLE tmp_table AS
    SELECT * FROM yourtable WHERE   id = myvar;
END $$;

SELECT * FROM tmp_table;

Lo anterior requiere Postgres 9.0 o posterior.

Dario Barrionuevo
fuente
1
La declaración DO se agregó en PostgreSQL 9.0 y no funciona en 8.3.
Johny
14
Use CREAR TABLA TEMPORAL o CREAR TABLA DE TEMP, no CREAR TABLA. Pero por lo demás bien.
Stefan Steiger
60

Configuración de configuración dinámica

puede "abusar" de la configuración dinámica de configuración para esto:

-- choose some prefix that is unlikely to be used by postgres
set session my.vars.id = '1';

select *
from person 
where id = current_setting('my.vars.id')::int;

Los ajustes de configuración siempre son valores varchar, por lo que debe convertirlos al tipo de datos correcto cuando los use. Esto funciona con cualquier cliente SQL, mientras que \setsolo funciona enpsql

Lo anterior requiere Postgres 9.2 o posterior.

Para versiones anteriores, la variable tenía que declararse postgresql.confantes de ser utilizada, por lo que limitaba un poco su usabilidad. En realidad no es la variable completamente, sino la "clase" de configuración, que es esencialmente el prefijo. Pero una vez que se definió el prefijo, cualquier variable podría usarse sin cambiarpostgresql.conf

un caballo sin nombre
fuente
3
@BrijanElwadhi: sí, eso es transaccional.
a_horse_with_no_name
2
Como nota al margen: algunas palabras están reservadas, por ejemplo cambiando set session my.vars.id = '1';a set session my.user.id = '1';rendiráERROR: syntax error at or near "user"
Dominik
2
@BrijanElwadhi: Para concretar la transacción variable que se debe utilizar: SET LOCAL .... La sessionvariable estará vigente mientras su conexión lo esté. El localalcance de la transacción.
Eugen Konkov
@dominik Puede sortear esa limitación con comillas, por ejemplo, set session "my.user.id" = '1';la current_setting('my.user.id')llamada funciona como se espera.
Miles Elam
58

Depende de tu cliente.

Sin embargo, si está utilizando el cliente psql , puede usar lo siguiente:

my_db=> \set myvar 5
my_db=> SELECT :myvar  + 1 AS my_var_plus_1;
 my_var_plus_1 
---------------
             6

Si está utilizando variables de texto, necesita citar.

\set myvar 'sometextvalue'
select * from sometable where name = :'myvar';
Shahriar Aghajani
fuente
1
\setdebe estar en minúsculas
deluan
db = # \ set profile_id 102 db = #: profile_id; ERROR: error de sintaxis en o cerca de "102" LÍNEA 1: 102; ^
AlxVallejo
1
@AlxVallejo tienes que usarlo en la declaración y la consola psql . db=> \set someid 8292 db=> SELECT * FROM sometable WHERE id = :someid;
everis
21

Usar una tabla temporal fuera de pl / PgSQL

Fuera de usar pl / pgsql u otro lenguaje pl / * como se sugiere, esta es la única otra posibilidad que se me ocurre.

begin;
select 5::int as var into temp table myvar;
select *
  from somewhere s, myvar v
 where s.something = v.var;
commit;
Evan Carroll
fuente
13

Quiero proponer una mejora a la respuesta de @ DarioBarrionuevo , para simplificar el aprovechamiento de las tablas temporales.

DO $$
    DECLARE myvar integer = 5;
BEGIN
    CREATE TEMP TABLE tmp_table ON COMMIT DROP AS
        -- put here your query with variables:
        SELECT * 
        FROM yourtable
        WHERE id = myvar;
END $$;

SELECT * FROM tmp_table;
azulado
fuente
¡Una buena solución para resolver el bloque DO no puede devolver el conjunto de datos!
CodeFarmer el
En PostgreSQL 11.0, dicha consulta devuelve 1(presumiblemente el recuento de filas) en lugar del contenido de tmp_table.
Ed Noepel
9

Esta solución se basa en la propuesta por fei0x, pero tiene las ventajas de que no es necesario unir la lista de valores de las constantes en la consulta y las constantes se pueden enumerar fácilmente al comienzo de la consulta. También funciona en consultas recursivas.

Básicamente, cada constante es una tabla de un solo valor declarada en una cláusula WITH que luego se puede invocar en cualquier parte de la parte restante de la consulta.

  • Ejemplo básico con dos constantes:
WITH
    constant_1_str AS (VALUES ('Hello World')),
    constant_2_int AS (VALUES (100))
SELECT *
FROM some_table
WHERE table_column = (table constant_1_str)
LIMIT (table constant_2_int)

Alternativamente, puede usar en SELECT * FROM constant_namelugar de lo TABLE constant_nameque podría no ser válido para otros lenguajes de consulta diferentes a postgresql.

Jorge Luis
fuente
6

Aquí hay un ejemplo usando las declaraciones PREPARE . Todavía no puede usar ?, pero puede usar la $nnotación:

PREPARE foo(integer) AS
    SELECT  *
    FROM    somewhere
    WHERE   something = $1;
EXECUTE foo(5);
DEALLOCATE foo;
Martin Zinovsky
fuente
Funciona bastante bien! Gracias.
Rui Carvalho
4

Es cierto que no hay una forma vívida e inequívoca de declarar una variable de valor único, lo que puede hacer es

with myVar as (select "any value really")

entonces, para obtener acceso al valor almacenado en esta construcción, debe

(select * from myVar)

por ejemplo

with var as (select 123)    
... where id = (select * from var)
Michael Medvedskiy
fuente
3

Puede recurrir a funciones especiales de herramientas. Al igual que para la sintaxis propia de DBeaver:

@set name = 'me'
SELECT :name;
SELECT ${name};

DELETE FROM book b
WHERE b.author_id IN (SELECT a.id FROM author AS a WHERE a.name = :name);
gavenkoa
fuente
Esto está más cerca de ser utilizable: voy a analizar si DBeaver admite listas y bucles: necesito aplicar el mismo sql a múltiples esquemas y la lista sería de los esquemas para aplicarlos.
Java
1

En DBeaver puede usar parámetros en las consultas como puede hacerlo desde el código, por lo que esto funcionará:

SELECT *
FROM somewhere
WHERE something = :myvar

Cuando ejecute la consulta, DBeaver le pedirá el valor de: myvar y ejecutará la consulta.

El codificador
fuente