how to execute the next T-SQL when a before T-SQL throw an error? - tsql

I am working with SQlite and I have many T-SQL that I want to execute all of them in this way:
T-SQL1; T-SQL2, ... T-SQLN
My T-SQL are:
insert into myRelationTable(IDTable1, IDTabl2) VALUES (1,1);
insert into myRelationTable(IDTable1, IDTabl2) VALUES (1,2);
insert into myRelationTable(IDTable1, IDTabl2) VALUES (1,3);
...
With this T.SQLs I want to related records from the table1 with the table2. If any of the relations exist, there are no problem all the T-SQL is execute, but if for exameple there are a problem with the second, the first is execute but the third and the next T-SQL are not executed.
My quiestion it's if there are any way to continue execute the T-SQL and don't take care if some of the T-SQL throw an error, because what I want it's to have the relation, if some relation exists it's because other user created it, so at the end it's what I want, that the relation exists, so I would like to continue with the next T-SQL.
Is it possible?
However, if I try to delete a record that does not exists, the next T-SQL are executed, so SQLite does not take care about the error and continue with the following. Why when I try to add a new record it does not work in the same way?
Thanks.

I would strongly recommend checking if it is OK to perform the T-SQL rather than ignoring errors.
You can do this by:
DECLARE #count int
SET #count = (SELECT COUNT(1) FROM myRelationTable WHERE IDTable1 =1 AND IDTabl2 = 1)
IF #count = 0 OR #count IS NULL
BEGIN
INSERT INTO myRelationTable(IDTable1, IDTabl2) VALUES (1,1)
END
SET #count = (SELECT COUNT(1) FROM myRelationTable WHERE IDTable1 =1 AND IDTabl2 = 2)
IF #count = 0 OR #count IS NULL
BEGIN
INSERT INTO myRelationTable(IDTable1, IDTabl2) VALUES (1,2)
END
SET #count = (SELECT COUNT(1) FROM myRelationTable WHERE IDTable1 =1 AND IDTabl2 = 3)
IF #count = 0 OR #count IS NULL
BEGIN
INSERT INTO myRelationTable(IDTable1, IDTabl2) VALUES (1,3)
END
Which can very easily be wrapped within a stored procedure.
As to your question the answer is:
Sure, easily:
BEGIN TRY
insert into myRelationTable(IDTable1, IDTabl2) VALUES (1,1);
END TRY
BEGIN CATCH
--Do nothing
END CATCH
BEGIN TRY
insert into myRelationTable(IDTable1, IDTabl2) VALUES (1,2);
END TRY
BEGIN CATCH
--Do nothing
END CATCH
BEGIN TRY
insert into myRelationTable(IDTable1, IDTabl2) VALUES (1,3);
END TRY
BEGIN CATCH
--Do nothing
END CATCH

Really my problem is that I am using SQLite Expert to execute the T-SQL, but this program detect as error syntax the Begin try line.
Other way to solve the problem is to use the igonre keyword in this way:
insert or ignore into my table...
This makes that if exists an error, the ignore it and execute the next statement. But again, the "ignore" keyword is detected as an syntax error by SQLite Expert.
The "ignore" keyword belongs to the on conflict clouse. There are more information in this link.
Thanks.

Related

Postgresql trigger syntax error at or near "NEW"

Here is what i'm trying to do:
ALTER TABLE publishroomcontacts ADD COLUMN IF NOT EXISTS contactorder integer NOT NULL default 1;
CREATE OR REPLACE FUNCTION publishroomcontactorder() RETURNS trigger AS $publishroomcontacts$
BEGIN
IF (TG_OP = 'INSERT') THEN
with newcontactorder as (SELECT contactorder FROM publishroomcontacts WHERE publishroomid = NEW.publishroomid ORDER BY contactorder limit 1)
NEW.contactorder = (newcontactorder + 1);
END IF;
RETURN NEW;
END;
$publishroomcontacts$ LANGUAGE plpgsql;
CREATE TRIGGER publishroomcontacts BEFORE INSERT OR UPDATE ON publishroomcontacts
FOR EACH ROW EXECUTE PROCEDURE publishroomcontactorder();
I've been looking into a lot of examples and they all look like this. Most of them a couple of years old tho. Has this changed or why doesn't NEW work? And do i have to do the insert in the function or does postgres do the insert with the returned NEW object after the function is done?
I'm not sure what you're trying to do, but your syntax is wrong here:
with newcontactorder as (SELECT contactorder FROM publishroomcontacts WHERE publishroomid = NEW.publishroomid ORDER BY contactorder limit 1)
NEW.contactorder = (newcontactorder + 1);
Do not use CTE query if there is no select that comes afterwards. If you want to increment contactorder column for particular publishroomid whenever new one is being added and this is your sequence (auto increment) mechanism then you should replace it with:
NEW.contactorder = COALESCE((
SELECT max(contactorder)
FROM publishroomcontacts
WHERE publishroomid = NEW.publishroomid
), 1);
Note the changes:
there's no CTE, just variable assignment with SELECT query
use MAX() aggregate function instead of ORDER BY + LIMIT
wrapped up with COALESCE(x,1) function to properly insert first contacts for rooms, it will return 1 if your query does return NULL
Your trigger should look like this
CREATE OR REPLACE FUNCTION publishroomcontactorder() RETURNS trigger AS $publishroomcontacts$
BEGIN
IF (TG_OP = 'INSERT') THEN
NEW.contactorder = COALESCE((
SELECT max(contactorder) + 1
FROM publishroomcontacts
WHERE publishroomid = NEW.publishroomid
), 1);
END IF;
RETURN NEW;
END;
$publishroomcontacts$ LANGUAGE plpgsql;
Postgres will insert the row itself, you don't have to do anything, because RETURN NEW does that.
This solution does not take care of concurrent inserts which makes it unsafe for multi-user environment! You can work around this by performing an UPSERT !
WITH is not an assignment in PL/pgSQL.
PL/pgSQL interprets the line as SQL statement, but that is bad SQL because the WITH clause is followed by NEW.contactorder rather than SELECT or another CTE.
Hence the error; it has nothing to do with NEW as such.
You probably want something like
SELECT contactorder INTO newcontactorder
FROM publishroomcontacts
WHERE publishroomid = NEW.publishroomid
ORDER BY contactorder DESC -- you want the biggest one, right?
LIMIT 1;
You'll have to declare newcontactorder in the DECLARE section.
Warning: If there are two concurrent inserts, they might end up with the same newcontactorder.

Syntax error when calling one function from another

I am trying to create a function which calls 2 other functions.
Below is the calling function's code from where I am trying to call 2 another functions, schema1.func1() and schema1.func2().
But it is throwing error at the line SELECT schema1.func1(temp_val); saying:
syntax error at or near "SELECT".
I tried to figure out the correct syntax but couldn't resolve.
I am using Postgres version 1.14.3
DECLARE
temp_val int;
cursor1 CURSOR
FOR
SELECT col1 from schema1.table1;
BEGIN
OPEN cursor1;
LOOP
FETCH cursor1 INTO temp_val;
EXIT WHEN NOT FOUND;
SELECT CASE
WHEN NOT EXISTS (SELECT col2 FROM schema1.table2 WHERE col2 = temp_val)
THEN
BEGIN
SELECT schema1.func1(temp_val);
SELECT schema1.func2(temp_val);
END;
END CASE;
END LOOP;
CLOSE cursor1;
END;
There is a ; missing at:
SELECT CASE WHEN NOT EXISTS (SELECT col2 FROM schema1.table2 WHERE col2 = temp_val)
so..
SELECT CASE WHEN NOT EXISTS (SELECT col2 FROM schema1.table2 WHERE col2 = temp_val);
You can't mix PL/pgSQL BEGIN ... END blocks with SQL statement - even if that SQL statement is part of a PL/pgSQL function. So the BEGIN inside the THEN part of the CASE expression is invalid. A SQL CASE expression ends with just an END. A PL/pgSQL CASE statement would end with END CASE. As you are trying to use a CASE expression, it would require just END, not end case.
You also need to use perform in order to call a function where you want to discard the result.
It's not clear to me what exactly you want to achieve. If you only want to call those two functions if a row does not exist in table2 then you can do this with a single loop:
DECLARE
t1_rec record;
BEGIN
FOR t1_rec IN select t1.col1
from table1 t1
where not exists (select *
from table2 t2
where t2.col2 = t1.col1)
LOOP
perform schema1.func1(t1_rec.col1);
perform schema1.func2(t1_rec.col1);
END LOOP;
END;

Using IF Exists function in postgreSQL pgadmin3 (syntax error ,SQL state: 42601 Character: 1)

I am trying to execute an SQL statement in pgadmin3 that do the following:
If a student with a specific name and age already exists then get the student_id else insert a new record with the specified name and age and then get the created student_id
I have tried this code:
IF EXISTS (SELECT 1 FROM main.student WHERE studentname='hhh' and studentage=15)
BEGIN
SELECT student_id FROM main.student WHERE studentname='hhh' and studentage=15
END
ELSE
BEGIN
INSERT INTO main.student(studentname,studentage) VALUES('hhh',15)
END;
END IF;
But I am always getting this error:
syntax error at or near "IF"
SQL state: 42601
Character: 1
Can you please tell me what I am doing wrong. Also how can I get the student_id after the insert statement?
Actually your statement is not SQL. it is PL/PGSQL statement.
So obviously when you send this to Postgres as SQL query it will throw an exception.
If you are doing this from pgAdmin3 it has nice feature to run pl/psql scripts. use it
IF EXISTS (SELECT 1 FROM main.student WHERE studentname='hhh' and studentage=15) THEN
SELECT student_id FROM main.student WHERE studentname='hhh' and studentage=15;
ELSE
INSERT INTO main.student(studentname,studentage) VALUES('hhh',15);
END IF;
Some points you need to consider:
For IF statement, you need to use THEN
To run just one syntax, you don't need the BEGIN/END
Use ; at the end of each statement
Also if you are running an ad-hoc statement, you need to run it within DO command
DO
$do$
IF EXISTS (SELECT 1 FROM main.student WHERE studentname='hhh' and studentage=15) THEN
SELECT student_id FROM main.student WHERE studentname='hhh' and studentage=15;
ELSE
INSERT INTO main.student(studentname,studentage) VALUES('hhh',15);
END IF;
END
$do$
For the last part of your question, you can return the id you are inserting
INSERT INTO main.student(studentname,studentage) VALUES('hhh',15) RETURNING student_id

What does a try catch change in a while statement that uses ##ROWCOUNT?

I want to add a try catch to a while loop. The loop used while on ##ROWCOUNT > 0. In the while I have an update top (100) statement that works well without a try catch around it. When I add the try, the while ends in the first loop. What impact does the try have on ##ROWCOUNT that makes the while loop end even tough the update touched 100 records?
--do we have anything to process?
select top 1 * from SomeTable where processedFlag is null
WHILE(##ROWCOUNT > 0)
BEGIN
begin try
-- here I have an udpate top (100) statement that processes records with null flag in small batches
end try
begin catch
-- update ##ROWCOUNT so the while continues?
select top 1 * from SomeTable where processedFlag is null
end catch
END
I believe it because of
Statements such as USE, SET , DEALLOCATE CURSOR, CLOSE CURSOR,
BEGIN TRANSACTION or COMMIT TRANSACTION reset the ROWCOUNT value to 0.
May be END TRY is among them, but MSDN doesn't list all possible statements.
This will fix the problem:
DECLARE #i INT
SELECT #i = COUNT(*) FROM SomeTable WHERE processedFlag IS NULL
WHILE(#i > 0)
BEGIN
BEGIN TRY
UPDATE...
SET #i = ##ROWCOUNT
END TRY
BEGIN CATCH
SELECT #i = COUNT(*) FROM SomeTable WHERE processedFlag IS NULL
END CATCH
END

How to select into a variable in PL/SQL when the result might be null?

Is there a way in to just run a query once to select into a variable, considering that the query might return nothing, then in that case the variable should be null.
Currently, I can't do a select into a variable directly, since if the query returns nothing, the PL/SQL would complain variable not getting set. I can only run the query twice, with the first one do the count and if the count is zero, set the variable to null, and if the count is 1, select into the variable.
So the code would be like:
v_column my_table.column%TYPE;
v_counter number;
select count(column) into v_counter from my_table where ...;
if (v_counter = 0) then
v_column := null;
elsif (v_counter = 1) then
select column into v_column from my_table where ...;
end if;
thanks.
Update:
The reason I didn't use exception is I still have some following logic after assigning the v_column, and I have to use goto in the exception section to jump back to the following code. I'm kind of hesitate of goto lines.
You can simply handle the NO_DATA_FOUND exception by setting your variable to NULL. This way, only one query is required.
v_column my_table.column%TYPE;
BEGIN
BEGIN
select column into v_column from my_table where ...;
EXCEPTION
WHEN NO_DATA_FOUND THEN
v_column := NULL;
END;
... use v_column here
END;
I know it's an old thread, but I still think it's worth to answer it.
select (
SELECT COLUMN FROM MY_TABLE WHERE ....
) into v_column
from dual;
Example of use:
declare v_column VARCHAR2(100);
begin
select (SELECT TABLE_NAME FROM ALL_TABLES WHERE TABLE_NAME = 'DOES NOT EXIST')
into v_column
from dual;
DBMS_OUTPUT.PUT_LINE('v_column=' || v_column);
end;
What about using MAX?
That way if no data is found the variable is set to NULL, otherwise the maximum value.
Since you expect either 0 or 1 value, MAX should be OK to use.
v_column my_table.column%TYPE;
select MAX(column) into v_column from my_table where ...;
Using an Cursor FOR LOOP Statement is my favourite way to do this.
It is safer than using an explicit cursor, because you don't need to remember to close it, so you can't "leak" cursors.
You don't need "into" variables, you don't need to "FETCH", you don't need to catch and handle "NO DATA FOUND" exceptions.
Try it, you'll never go back.
v_column my_table.column%TYPE;
v_column := null;
FOR rMyTable IN (SELECT COLUMN FROM MY_TABLE WHERE ....) LOOP
v_column := rMyTable.COLUMN;
EXIT; -- Exit the loop if you only want the first result.
END LOOP;
From all the answers above, Björn's answer seems to be the most elegant and short. I personally used this approach many times. MAX or MIN function will do the job equally well. Complete PL/SQL follows, just the where clause should be specified.
declare v_column my_table.column%TYPE;
begin
select MIN(column) into v_column from my_table where ...;
DBMS_OUTPUT.PUT_LINE('v_column=' || v_column);
end;
I would recommend using a cursor. A cursor fetch is always a single row (unless you use a bulk collection), and cursors do not automatically throw no_data_found or too_many_rows exceptions; although you may inspect the cursor attribute once opened to determine if you have a row and how many.
declare
v_column my_table.column%type;
l_count pls_integer;
cursor my_cursor is
select count(*) from my_table where ...;
begin
open my_cursor;
fetch my_cursor into l_count;
close my_cursor;
if l_count = 1 then
select whse_code into v_column from my_table where ...;
else
v_column := null;
end if;
end;
Or, even more simple:
declare
v_column my_table.column%type;
cursor my_cursor is
select column from my_table where ...;
begin
open my_cursor;
fetch my_cursor into v_column;
-- Optional IF .. THEN based on FOUND or NOTFOUND
-- Not really needed if v_column is not set
if my_cursor%notfound then
v_column := null;
end if;
close my_cursor;
end;
I use this syntax for flexibility and speed -
begin
--
with KLUJ as
( select 0 ROES from dual
union
select count(*) from MY_TABLE where rownum = 1
) select max(ROES) into has_rows from KLUJ;
--
end;
Dual returns 1 row, rownum adds 0 or 1 rows, and max() groups to exactly 1. This gives 0 for no rows in a table and 1 for any other number of rows.
I extend the where clause to count rows by condition, remove rownum to count rows meeting a condition, and increase rownum to count rows meeting the condition up to a limit.
COALESCE will always return the first non-null result. By doing this, you will get the count that you want or 0:
select coalesce(count(column) ,0) into v_counter from my_table where ...;