Tsql IF statement is not being evaluated correctly - tsql

An xml file gets parsed and populates 7 tables within a stored procedure in an SSIS package.
When I pass an empty xml file one of the tables (Eg Table7) was still getting populated.
As a workaround to prevent it, I added an IF statement. When I run my procedure in SSMS I get the desired results as the #RecCt =0. All inserts are bypassed.
But when I execute the SSIS package, only ‘Table7’ gets populated (This table has summary values).
Create procedure SP_test
As
Begin
Declare #RecCt int
select #RecCt=(select count(*) from dbo.Order)
If #RecCt >0
Begin
Insert into Table1 …
Insert into Table2 …
Insert into Table3 …
Insert into Table4 …
Insert into Table5 …
Insert into Table6 …
Insert into Table7 …
End
Else
Begin
Select ‘Empty File’
End
End
Why is this happening in spite of being evaluated as false? Thank you much. SD

Related

Query has no result in destination data when calling colpivot inside pgsql stored procedure

I have created a procedure to generate temp table using colpivot https://github.com/hnsl/colpivot
and saving the result into a physical table as below in PGSQL
create or replace procedure create_report_table()
language plpgsql
as $$
begin
drop table if exists reports;
select colpivot('_report',
'select
u.username,
c.shortname as course_short_name,
to_timestamp(cp.timecompleted)::date as completed
FROM mdl_course_completions AS cp
JOIN mdl_course AS c ON cp.course = c.id
JOIN mdl_user AS u ON cp.userid = u.id
WHERE c.enablecompletion = 1
ORDER BY u.username' ,array['username'], array['course_short_name'], '#.completed', null);
create table reports as (SELECT * FROM _report);
commit;
end; $$
colpivot function , drop table , delete table works really fine in isolation. but when I create the procedure as above, and call the procedure to execute, this throws an error Query has no result in destination data
Is there any way I can use colpivot in collaboration with several queries as I am currently trying ?
Use PERFORM instead of SELECT. That will execute the statement, without the need to keep the result somewhere. This is what the manual says:
Sometimes it is useful to evaluate an expression or SELECT query but discard the result, for example when calling a function that has
side-effects but no useful result value. To do this in PL/pgSQL, use
the PERFORM statement

Postgres Trigger to INSERT UPDATE DELETE on similar derivative table

Description:
I am running postgresql 13
I have two tables under different schemas, t1 and t2.
t2 is derivative of t1 in the sense that they share all the same
columns and data, but t2 is always downstream of t1 as far as
validity.
The rows in both tables share the same primary key, which is what I assume would be used as the link between them.
The ask:
I would like to create a trigger that reflects any changes in t1 and syncs t2 to be the same.
I started with INSERT or UPDATE, but if DELETE is easily added, I would like to implement that as well.
Trigger Code:
-- Trigger for t1 to t2 --
CREATE OR REPLACE FUNCTION t1_schema.sync_trigger()
RETURNS TRIGGER AS
$$
BEGIN
INSERT INTO t2_schema.t2 (col1, col2, col3)
VALUES (NEW.col1, NEW.col2, NEW.col3);
RETURN NEW;
END
$$ LANGUAGE plpgsql;
CREATE TRIGGER t1t2_test_sync
AFTER INSERT OR UPDATE ON t1_schema.t1
FOR EACH ROW
EXECUTE PROCEDURE t1_schema.sync_trigger()
When I execute this code and do a test UPDATE on t1, the same row on t2 does not reflect the changes or give me any errors.
I have tried:
Discretely labeling all rows as updated with NEW. format, but run into the problem of primary key column not being editable in t2.
Adding a WHERE clause after the VALUES clause, something like WHERE primary_key=NEW.primary_key, but I get an error.
Another option I have seen is adding an IF statement before the
INSERT, or adding a WHEN clause in the trigger, but neither have
worked.
Your best approach is to not create t2 as a table. Instead create it as a VIEW on t1. This totally eliminates triggers to keep them synchronized because the actual source is the same. Follows the concept to store a single data point in only 1 place. Keep in mind that if you store a single piece in 2 places, 1 on them will be wrong at some point. (see demo).
create view soq2.t2 as
select *
from soq1.t1;
Also if you need column names to change then use an alias during the create view;
create view soq2.t2a as
select t1_id as t2_id
, name as t2_name
, status as t2_status
from soq1.t1;
(A) Solution based on triggers
You maybe get an error when updating a row in t1 because your trigger function tries to insert a new row in t2 which has alreday been inserted in t2 by the same trigger function when it has been inserted in t1. You need to duplicate and specialize your trigger functions, one for insert, one for update, one for delete because the treatment to be triggered on t2 is different :
CREATE OR REPLACE FUNCTION t1_schema.sync_trigger_insert()
RETURNS TRIGGER AS
$$
BEGIN
INSERT INTO t2_schema.t2 (col1, col2, col3)
VALUES (NEW.col1, NEW.col2, NEW.col3);
RETURN NEW;
END
$$ LANGUAGE plpgsql;
CREATE TRIGGER t1t2_test_sync_insert
AFTER INSERT ON t1_schema.t1
FOR EACH ROW EXECUTE PROCEDURE t1_schema.sync_trigger_insert() ;
CREATE OR REPLACE FUNCTION t1_schema.sync_trigger_update()
RETURNS TRIGGER AS
$$
BEGIN
UPDATE t2
SET col1 = NEW.col1
, col2 = NEW.col2
, col3 = NEW.col3
WHERE primary_key_t2 = NEW. primary_key_t1 ; -- primary_key_t2 must be replaced by the set of columns which are in the primary key of t2 with AND operators, the same for NEW.primary_key_t1
RETURN NEW;
END
$$ LANGUAGE plpgsql;
CREATE TRIGGER t1t2_test_sync_update
AFTER UPDATE ON t1_schema.t1
FOR EACH ROW EXECUTE PROCEDURE t1_schema.sync_trigger_update() ;
CREATE OR REPLACE FUNCTION t1_schema.sync_trigger_delete()
RETURNS TRIGGER AS
$$
BEGIN
DELETE FROM t2
WHERE primary_key_t2 = NEW. primary_key_t1 ; -- primary_key_t2 must be replaced by the set of columns which are in the primary key of t2 with AND operators, the same for NEW.primary_key_t1
RETURN OLD; -- NEW is not available for triggers ON DELETE
END
$$ LANGUAGE plpgsql;
CREATE TRIGGER t1t2_test_sync_delete
AFTER DELETE ON t1_schema.t1
FOR EACH ROW EXECUTE PROCEDURE t1_schema.sync_trigger_delete() ;
(B) Solution based on foreign key
It is possible that a foreign key on t2 (col1,col2,col3) referencing t1 (col1, col2, col3) with the options ON UPDATE CASCADE ON DELETE CASCADE may deliver your expected result in a much more simple and efficient way, see the manual. In this case, you don't need the triggers ON UPDATE and ON DELETE anymore, but you still need the trigger ON INSERT.

IF... ELSE... two mutually exclusive inserts INTO #temptable

I need to insert either set A or set B of records into a #temptable, depending on certain condition
My pseudo-code:
IF OBJECT_ID('tempdb..#t1') IS NOT NULL DROP TABLE #t1;
IF {some-condition}
SELECT {columns}
INTO #t1
FROM {some-big-table}
WHERE {some-filter}
ELSE
SELECT {columns}
INTO #t1
FROM {some-other-big-table}
WHERE {some-other-filter}
The two SELECTs above are exclusive (guaranteed by the ELSE operator). However, SQL compiler tries to outsmart me and throws the following message:
There is already an object named '#t1' in the database.
My idea of "fixing" this is to create #t1 upfront and then executing a simple INSERT INTO (instead of SELECT... INTO). But I like minimalism and am wondering whether this can be achieved in an easier way i.e. without explicit CREATE TABLE #t1 upfront.
Btw why is it NOT giving me an error on a conditional DROP TABLE in the first line? Just wondering.
You can't have 2 temp tables with the same name in a single SQL batch. One of the MSDN article says "If more than one temporary table is created inside a single stored procedure or batch, they must have different names". You can have this logic with 2 different temp tables or table variable/temp table declared outside the IF-Else block.
Using a Dyamic sql we can handle this situation. As a developoer its not a good practice. Best to use table variable or temp table.
IF 1=2
BEGIN
EXEC ('SELECT 1 ID INTO #TEMP1
SELECT * FROM #TEMP1
')
END
ELSE
EXEC ('SELECT 2 ID INTO #TEMP1
SELECT * FROM #TEMP1
')

Navigating the results of a stored procedure via a cursor using T-SQL

Due to a legacy report generation system, I need to use a cursor to traverse the result set from a stored procedure. The system generates report output by PRINTing data from each row in the result set. Refactoring the report system is way beyond scope for this problem.
As far as I can tell, the DECLARE CURSOR syntax requires that its source be a SELECT clause. However, the query I need to use lives in a 1000+ line stored procedure that generates and executes dynamic sql.
Does anyone know of a way to get the result set from a stored procedure into a cursor?
I tried the obvious:
Declare Cursor c_Data For my_stored_proc #p1='foo', #p2='bar'
As a last resort, I can modify the stored procedure to return the dynamic sql it generates instead of executing it and I can then embed this returned sql into another string and, finally, execute that. Something like:
Exec my_stored_proc #p1='foo', #p2='bar', #query='' OUTPUT
Set #sql = '
Declare Cursor c_Data For ' + #query + '
Open c_Data
-- etc. - cursor processing loop etc. goes here '
Exec #sql
Any thoughts? Does anyone know of any other way to traverse the result set from a stored proc via a cursor?
Thanks.
You could drop the results from the stored proc into a temp table and select from that for your cursor.
CREATE TABLE #myResults
(
Col1 INT,
Col2 INT
)
INSERT INTO #myResults(Col1,Col2)
EXEC my_Sp
DECLARE sample_cursor CURSOR
FOR
SELECT
Col1,
Col2
FROM
#myResults
Another option may be to convert your stored procedure into a table valued function.
DECLARE sample_cursor CURSOR
FOR
SELECT
Col1,
Col2
FROM
dbo.NewFunction('foo', 'bar')
You use INSERT ... EXEC to push the result of the procedure into a table (can be a temp #table or a #table variable), the you open the cursor over this table. The article in the link discusses the problems that may occur with this technique: it cannot be nested and it forces a transaction around the procedure.
You could execute your SP into a temporary table and then iterate over the temporary table with the cursor
create table #temp (columns)
insert into #temp exec my_stored_proc ....
perform cursor work
drop table #temp

SQL Server 2000 TSQL : Stored proc results into table

I would like to get the out put of a stored proc call and create a permanent table with out specifying columns. Because the stored proc return lots of columns...
So.. how can I do this??
SELECT * INTO MYTABLE FROM MYSTOREDPROC #PARAM1 = 1, #PARAM2 = 'HELLO'
is it possible??
Example would help me alot
you can't generate the table from calling stored procedure. However, edit the called stored procedure and alter the select that generates the result set to have an INTO YourNewTable clause:
SELECT
columns....
INTO YourNewTable
from ...
where
order by...
Run the procedure 1 time, to generate the table. remove the changes (INTO YourNewTable clause) and then call the procedure as:
INSERT INTO YourNewTable
EXEC YourProcedure params...