Syntax error when declaring a postgres variable [duplicate] - sql

This question already has answers here:
How to declare a variable in a PostgreSQL query
(15 answers)
Closed 3 years ago.
I'm getting a SQL error when trying to declare a variable in Postgresql. Check out my code below:
declare number int8;
begin
select ID into number from public.People p where p."Name" = 'Jeff';
if found then
insert into public.Friends ("PeopleID", "Name", "PhoneNumber")
values (number, 'Jeff', '205-123-4567')
end if;
end;
When I try and run this, it returns this exception:
SQL Error [42601]: ERROR: syntax error at or near "int8"
I'm expecting there to be only one entry with the name "Jeff", so I won't have to worry about having more than one possible value for "number".

You cannot declare a variable in pure SQL in Postgres, unlike other RDBMS such as SQL Server or MySQL.
Your code compiles properly in this Postgres 12 db fiddle when I put it within plgpsql procedure (still, you should consider another name that number for a variable).
create procedure test_proc() language plpgsql AS '
declare number int8;
begin
select ID into number from people p where p."Name" = ''Jeff'';
if found then
insert into friends ("PeopleID", "Name", "PhoneNumber")
values (number, ''Jeff'', ''205-123-4567'');
end if;
end;
';
Or in Postgres < 11 with a void function:
create function test_proc() returns void language plpgsql AS '
declare number int8;
begin
select ID into number from people p where p."Name" = ''Jeff'';
if found then
insert into friends ("PeopleID", "Name", "PhoneNumber")
values (number, ''Jeff'', ''205-123-4567'');
end if;
end;
';
You might also want consider this insert ... select query, that achieves the same purpose in a single database call:
insert into friends("PeopleID", "Name", "PhoneNumber")
select ID, "Name", '205-123-4567' from people where p."Name" = 'Jeff'

Related

Posgresql is trying to insert into GENERATED ALWAYS column

My table schema:
CREATE TABLE project_sectors(
sector_id int GENERATED ALWAYS AS IDENTITY,
sector_name varchar(256),
project_count int,
PRIMARY KEY (sector_id)
);
And I am trying to execute a query for many tables with some particular column name:
DO $$
DECLARE
t text;
BEGIN
FOR t IN
SELECT table_name FROM information_schema.columns WHERE column_name = 'project_name'
LOOP
RAISE NOTICE 'INSERT METADATA FOR: %', t;
EXECUTE 'INSERT INTO project_sectors VALUES ($1, 0)'
USING t;
end loop;
end
$$ language 'plpgsql';
Once I try to run the query I get:
[42804] ERROR: column "sector_id" is of type integer but expression is of type text Hint: You will need to rewrite or cast the expression. Where: PL/pgSQL function inline_code_block line 9 at EXECUTE
When previously the EXECUTE statement was
EXECUTE format('INSERT INTO megaproject_sectors VALUES (''%I'', 0)', t)
I would get the error
ERROR: invalid input syntax for type integer: "railway"
railway is the value of t.
Why is it trying to insert data into GENERATED ALWAYS column?
Why is it trying to insert data into GENERATED ALWAYS column?
Because you are not specifying the target columns in your INSERT statement, so Postgres uses them from left to right.
It is good coding practice to always specify the target columns. As your table name is hardcoded, the dynamic SQL is unnecessary as well:
INSERT INTO project_sectors (sector_name, sector_count) VALUES (t.table_name, 0)
Note that in other database products, specifying less values than the table has columns would result in an error. So in e.g. Oracle your statement would result in "ORA-00947: not enough values"

Select only one column value based on id from table and use in function in postgresql

Description
I am creating a postgresql function and encountered a problem. I am reading data from table and based on that data i want to update data or not.
but for selection i need to either create a temp table or create another function that return a single decimal value.
Here is my code
Declare command text := 'select distance from road where gid ='|| id;
Execute command;
i am stuck at this point
i dont know what to do as i am new to postgresql
What i need
i want to apply condition on distance returned by this query
for example
IF distance < 100
THEN
(Insert into another table)
END;
What i tried
select distance into varDistance from road where gid ='|| id;
i go through Select Into command and came to know that this should be same as table . which is not acceptable to me .
Is this possible to have double type variable and after query i get my varibale initialed with value? Or else solution
It's unclear to me what you are trying to do, but to read a single value from a table, you would need the select into
Something along the lines:
create function some_function(p_id integer)
returns ...
as
$$
declare
l_distance double precision;
begin
select distance
into l_distance
from road
where id = p_id; --<< this is the parameter
if l_distance < 100 then
insert into some_other_table (...)
values (...)
end if;
end;
$$
language plpgsql;
From the little information you have provided, I don't see any reason for dynamic SQL.
If you do need dynamic SQL, use the format() function to create the SQL string with a placeholder, then use execute with an into and using clause
l_sql := format('select distance from %I gid = $1', l_table_name);
execute l_sql
into l_distance
using p_id; --<< this is the parameter

PostgreSQL variable in select and delete statements

The Problem: I have many delete lines in a PostgreSQL script where I am deleting data related to the same item in the database. Example:
delete from <table> where <column>=180;
delete from <anothertable> where <column>=180;
...
delete from <table> where <column>=180;
commit work;
There are about 15 delete statements deleting data that references <column>=180.
I have tried to replace the 180 with a variable so that I only have to change the variable, instead of all the lines in the code (like any good programmer would do). I can't seem to figure out how to do it, and it's not working.
NOTE: I am very much a SQL novice (I rarely use it), so I know there's probably a better way to do this, but please enlighten me on how I can fix this problem.
I have used these answers to try and fix it with no luck: first second third. I've even gone to the official PostgreSQL documentation, with no luck.
This is what I'm trying (these lines are just for testing and not in the actual script):
DO $$
DECLARE
variable INTEGER:
BEGIN
variable := 101;
SELECT * FROM <table> WHERE <column> = variable;
END $$;
I've also tried just delcaring it like this:
DECLARE variable INTEGER := 101;
Whenever I run the script after replacing one of the numbers with a variable this is the error I get:
SQL Error [42601]: ERROR: query has no destination for result data
Hint: If you want to discard the results of a SELECT, use PERFORM instead.
Where: PL/pgSQL function inline_code_block line 6 at SQL statement
Can someone tell me where I'm going wrong? It would be nice to only have to change the number in the variable, instead of in all the lines in the script, and I just can't seem to figure it out.
As #Vao Tsun said, you must define a destination to your SELECT statement. Use PERFORM otherwise:
--Test data
CREATE TEMP TABLE my_table (id, description) AS
VALUES (1, 'test 1'), (2, 'test 2'), (101, 'test 101');
--Example procedure
CREATE OR REPLACE FUNCTION my_procedure(my_arg my_table) RETURNS VOID AS $$
BEGIN
RAISE INFO 'Procedure: %,%', my_arg.id, my_arg.description;
END
$$ LANGUAGE plpgsql;
DO $$
DECLARE
variable INTEGER;
my_record my_table%rowtype;
BEGIN
variable := 101;
--Use your SELECT inside a LOOP to work with result
FOR my_record IN SELECT * FROM my_table WHERE id = variable LOOP
RAISE INFO 'Loop: %,%', my_record.id, my_record.description;
END LOOP;
--Use SELECT to populate a variable.
--In this case you MUST define a destination to your result data
SELECT * INTO STRICT my_record FROM my_table WHERE id = variable;
RAISE INFO 'Select: %,%', my_record.id, my_record.description;
--Use PERFORM instead of SELECT if you want to discard result data
--It's often used to call a procedure
PERFORM my_procedure(t) FROM my_table AS t WHERE id = variable;
END $$;
--DROP FUNCTION my_procedure(my_table);

Oracle merge statement within procedure fails to recognize variable in using clause [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
UPSERT into table with dynamic table name
The following procedure is declared as so:
CREATE OR REPLACE
PROCEDURE STUFF(tableToQuery VARCHAR2) AS
BEGIN
MERGE INTO myTable m
USING (select * from tableToQuery) t
ON (m.id = t.id)
... --other stuff
END STUFF;
I receive an ORA-00903 error that states the table name is invalid. My question is how do I get the value that resides within tableToQuery to equate to a valid table name in the select statement? Assume that I do not know the table name ahead of time.
UPDATE
The function compiles now, however I currently receive the unknown keyword error at the end of my function.
You need to use dynamic SQL: ie construct your SQL statement in a string and then pass that string to Oracle to execute using the execute immediate statement.
Something like
CREATE OR REPLACE
PROCEDURE STUFF(tableToQuery IN VARCHAR2) AS
s varchar2(100);
BEGIN
s := 'MERGE INTO myTable m'
|| ' USING (select * from ' || tableToQuery || ') t'
|| ' ON (m.id = t.id)';
EXECUTE IMMEDIATE s;
--other stuff
END STUFF;
should do the trick for you.
NB don't pass unverified data from end users (especially ones on the web) in the parameter tableToQuery as then you will have a SQL injection vulnerability!

Using variables in Oracle script

There is a complex query which generates a report. The query has several sub queries that generate 3-columns table for different products. Each sub query returns one row. All returned rows then need to be united.
But there is one requirement. If there are no result rows for a sub query we need to include the corresponding product to the final report anyway, but specify that Trades_Count is equal to zero.
I can achieve this using set of variables. The following code will work perfectly in MS SQL Server:
DECLARE #PRODUCT_NAME_1 nvarchar(100);
DECLARE #OFFER_VALID_DATE_1 datetime;
DECLARE #TRADES_COUNT_1 int;
DECLARE #PRODUCT_NAME_2 nvarchar(100);
DECLARE #OFFER_VALID_DATE_2 datetime;
DECLARE #TRADES_COUNT_2 int;
--Product 1
select #PRODUCT_NAME_1 = PRODUCT_NAME, #OFFER_VALID_DATE_1 = MAX(EXPIRY_DATE), #TRADES_COUNT_1 = COUNT(DEAL_NUMBER)
from (
--Data extractions with several joins goes here....
) as TempTable1
GROUP BY PRODUCT_NAME
--Product 2
select #PRODUCT_NAME_2 = PRODUCT_NAME, #OFFER_VALID_DATE_2 = MAX(EXPIRY_DATE), #TRADES_COUNT_2 = COUNT(DEAL_NUMBER)
from (
--Data extractions with several joins goes here....
) as TempTable2
GROUP BY PRODUCT_NAME
SELECT ISNULL(#PRODUCT_NAME_1,'Product 1') AS PRODUCT_NAME, #OFFER_VALID_DATE_1 AS MAX_MATURITY, ISNULL(#TRADES_COUNT_1,0)
UNION
(
SELECT ISNULL(#PRODUCT_NAME_2,'Product 2') AS PRODUCT_NAME, #OFFER_VALID_DATE_2 AS MAX_MATURITY, ISNULL(#TRADES_COUNT_2,0)
)
I think that I haven’t used anything T-SQL specific, but pure ANSI-SQL (I’m not 100% sure though).
So this is not working in Oracle.
First of all it requires having only one DECLARE keyword. Then it forces me using Begin … End execution scope. Then it doesn’t allow me to assign variables like I do (see example above) – I need to use “Select INTO” statement instead. After all calculations are done it doesn’t allow me selecting values from local variables. Heck.
Does anyone know how to make it work in Oracle?
Thanks!
PL/SQL is different than t-sql, I did a change with some comments for you, but definitely look at the links from Andy. This was ran in oracle's free SQL Developer (which also has a "Translation Scratch Handler (tools>Migration>Translation Scratch Handler) that may be of use.
--this creates a refcursor to allow us to simply print the results
var refc refcursor
/
declare --here we declare our variables
product_name_1 varchar2(15) ;
offer_valid_date_1 date ;
trade_count_1 number ;
product_name_2 varchar2(15) ;
offer_valid_date_2 date ;
trade_count_2 number ;
begin
begin --this creates a block so we may handle any exceptions just to this
select PRODUCT_NAME, MAX(EXPIRY_DATE), COUNT(DEAL_NUMBER)
into product_name_1 , offer_valid_date_1 , trade_count_1
--in oracle you select INTO, not var=COL
from (
--Data extractions with several joins goes here....
select
123 PRODUCT_NAME,
sysdate EXPIRY_DATE,
5 DEAL_NUMBER
from dual --this is a 'fake' table to generate some data for testing
) TempTable1 --drop the "as"
GROUP BY PRODUCT_NAME ;
exception --if not data is found, then this error is thrown
--if multiple values are thrown an error will also be thrown (not caught here)
when no_data_found then
product_name_1 := null ; --note, to do a var = , we use "var := value;"
offer_valid_date_1 := null;
trade_count_1 := null;
end ;
begin
select PRODUCT_NAME, MAX(EXPIRY_DATE), COUNT(DEAL_NUMBER)
into product_name_2 , offer_valid_date_2 , trade_count_2
--in oracle you select INTO, not var=COL
from (
--Data extractions with several joins goes here....
select 555 PRODUCT_NAME, sysdate EXPIRY_DATE, 6 DEAL_NUMBER
from dual
) TempTable2 -- drop the "as"
GROUP BY PRODUCT_NAME ;
exception --if not data is found, then this error is thrown
--if multiple values are thrown an error will also be thrown (not caught here)
when no_data_found then
product_name_2 := null ;
offer_valid_date_2 := null;
trade_count_2 := null;
end ;
open :refc for --you cannot just have a select statement, you must "open" a cursor for it
--oracle IsNull is NVL (or NVL2 or you can do a case or decode...)
SELECT nvl(PRODUCT_NAME_1,'Product 1') AS PRODUCT_NAME
, OFFER_VALID_DATE_1 AS MAX_MATURITY
, nvl(TRADE_COUNT_1,0)
FROM DUAL --you also must have a table, DUAL is an oracle table for this tasks
UNION
SELECT nvl(PRODUCT_NAME_2,'Product 2') AS PRODUCT_NAME
, OFFER_VALID_DATE_2 AS MAX_MATURITY
, nvl(TRADE_COUNT_2,0)
FROM DUAL;
end ;
/
--now print the results, if you did this in a proc you would simple have this as an output
print refc;
-------------
PRODUCT_NAME MAX_MATURITY NVL(:B1,0)
-------------------------------------- ----------------------
123 18.FEB.2011 08:43 1
555 18.FEB.2011 08:43 1
Oracle concepts used here:
Dual Table , NVL, Variables, pl/sql Exception
and look at this http://www.dba-oracle.com/t_convent_sql_server_tsql_oracle_plsql.htm
PL/SQL formats procedural blocks differently than T-SQL.
You'll want to use the following structure:
DECLARE
astring varchar2(1000);
anumber number;
BEGIN
my SQL code here...
END;
You don't use the # either in PL/SQL. Just use variables names directly.