Execute immediate UPDATE Statement - sql

I try to make an update table CHECK_COMPRESSER into PROCEDURE and I use EXECUTE IMMEDIATE :
EXECUTE immediate 'update CHECK_COMPRESSER
set NEW_SIZE_MB = '||''''||TABLE_P_ENTRY.NEW_SIZE_MB || '''' ||
' WHERE EXEC_ID = ' || '''' || EXEC_ID || '''' || ' AND TABLE = ' || '''' || TABLE_P_ENTRY.SEGMENT_NAME || '''' || ' AND PARTITION = ' || '''' || TABLE_P_ENTRY.PARTITION_NAME || '''';
dbms_output.put_line shows:
update CHECK_COMPRESSER set NEW_SIZE_MB = '182' WHERE EXEC_ID = '43' AND TABLE = 'MA_CONTACT_COMPRESS' AND PARTITION = 'P_OLD'
but there is an error:
ORA-00936: missing expression ORA-06512: at "SASDBA.COMPRESS_TABLE",
line 50
so, how should I edit this code?

TABLE is a keyword. It can be used as identifier only if quoted: "TABLE".
P.S. PARTITION is the same.

Related

Optimising changing column types

I've got an ETL script which changes the column types of a table to desired type and updates the data. The script works perfectly but I was wondering if there was a better & quicker way of doing it? Working in Redshift.
Current procedure;
CREATE or REPLACE PROCEDURE p_alter_staging_tbls() AS $$
DECLARE
row RECORD;
BEGIN
FOR row IN select * from
(
select distinct table_name, column_name,data_type from staging.staging_col_info_v a order by a.table_name asc
)
loop
EXECUTE 'ALTER TABLE staging.' || row.table_name || ' ' || 'ADD COLUMN ' || concat('new_',row.column_name) || ' ' || row.data_type ;
EXECUTE 'UPDATE staging.' || row.table_name || ' ' || 'SET ' || concat('new_',row.column_name) || ' ' || '=' || ' ' || row.column_name || '::' || row.data_type ;
EXECUTE 'ALTER TABLE staging.' || row.table_name || ' ' || 'DROP COLUMN ' || row.column_name ;
execute 'ALTER TABLE staging.' || row.table_name || ' ' || 'RENAME COLUMN '|| concat('new_',row.column_name) || ' ' || 'TO ' || row.column_name;
END LOOP;
RETURN;
END;
$$ LANGUAGE plpgsql;
For optimization, you can check if the data type needs to be changed or if you already have the correct type and size.
For this purpose you can use a query like this:
SELECT table_schema, table_name, column_name, data_type,
column_default, character_maximum_length, numeric_precision
FROM information_schema.columns
WHERE table_schema = 'staging'
AND table_name = row.table_name
AND column_name = row.column_name
and then check if "data_type" AND "numeric_precision" is different, in this case you can alter the column.
You can also consider to not create a new column, but you can use:
ALTER TABLE table_name
ALTER COLUMN column_name [SET DATA] TYPE new_data_type;
instead of:
EXECUTE 'ALTER TABLE staging.' || row.table_name || ' ' || 'ADD COLUMN ' || concat('new_',row.column_name) || ' ' || row.data_type ;
EXECUTE 'UPDATE staging.' || row.table_name || ' ' || 'SET ' || concat('new_',row.column_name) || ' ' || '=' || ' ' || row.column_name || '::' || row.data_type ;
EXECUTE 'ALTER TABLE staging.' || row.table_name || ' ' || 'DROP COLUMN ' || row.column_name ;
execute 'ALTER TABLE staging.' || row.table_name || ' ' || 'RENAME COLUMN '|| concat('new_',row.column_name) || ' ' || 'TO ' || row.column_name;
you can use this:
EXECUTE 'ALTER TABLE staging.' || row.table_name || ' ' || 'ALTER COLUMN '|| row.column_name || ' ' || 'TYPE ' || || row.data_type;

db2 Why am I getting a "[SQL0802] Data conversion of data mapping error" exception?

I work on a C# project and i created an sql procedure on Iseries (AS400) and Iam getting the following error.
An error occurred while processing the results. - [SQL0802] Data conversion or data mapping error.
This is the code of sql procedure. Can anyone have an idea to help me with this. The scenario is that the usercode, applicationcategory and isfavorite getting as parameters.
Thanks
BEGIN
DECLARE STRING_QUERY VARCHAR ( 2000 ) ;
DECLARE C1 CURSOR WITH RETURN TO CLIENT FOR SQL_STATEMENT ;
IF ISFAVORITE = '0' THEN
SET STRING_QUERY = 'SELECT A.APPLICATIONCATEGORY,C.FOLDEROFPROJECT,' ||
'C.PROJECT,D.PROJECTGRDESCRIPTION,D.PROJECTENDESCRIPTION,' ||
'D.OBJECTNAME,D.OBJECTTYP,' ||
'C.FAVORITE FROM ERPLIB.APPLICATIONDEPARTMENT A ' ||
'INNER JOIN ERPLIB.APPLICATIONUSERDEPARTMENTAUTHIRITY B ' ||
'ON B.USERCODE = ' || P_USERCODE ||
' AND B.APPLICATIONCATEGORY = A.APPLICATIONCATEGORY' ||
' INNER JOIN ERPLIB.APPLICATIONUSERMODULEAUTHORITY C ' ||
'ON C.USERCODE = ' || P_USERCODE ||
' AND C.APPLICATIONCATEGORY = A.APPLICATIONCATEGORY' ||
' AND C.FAVORITE = ''0''' ||
' INNER JOIN ERPLIB.APPLICATIONMODULE D ' ||
'ON D.APPLICATIONCATEGORY = A.APPLICATIONCATEGORY' ||
' AND D.FOLDEROFOBJECT = C.FOLDEROFPROJECT AND ' ||
'D.PROJECT = C.PROJECT ' ||
'WHERE A.APPLICATIONCATEGORY = ''001''' ;
END IF ;
PREPARE SQL_STATEMENT FROM STRING_QUERY ;
OPEN C1 ;
RETURN ;

How can i turn this pl/sql into a procedure

I had to write this query for an assignement. So we have a database and we are pulling information from it, this is going to work with some back end c# eventually. Is there anything i can do , knowing im going to reuse this, in order to make it better and more adaptable when the day comes when i have to connect it all.
set serveroutput on
DECLARE
LV_DATE HVK_RESERVATION.RESERVATION_START_DATE%TYPE;
LV_SERV VARCHAR(100);
CURSOR LCUR_RES IS
SELECT *
FROM HVK_RESERVATION R
INNER JOIN HVK_PET_RESERVATION PR
ON R.RESERVATION_NUMBER = PR.RES_RESERVATION_NUMBER
INNER JOIN HVK_PET P
ON P.PET_NUMBER = PR.PET_PET_NUMBER
INNER JOIN HVK_OWNER OW
ON OW.OWNER_NUMBER = P.OWN_OWNER_NUMBER
WHERE R.RESERVATION_START_DATE < LV_DATE
AND R.RESERVATION_END_DATE > LV_DATE;
CURSOR LCUR_SERVICE(PET_RES_NUM NUMBER) IS
SELECT *
FROM HVK_SERVICE S
INNER JOIN HVK_PET_RESERVATION_SERVICE PRS
ON PRS.SERV_SERVICE_NUMBER = S.SERVICE_NUMBER
AND PRS.PR_PET_RES_NUMBER = PET_RES_NUM;
BEGIN
LV_DATE := TO_DATE('&logdate', 'yy-mm-dd');
DBMS_OUTPUT.PUT_LINE('Kennel log for ' || '' || LV_DATE);
DBMS_OUTPUT.PUT_LINE('-------------------------------');
FOR I IN LCUR_RES LOOP
DBMS_OUTPUT.PUT_LINE('Run:' || '' || I.RUN_RUN_NUMBER || ' ' ||
'Pet: ' || '' || I.PET_NAME || ' ' ||
I.OWNER_LAST_NAME || ' Pet Reservation: ' || '' ||
I.PET_RES_NUMBER);
DBMS_OUTPUT.PUT_LINE('Reservation start/end ' || ' ' ||
I.RESERVATION_START_DATE || ' ' ||
I.RESERVATION_END_DATE);
DBMS_OUTPUT.PUT('Services : ');
FOR X IN LCUR_SERVICE(I.PET_RES_NUMBER) LOOP
DBMS_OUTPUT.PUT(X.SERVICE_DESCRIPTION || ' ');
END LOOP;
DBMS_OUTPUT.PUT_LINE('');
FOR LREC_LOG IN (SELECT *
FROM HVK_KENNEL_LOG KL
WHERE KL.PR_PET_RES_NUMBER = I.PET_RES_NUMBER
) LOOP
DBMS_OUTPUT.PUT_LINE('Notes: ' || '' ||
LREC_LOG.KENNEL_LOG_SEQUENCE_NUMBER || ' ' ||
'Log Note: ' || '' || LREC_LOG.KENNEL_LOG_NOTES);
END LOOP;
DBMS_OUTPUT.PUT_LINE(' ');
END LOOP;
END;
It it supposed to output the run number , reservation number , pet name , and any relate notes.
you can replace DECLARE with CREATE OR REPLACE PROCEDURE my_proc(in_logdate in date) IS.
in that case my_proc will be the name of your procedure.
you should also use a parameter instead of &logdate
so e.g. parameter name in_logdate of type date
...
LV_DATE := in_logdate;
...

Sanitize user input with the USING keyword in PL/pgSQL

This is how I create my search_term:
IF char_length(search_term) > 0 THEN
order_by := 'ts_rank_cd(textsearchable_index_col, to_tsquery(''' || search_term || ':*''))+GREATEST(0,(-1*EXTRACT(epoch FROM age(last_edited)/86400))+60)/60 DESC';
search_term := 'to_tsquery(''' || search_term || ':*'') ## textsearchable_index_col';
ELSE
search_term := 'true';
END IF;
I am having some trouble with a PLPGSQL function:
RETURN QUERY EXECUTE '
SELECT
*
FROM
articles
WHERE
$1 AND
' || publication_date_query || ' AND
primary_category LIKE ''' || category_filter || ''' AND
' || tags_query || ' AND
' || districts_query || ' AND
' || capability_query || ' AND
' || push_notification_query || ' AND
' || distance_query || ' AND
' || revision_by || ' AND
' || publication_priority_query || ' AND
' || status_query || ' AND
is_template = ' || only_templates || ' AND
status <> ''DELETED''
ORDER BY ' || order_by || ' LIMIT 500'
USING search_term;
END; $$;
returns ERROR:
argument of AND must be type boolean, not type text at character 64
As opposed to:
RETURN QUERY EXECUTE '
SELECT
*
FROM
articles
WHERE
' || search_term || ' AND
' || publication_date_query || ' AND
primary_category LIKE ''' || category_filter || ''' AND
' || tags_query || ' AND
' || districts_query || ' AND
' || capability_query || ' AND
' || push_notification_query || ' AND
' || distance_query || ' AND
' || revision_by || ' AND
' || publication_priority_query || ' AND
' || status_query || ' AND
is_template = ' || only_templates || ' AND
status <> ''DELETED''
ORDER BY ' || order_by || ' LIMIT 500';
END; $$;
... which works. Am I missing something?
My goal is to sanitize my user input.
If some of your input parameters can be NULL or empty and should be ignored in this case, you best build your whole statement dynamically depending on user input - and omit respective WHERE / ORDER BY clauses completely.
The key is to handle NULL and empty string correctly, safely (and elegantly) in the process. For starters, search_term <> '' is a smarter test than char_length(search_term) > 0. See:
Best way to check for "empty or null value"
And you need a firm understanding of PL/pgSQL, or you may be in over your head. Example code for your case:
CREATE OR REPLACE FUNCTION my_func(
_search_term text = NULL -- default value NULL to allow short call
, _publication_date_query date = NULL
-- , more parameters
)
RETURNS SETOF articles AS
$func$
DECLARE
sql text;
sql_order text; -- defaults to NULL
BEGIN
sql := concat_ws(' AND '
,'SELECT * FROM articles WHERE status <> ''DELETED''' -- first WHERE clause is immutable
, CASE WHEN _search_term <> '' THEN '$1 ## textsearchable_index_col' END -- ELSE NULL is implicit
, CASE WHEN _publication_date_query <> '' THEN 'publication_date > $2' END -- or similar ...
-- , more more parameters
);
IF search_term <> '' THEN -- note use of $1!
sql_order := 'ORDER BY ts_rank_cd(textsearchable_index_col, $1) + GREATEST(0,(-1*EXTRACT(epoch FROM age(last_edited)/86400))+60)/60 DESC';
END IF;
RETURN QUERY EXECUTE concat_ws(' ', sql, sql_order, 'LIMIT 500')
USING to_tsquery(_search_term || ':*') -- $1 -- prepare ts_query once here!
, _publication_date_query -- $2 -- order of params must match!
-- , more parameters
;
END
$func$ LANGUAGE plpgsql;
I added default values for function parameters, so you can omit params that don't apply in the call. Like:
SELECT * FROM my_func(_publication_date_query => '2016-01-01');
More:
Functions with variable number of input parameters
The forgotten assignment operator "=" and the commonplace ":="
Note the strategic use of concat_ws(). See:
How to concatenate columns in a Postgres SELECT?
Here is a related answer with lots of explanation:
Test for null in function with varying parameters

Oracle PL/SQL cursor update

I'm using oracle. My SQL skills are very bad, I want to update information from a query that I have obtained through the use of a cursor, I've read about using the WHERE CURRENT OF statement, but I don't see how that can fit into my current code. Does anyone mind lending a helping hand? I want to allow a calling program to update a row in the cursor (I want to update the race location) returned by the query in my current code. Here's my code so far:
DECLARE
l_race_rec race%rowtype;
CURSOR Query1
IS
SELECT *
FROM RACE
WHERE Race_Time='22-SEP-14 12.00.00.000000000';
BEGIN
OPEN Query1;
LOOP
FETCH query1 INTO l_race_rec;
EXIT WHEN query1%notfound;
dbms_output.put_line( l_race_rec.raceid || ', ' || l_race_rec.race_location || ', ' ||
l_race_rec.race_type || ', ' || l_race_rec.race_time || ', ' || l_race_rec.sex || ', ' ||
l_race_rec.minage || ', ' || l_race_rec.maxage );
END LOOP;
CLOSE Query1;
END;
Here's an example to get you going:
DECLARE
l_race_rec race%rowtype;
CURSOR Query1 IS
SELECT *
FROM RACE
WHERE Race_Time = '22-SEP-14 12.00.00.000000000';
nSome_value NUMBER := 42;
BEGIN
OPEN Query1;
LOOP
FETCH query1 INTO l_race_rec;
EXIT WHEN query1%notfound;
dbms_output.put_line(l_race_rec.raceid || ', ' ||
l_race_rec.race_location || ', ' ||
l_race_rec.race_type || ', ' ||
l_race_rec.race_time || ', ' ||
l_race_rec.sex || ', ' ||
l_race_rec.minage || ', ' ||
l_race_rec.maxage );
UPDATE RACE
SET SOME_FIELD = nSome_value
WHERE CURRENT OF QUERY1;
END LOOP;
CLOSE Query1;
END;
Share and enjoy.
Why don't you use a cursor for loop.
...
for row in query1
loop
dbms_output.put_line(row.raceid || ', ' ||
row.race_location || ', ' ||
row.race_type || ', ' ||
row.race_time || ', ' ||
row.sex || ', ' ||
row.minage || ', ' ||
row.maxage );
UPDATE RACE
SET SOME_FIELD = nSome_value
WHERE CURRENT OF QUERY1;
end loop;
...
In this way there no need to open and to close a cursor.
Keep in mind that a cursor for loop works better for a cursor with more than 1 row as result.
Good luck.