I'm trying to update/insert data to postgresql depending whether given id is already preset in the table.
This works for update
UPDATE table SET
column=#Column
WHERE id=#Id;
And this works also (hardcoded values)
DO
$$
BEGIN
IF EXISTS (SELECT * FROM table WHERE id=123) THEN
UPDATE table SET
column=123
WHERE id=123;
ELSE
INSERT INTO table(id,column) VALUES (123,123);
END IF;
END
$$
But as soon as I try to give values as parameters, it breaks giving an error syntax error at or near "=#"
DO
$$
BEGIN
IF EXISTS (SELECT * FROM table WHERE id=#Id) THEN
UPDATE table SET
column=#Column
WHERE id=#Id;
ELSE
INSERT INTO table(id,column) VALUES (#Id,#Column);
END IF;
END
$$
To isolate the problem leaving just one parameter like this
DO
$$
BEGIN
IF EXISTS (SELECT * FROM table WHERE id=#Id) THEN
UPDATE table SET
column=123
WHERE id=123;
ELSE
INSERT INTO table(id,column) VALUES (123,123);
END IF;
END
$$
Gives error is Npgsql.PostgresException: '42703: column "id" does not exist'. So is there some kind of magic I have to do with the parameters?
Npgsql does not support parameters within dollar-quoted string literals ($$).
However, you seem to be implementing upsert (update or insert) - PostgreSQL supports that natively with INSERT ... ON CONFLICT syntax. See this tutorial or other documentation on this feature, which should obviate the complexity of what you're trying to do.
Related
Let's assume we have a table that contains data as below:
CREATE TABLE tab(i INT PRIMARY KEY);
INSERT INTO tab(i) VALUES(1),(2),(3);
SELECT * FROM tab;
Now my goal is to create SQL script that will add a new column to existing table:
ALTER TABLE IF EXISTS tab ADD COLUMN col VARCHAR(10);
Everything works as intended. Except the fact I would like to be able to run script multiple times but the effect should take place only once(idempotence).
If I try to run it again I will get:
SQL compilation error: column COL already exists
Normally I would use one of these approaches:
a) Using control structure IF to check metadata tables before executing query:
-- (T-SQL)
IF NOT EXISTS(SELECT * FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME='TAB' AND COLUMN_NAME = 'COL')
BEGIN
ALTER TABLE tab ADD col VARCHAR(10);
END;
db<>fiddle demo
I have not found IF statement in Snowflake's documentation.
b) SQL dialect that supports IF NOT EXISTS syntax:
-- PostgreSQL
ALTER TABLE IF EXISTS tab ADD COLUMN IF NOT EXISTS col VARCHAR(10);
db<>fiddle demo
Most of Snowflake SQL commands contain IF EXISTS/OR REPLACE clauses which means it was written in a way to allow running scripts multiple times.
I was considering using code like:
CREATE OR REPLACE TABLE tab
AS
SELECT i, CAST(NULL AS VARCHAR(10)) AS col
FROM tab;
This approach on other hand causes unnecessary table creation and does not preserve metadata(like primary key).
Is there a way to achieve similar effect on Snowflake? Preferably by using conditional code(add column is an example).
You can use something like this. It will report the failure to add the column if it already exists, but it will handle the error so it won't interfere with the execution of a sql script:
create or replace procedure SafeAddColumn(tableName string, columnName string, columnType string)
returns string
language JavaScript
as
$$
var sql_command = "ALTER TABLE IF EXISTS " + TABLENAME + " ADD COLUMN " + COLUMNNAME + " " + COLUMNTYPE + ";";
var strOut;
try {
var stmt = snowflake.createStatement( {sqlText: sql_command} );
var resultSet = stmt.execute();
while (resultSet.next()) {
strOut = resultSet.getColumnValue(1);
}
}
catch (err) {
strOut = "Failed: " + err; // Return a success/error indicator.
}
return strOut;
$$;
CREATE OR REPLACE TABLE tab(i INT PRIMARY KEY);
INSERT INTO tab(i) VALUES(1),(2),(3);
SELECT * FROM tab;
call SafeAddColumn('tab', 'col', 'varchar(10)');
select * from tab;
call SafeAddColumn('tab', 'col', 'varchar(10)');
It is possible to write conditional code using Snowflake Scripting.
Working with Branching Constructs
Snowflake Scripting supports the following branching constructs:
IF-THEN-ELSEIF-ELSE
CASE
Setup:
CREATE OR REPLACE TABLE PUBLIC.tab(i INT PRIMARY KEY);
INSERT INTO tab(i) VALUES(1),(2);
SELECT * FROM tab;
-- i
-- 1
-- 2
Code that can be rerun multiple times(subsequent runs will take no effect):
-- Snowsight
BEGIN
IF (NOT EXISTS(SELECT *
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'TAB'
AND TABLE_SCHEMA = 'PUBLIC'
AND COLUMN_NAME = 'COL')) THEN
ALTER TABLE IF EXISTS tab ADD COLUMN col VARCHAR(10);
END IF;
END;
EXECUTE IMMEDIATE is required is run using "classic web interface":
EXECUTE IMMEDIATE $$
BEGIN
IF (NOT EXISTS(SELECT *
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'TAB'
AND TABLE_SCHEMA = 'PUBLIC'
AND COLUMN_NAME = 'COL')) THEN
ALTER TABLE IF EXISTS tab ADD COLUMN col VARCHAR(10);
END IF;
END;
$$
After:
SELECT * FROM tab;
-- i col
-- 1 NULL
-- 2 NULL
Although Snowflake has implemented a pretty rich mix of DDL and DML for their SQL implementation, when it comes to procedural code they seem to be relying on JavaScript, at least at this point. But you should be able to accomplish your idempotent ALTER script through a JavaScript stored procedure.
I'm afraid I lack the JavaScript skills to provide you with a working sample myself at this point. The organization I'm with recently adopted Snowflake, though, so I'll share some of my research.
Here's a recent blog post on just this question:
Snowflake Control Structures – IF, DO, WHILE, FOR
Snowflake's overview documentation regarding stored procedures:
Stored Procedures
On the page above, what is currently the third link down contains extensive sample code.
Working With Stored Procedures
Building on Lukasz answer, to include database in condition you can use:
execute immediate $$
BEGIN
IF (
NOT EXISTS(
SELECT *
FROM "INFORMATION_SCHEMA"."COLUMNS"
WHERE
"TABLE_CATALOG" = 'DB_NAME'
AND "TABLE_SCHEMA" = 'SCHEMA_NAME'
AND "TABLE_NAME" = 'TABLE_NAME'
AND "COLUMN_NAME" = 'col_name'
)
) THEN
ALTER TABLE IF EXISTS "DB_NAME"."SCHEMA_NAME"."TABLE_NAME"
ADD COLUMN "col_name" VARCHAR NULL;
END IF;
END;
$$;
I am new to oracle.So,I am creating a procedure which has all features of insert,update,delete and select method.So when I hit the compilation button then the result is:
Warning: compiled but with compilation errors
But i am unable to see where is the mistake I did.My table is:
Now the procedure I tried is:
CREATE OR REPLACE PROCEDURE OT.ALL_CRUD_PERSON(DATA1 VARCHAR2,
ID PERSON.ID%TYPE,
NAME PERSON.NAME%TYPE,
AGE PERSON.AGE%TYPE,
R_C OUT SYS_REFCURSOR)
IS
CURSOR CUR IS
SELECT MAX(ID) FROM OT.PERSON;
BEGIN
IF DATA1='INSERT' THEN
INSERT INTO OT.PERSON(ID,NAME,AGE) VALUES (CUR,NAME,AGE);
END IF;
IF DATA1='UPDATE' THEN
UPDATE OT.PERSON SET NAME=NAME,AGE=AGE WHERE ID=ID;
END IF;
IF DATA1='DELETE' THEN
DELETE FROM OT.PERSON WHERE ID=ID;
END IF;
IF DATA1='SELECT' THEN
OPEN R_C FOR
SELECT * FROM OT.PERSON;
END IF;
END;
/
Also,I want to ask is it the good process to put all the functionality in same procedure?
Problem 1
INSERT INTO PERSON(ID,NAME,AGE) VALUES (CUR,NAME,AGE);
This will result in a
Error(19,41): PL/SQL: ORA-00904: "CUR": invalid identifier
Perhaps should be
INSERT INTO PERSON(ID,NAME,AGE) VALUES (ID,NAME,AGE);
That at least will compile without errors.
Looks like you're using Toad...I know if you use SQL Developer it will automatically show you the Errors whenever you compile PL/SQL with compiler feedback.
Also, ask yourself this question - do you want due to a bug, for a call to do an UPDATE to accidentally do a DELETE?
I would suggest you break these operations out to individual functions/procedures - and tie them together using a PACKAGE.
I did a search over the net but I couldnt find my answer
in oracle , if we to specify for the trigere if its insert or update , we write like this :
create or replace trigger TRG_LOGS
after INSERT or update or delete
ON TABOE_LOGS
FOR EACH ROW
DECLARE
V_USERNAME VARCHAR2(100);
BEGIN
if inserting then
insert into long_log(NAME) VALUE (:new.NAME)
ELSE if UPDATING THEN
insert into long_log(NAME) VALUE (:OLD.NAME)
END;
END;
Is throwing an error on Incorrect syntax near the keyword 'insert'.
For Sybase, each action is a seperate trigger:
create trigger TRG_LOGS_INS on TABOE_LOGS
for INSERT
as
DECLARE #V_USERNAME varchar(100)
BEGIN
insert into long_log
select NAME from INSERTED
END
....
create trigger TRG_LOGS_UPD on TABOE_LOGS
for UPDATE
as
DECLARE #V_USERNAME varchar(100)
BEGIN
insert into long_log
select NAME from DELETED
END
Not sure if my syntax is exactly right, but should get you pointed in the right direction. The INSERTED table (similar to Oracles new) stores the new records on either an insert or update action. The DELETED table (similar to Oracles old) stores the old records on either an update or delete action.
More information and examples can be found in the Sybase T-SQL Users Guide: Triggers
I'm new to SQL and I'm trying to create a trigger that would insert into an audit table.
create or replace trigger late_ship_insert
after insert on suborder
for each row
declare
employee int;
begin
select emp_id
into employee
from handles
where order_no = :new.order_no;
if :new.actual_ship_date > :new.req_ship_date then
insert into ship_audit
values (employee, :new.order_no, :new.suborder_no, :new.req_ship_date, :new.actual_ship_date);
end;
Error:
Warning: execution completed with warning
trigger late_ship_insert Compiled.
But once I try an insert statement it tell me the trigger is not working it to drop it.
Error starting at line 1 in command:
insert into suborder
values ( 8, 3, '10-jun-2012', '12-jul-2012', 'CVS', 3)
Error at Command Line:1 Column:12
Error report:
SQL Error: ORA-04098: trigger 'COMPANY.LATE_SHIP_INSERT' is invalid and failed re-validation
04098. 00000 - "trigger '%s.%s' is invalid and failed re-validation"
*Cause: A trigger was attempted to be retrieved for execution and was
found to be invalid. This also means that compilation/authorization
failed for the trigger.
*Action: Options are to resolve the compilation/authorization errors,
disable the trigger, or drop the trigger.
Any idea what is causing this, any help would be greatly appreciated. Thanks!
The error that becomes apparent when you format your code is that your IF statement is missing the END IF
create or replace trigger late_ship_insert
after insert on suborder
for each row
declare
employee int;
begin
select emp_id
into employee
from handles
where order_no = :new.order_no;
if :new.actual_ship_date > :new.req_ship_date then
insert into ship_audit
values (employee, :new.order_no, :new.suborder_no, :new.req_ship_date, :new.actual_ship_date);
end if;
end;
As a general matter, you should always list the columns of the destination table in your INSERT statement rather than relying on the fact that your INSERT statement specifies a value for every column and specifies them in the proper order. That will make your code much more robust since it won't become invalid when someone adds additional columns to the table for example. That would look something like this (I'm guessing at the names of the columns in the ship_audit table)
create or replace trigger late_ship_insert
after insert on suborder
for each row
declare
employee int;
begin
select emp_id
into employee
from handles
where order_no = :new.order_no;
if :new.actual_ship_date > :new.req_ship_date then
insert into ship_audit( emp_id, order_no, suborder_no, req_ship_date, actual_ship_date )
values (employee, :new.order_no, :new.suborder_no, :new.req_ship_date, :new.actual_ship_date);
end if;
end;
I am performing the below operation. I am getting the error and unable to find what the error is.Could any one help me finding it.
a) Check for the availability of DESTINATION data base. If it is not exist, create the data base and move the tables to the data base.
b) If the table exists in the DESTINATION data base then no process required for the table.
if db_id('Destination')is null
begin
Create database Destination
select * into TabDestination from [Source].[dbo].[TabSource]
end
else
begin
use Destination
go
if('TabDestination' in (select name from sys.objects where type = 'u'))
insert into TabDestination select * from [Source].[dbo].[TabSource]
end
I am getting fallowing error
Msg 911, Level 16, State 1, Line 8
Database 'Destination' does not exist. Make sure that the name is entered correctly.
Msg 102, Level 15, State 1, Line 3
Incorrect syntax near 'end'.
Your problem is with the USE, from the documentation:
USE is executed at both compile and execution time...
If the database specified doesn't exist at compile time then the query will fail. You can see this by trying to run the following query:
IF 1 = 2
BEGIN
USE NonexistantDatabase
END
This query fails despite the fact that the USE statement is never executed.
You should instead change your query to use database qualified names, for example:
INSERT INTO Destination.dbo.Table SELECT * FROM Source.dbo.Table
Few problems here:
After Create database Destination you need to use that database before you do the select * into TabDestination... as you will create TabDestination in some other DB.
The Go in the middle of the begin...end block won't work.
To specify your database for the inserts to TabDesitination you'd be better to use the fully qualified name of the table than calling Use, eg Insert Destiation.dbo.TabDestination...
You need to use If Exists (select... for the second if statement.
Because your Database may not exists when the script compiles, a lot of the sql needs to be exec'd dynamically.
So your script could be re-written as:
if db_id('Destination')is null
begin
Create database Destination
exec ('select * into Destination.dbo.TabDestination from [Source].[dbo].[TabSource]')
end
else
begin
if exists (select name from Destination.sys.objects where name = 'TabDestination' and type = 'u')
insert into Destination.dbo.TabDestination select * from [Source].[dbo].[TabSource]
end
A variation on #Jon Egerton's answer, however there is one case you've neglected to cover: the database exists but the table does not.
DECLARE #sql NVARCHAR(MAX) = N'SELECT *
INTO Destination.dbo.TabDestination
FROM Source.dbo.TabSource;';
IF DB_ID('Destination') IS NULL
BEGIN
PRINT 'Creating database...';
CREATE DATABASE Destination;
PRINT 'Selecting into new table...';
EXEC sp_executeSQL #sql;
END
ELSE
BEGIN
IF EXISTS (SELECT 1 FROM Destination.sys.tables WHERE schema_id = 1
AND name = N'TabDestination')
BEGIN
PRINT 'Inserting into existing table...';
INSERT Destination.dbo.TabDestination SELECT * FROM Source.dbo.TabSource;
END
ELSE
BEGIN
PRINT 'Selecting into new table...';
EXEC sp_executeSQL #sql;
END
END
EDIT
Added PRINT statements for debugging purposes, as I suggested in the follow-up to #Jon's answer.
You just need to get rid of the GO command, its a batch separator so it breaks your begin/end. Oh and you can't use USE like that either.