This is my stored procedure:
nzsql -u user -pw pass -c "CREATE OR REPLACE PROCEDURE INSERT_LOGIC(varchar(50),varchar(20),varchar(40)) RETURNS BOOL LANGUAGE NZPLSQL AS BEGIN_PROC
DECLARE
t1 ALIAS FOR $1;
t2 ALIAS FOR $2;
t3 ALIAS FOR $3;
BEGIN
INSERT INTO ABC..XYZ
(select '$t1','$t2','$t3' from ABC..PQR limit 10);
END;
END_PROC;"
The ALIAS FOR is the only way I found on the internet to do this but I get the following error:
NOTICE: plpgsql: ERROR during compile of INSERT_LOGIC near line 3
ERROR: syntax error, unexpected ERROR, expecting VARIABLE or WORD at or near "t1Stuff"
How do I access the three "varchar variables" that I pass to the stored procedure inside the same?
Here is an example similar to your requirement and its working. I am using two tables 'tab1' and 'tab2' with following description:
$ nzsql -d test -c "\d tab1"
Table "TAB1"
Attribute | Type | Modifier | Default Value
-----------+---------------+----------+---------------
COL1 | INTEGER | |
COL2 | CHARACTER(10) | |
COL3 | INTEGER | |
Distributed on hash: "COL1"
$ nzsql -d test -c "\d tab2"
Table "TAB2"
Attribute | Type | Modifier | Default Value
-----------+---------------+----------+---------------
C1 | INTEGER | |
C2 | CHARACTER(10) | |
C3 | INTEGER | |
Distributed on hash: "C1"
Following is the stored procedure code that I used:
CREATE OR REPLACE PROCEDURE INSERT_LOGIC(varchar(50),varchar(20),varchar(40))
RETURNS BOOL
LANGUAGE NZPLSQL
AS
BEGIN_PROC
DECLARE
num_args int4;
sql char(100);
t1 ALIAS FOR $1;
t2 ALIAS FOR $2;
t3 ALIAS FOR $3;
BEGIN
num_args := PROC_ARGUMENT_TYPES.count;
RAISE NOTICE 'Number of arguments: %', num_args;
sql := 'INSERT INTO tab2 SELECT ' || t1 || ',' || t2 || ',' || t3 || ' FROM tab1 LIMIT 10 ';
RAISE NOTICE 'SQL Statement: %', sql;
EXECUTE IMMEDIATE sql;
END;
END_PROC;
Hope this will help!
You're attempting to reference variables by putting a $ in front of the name, which is not valid.
Look at the example in the docs.
DECLARE
logtxt ALIAS FOR $1;
curtime timestamp;
BEGIN
curtime := 'now()';
INSERT INTO logtable VALUES (logtxt, curtime);
RETURN curtime;
END
You should try
INSERT INTO ABC..XYZ
(select t1, t2, t3 from ABC..PQR limit 10);
Though it's possible that the column values won't resolve when used this way. If not, build a dynamic statement and execute it instead.
declare sql varchar;
sql := 'insert into abc..xyz select ' || t1 || ',' || t2 || ',' || t3 || ' from abc..pqr limit 10;'
execute immediate sql;
If you're passing values, not column names, as parameters:
declare sql varchar;
sql := 'insert into abc..xyz select ''' || t1 || ''',''' || t2 || ''',''' || t3 || ''' from abc..pqr limit 10;'
execute immediate sql;
Related
I have a db2 table DBTable with columns A, B, C (all of type varchar) which is linked to a library lib in SAS.
I use SAS to generate a dataset ValuesForA with one column whose content I want to write into the column A of DBTable with the additional requirement that the the column for B is filled with ' ' (blank) and the column for C with (null). So the DBTable should look something like this:
| A | B | C |
======================
| 'x' | ' ' | (null) |
| 'y' | ' ' | (null) |
| 'z' | ' ' | (null) |
I cannot find a way how to acchieve this as SAS as it treats blanks as null.
The simple approach specifying B as " " just fills this column with (null). I also tried to use the nullchar=no option and not specifying a value for C:
proc sql;
insert into lib.DBTable
(nullchar=no, A, B)
select
A, " " as B
from ValuesForA;
quit;
however the column C is then also filled with blanks
| A | B | C |
===================
| 'x' | ' ' | ' ' |
| 'y' | ' ' | ' ' |
| 'z' | ' ' | ' ' |
Try this:
proc sql;
insert into lib.DBTable
select
A, " " as B, null as C
from ValuesForA;
quit;
This gave me the results you requested using a DB2 temp table with three VARCHAR columns.
I posted the same question on communities.sas.com.
In the end, I used a solution proposed by s_lassen (you may check out his other one).
I give my own description of his solution here:
It seems that inserting blanks and nulls is not possible over the linked libraies. We can, however, write a SAS-program that produces an sql-statement which we can execute on the database server itself using pass-through sql.
This is all done by this little script:
/* *** Create a temporary document with the name "tempsas" *** */
Filename tempsas temp;
/* *** Loop throuhg ValuesForA and write into tempsas. *** */
/* (The resulting tempsas is basically an SQL insert statement. All observations are written in one big values statement) */
Data _null_;
file tempsas;
set ValuesForA end=done;
if _N_=1 then put
'rsubmit;' /
'proc sql;' /
' Connect to DB2(<connect options>);' /
' execute by DB2(' /
' insert into DBTable(A, B, C)' /
' values'
;
put
" ('" a +(-1) "', ' ', null)" #; /* "a" is an observation ValuesForA.a
"+(-1)" removes the trailing blank set by the put statement
"#" writes the next put statement in the same line */
if done then put
/
' );' /
'quit;' /
'endrsubmit;'
;
else put ',';
run;
/* *** Run the code in tempsas *** */
%include tempsas;
It creates a file "tempsas" in which it writes and executes the following code:
rsubmit;
proc sql;
Connect to DB2(<connect options>);
execute by DB2(
insert into DBTable(A, B, C)
values
('x', ' ', null),
('y', ' ', null),
('z', ' ', null)
);
quit;
endrsubmit;
I guess this solution is only feasible if there are not too many values to be inserted to the database.
I want to write dynamic query from update statement to merge merge statement
'UPDATE ' || T1_TABLENAME || ' t1 ' || 'SET ( ' ||
v_t1_fields || ' ) = (SELECT ' || v_t2_fields || ' FROM ' ||
T2_TEMPTABLE_NAME || ' tmp WHERE ' || v_con || ' ) ' ||
' WHERE EXISTS ( SELECT 1 FROM ' || T2_TEMPTABLE_NAME ||
' tmp WHERE ' || v_con || ' )';
-- HERE v_con = t1.D=t2.D,
v_t1_fields-it can store dynamically-A,B,C
v_t2_fields-it can store dynamically-A,B,C
------------
MERGE INTO TABLE1 t1
USING TABLE2 t2
ON(t1.D=t2.D)
WHEN MATCHED THEN
UPDATE
SET
t1.A=t2.A,
t1.B=t2.B, //update set (t1.A,t1.B,t1.C =t2.A,t2.B,t2.C) not work
t1.C=t2.C;
------------------
'MERGE INTO ' TABLE1 || ' t1 ' ||
' USING ' TABLE2 || ' t2 ' ||
' ON ( ' || v_cons || ' )
when matched then update set ('
|| v_t1_fields || ') = '( || v_t2_fields || ' );' // Its not work--ORA-01747: invalid user.table.column, table.column, or column specification
then i use reg_exp to split the columns
---------------------------
v_Sql := 'MERGE INTO ' TABLE1 || ' t1 ' ||
' USING ' TABLE2 || ' t2 ' ||
' ON ( ' || v_cons || ' )
when matched then update set ('
( ' || regexp_substr(v_t1_fields, '[^,]+', 1, 1) || ' ) = ( ' || regexp_substr(v_t2_fields, '[^,]+', 1, 1) || ' )
( ' || regexp_substr(v_t1_fields, '[^,]+', 1, 2) || ' ) = ( ' || regexp_substr(v_t2_fields, '[^,]+', 1, 2) || ' )
( ' || regexp_substr(v_t1_fields, '[^,]+', 1,3) || ' ) = ( ' || regexp_substr(v_t2_fields, '[^,]+', 1,3) || ' ) ';
this one also not work ----ORA-01747: invalid user.table.column, table.column, or column specification
dynamic update to dynamic merge
while changing merge with update statement
not working
There are few mistakes in concatenation and usage of the MERGE statement.
Try the following dynamic query:
'MERGE INTO '
|| TABLE1
|| ' t1 '
|| ' USING '
|| TABLE2
|| ' t2 '
|| ' ON ( '
|| V_CONS
|| ' ) when matched then update set '
|| REGEXP_SUBSTR(V_T1_FIELDS, '[^,]+', 1, 1)
|| ' = '
|| REGEXP_SUBSTR(V_T2_FIELDS, '[^,]+', 1, 1)
|| ', '
|| REGEXP_SUBSTR(V_T1_FIELDS, '[^,]+', 1, 2)
|| ' = '
|| REGEXP_SUBSTR(V_T2_FIELDS, '[^,]+', 1, 2)
|| ', '
|| REGEXP_SUBSTR(V_T1_FIELDS, '[^,]+', 1, 3)
|| ' = '
|| REGEXP_SUBSTR(V_T2_FIELDS, '[^,]+', 1, 3);
I am assuming that
TABLE1 and TABLE2 are valid table names
V_CONS is a valid condition on tables
V_T1_FIELDS and V_T2_FIELDS contains valid column names of the table
Cheers!!
I am basically doing row counts of tables with same names between 2 different databases.
Our sql script is something like this:
select (select count(1) from source.abc#remotedb) - (select count(1) from target.bcd) from dual;
we have almost 2000 scripts similar to above.
and the output is like following:
select count(1) from source.abc#remotedb) - (select count(1) from target.abc
----------------------------------------------------------------------------
0
select count(1) from source.opo#remotedb) - (select count(1) from target.opo
----------------------------------------------------------------------------
26
select count(1) from source.asd#remotedb) - (select count(1) from target.asd
----------------------------------------------------------------------------
-95
Now using using bash/shell scripting i want to print the output to a separate file of only those three lines where the numeric value is NOT equal to 0.
Example:
$ cat final_result.txt
select count(1) from source.opo#remotedb) - (select count(1) from target.opo
----------------------------------------------------------------------------
26
select count(1) from source.asd#remotedb) - (select count(1) from target.asd
----------------------------------------------------------------------------
-95
grep -E -B1 '\-{0,1}[1-9][0-9]*' fileinput > final_result.txt
-B1: one line Before the matched line
Maybe something like
egrep -B3 '^\-*[1-9]+$' fileinput > final_result.txt
I would do like this:
cat result_of_sql | grep -v '^$' | paste - - - | awk -F"\t" '$3!=0{print $1}'
Where
grep -v '^$' get rid of empty lines
paste - - - aggregate every 3 lines into 1 with tabs
awk -F"\t" '$3!=0{print $1}' awk magic: print the expected result.
My company works with a UI Framework written in PLSQL which interacts with a Java client program.
A lot of queries need to be passed as a VARCHAR2 string to it to be called when needed.
The query
SELECT DISTINCT cod_visum
FROM tbl_visum
WHERE num_firma = 0
AND (code_visum BETWEEN 1 AND 799 OR code_visum BETWEEN 900 AND 999)
AND id_worker IS NULL
OR ( date_valid_to IS NOT NULL
AND p_date_from > NVL (date_valid_to, p_date_from + 1))
ORDER BY 1;
runs fine and returns the expected results when run from the TOAD editor, but when I pass it as a VARCHAR2 to the framework, I keep getting an ORA-00920 (Invalid relational operator) but can't find the cause. (Also the framework catches the exception and only shows me a dialog with the exception number and text >.<)
I've tried several methods of concatinating the variable p_date_from into the VARCHAR2, like
v_sql_norm VARCHAR2 (450)
:= 'SELECT DISTINCT code_visum
FROM tbl_visum
WHERE num_firma = 0
AND (code_visum BETWEEN 1 AND 799 OR code_visum BETWEEN 900 AND 999)
AND id_worker IS NULL
OR ( date_valid_to IS NOT NULL AND '
|| p_date_from
|| ' > NVL(date_valid_to , '
|| (p_date_from + 1)
|| ')) ORDER BY 1';
I already checked the date formats, tried to convert the dates to a string before concatination but everything results in the same exception.
The table used in the query looks like this:
+--------------------+-------+-------------------+
| Column Name | NULL? | DATA TYPE |
+--------------------+-------+-------------------+
| ID_VISUM_ASSIGMENT | N | NUMBER (12) |
| NUM_FIRMA | N | NUMBER (3) |
| CODE_VISUM | N | VARCHAR2 (3 Char) |
| ID_WORKER | Y | NUMBER (10) |
| DATE_VALID_FROM | Y | DATE |
| DATE_VALID_TO | Y | DATE |
+--------------------+-------+-------------------+
Side info: a visum (or visa?) is assigned to a worker starting at a specific date (date_valid_from). It can be assigned to a certain date (date_valid_to), or indefinitely assigned (date_valid_to is NULL).
Thank you in advance, really appreciate any help! :)
edit: yes, cod_visum is a VARCHAR2 but in the query it's used as NUMBER but I also already tried casting it to a number (and mostly it's casted explicitely ^^)
This bit is wrong: the fourth line mixes variable with boilerplate.
OR ( date_valid_to IS NOT NULL AND '
|| p_date_from
|| ' > NVL(date_valid_to , '
|| (p_date_from + 1)
It should be something like
OR ( date_valid_to IS NOT NULL AND '
|| p_date_from
|| ' > NVL(date_valid_to , ('
|| p_date_from
|| ' + 1)'
Also the logic seems wonky, but that's an aside.
I'm trying to increase the age of every customer by 1 and display them; using a cursor.
This is the Table structure.
Select * from customers;
+----+----------+-----+-----------+----------+
| ID | NAME | AGE | ADDRESS | SALARY |
+----+----------+-----+-----------+----------+
| 1 | Ramesh | 32 | Ahmedabad | 2000.00 |
| 2 | Khilan | 25 | Delhi | 1500.00 |
| 3 | kaushik | 23 | Kota | 2000.00 |
| 4 | Chaitali | 25 | Mumbai | 6500.00 |
| 5 | Hardik | 27 | Bhopal | 8500.00 |
| 6 | Komal | 22 | MP | 4500.00 |
Here is my code:
DECLARE
c_id customers.id%TYPE;
c_name customers.name%TYPE;
c_age customers.age%TYPE;
CURSOR c1 IS
SELECT id, name, age
FROM customers
FOR UPDATE OF salary;
BEGIN
OPEN c1;
LOOP
FETCH c1 INTO c_id, c_name, c_age;
UPDATE customers
SET age = age + 1
WHERE CURRENT OF c1;
EXIT WHEN c1%NOTFOUND;
dbms_output.put_line( c_id || ' ' || c_name || ' ' || c_age );
END LOOP;
CLOSE c1;
END;
/
However, I am getting the following error:
Error report:
ORA-01410: invalid ROWID
ORA-06512: at line 13
01410. 00000 - "invalid ROWID"
*Cause:
*Action:
1 Ramesh 32
2 Khilan 25
3 kaushik 23
4 Chaitali 25
5 Hardik 27
6 Komal 22
Why is this happening and how can I stop it?
You have your exit in the wrong place; it should be straight after the fetch. You are processing your six real rows properly, but then you have a seventh fetch - after which %notfound will be true - so there is no 'current' row to update. You need to exit before you try to do anything with that invalid row.
BEGIN
OPEN c1;
LOOP
FETCH c1 INTO c_id, c_name, c_age;
EXIT WHEN c1%NOTFOUND;
UPDATE customers
SET age = age + 1
WHERE CURRENT OF c1;
dbms_output.put_line( c_id || ' ' || c_name || ' ' || c_age );
END LOOP;
CLOSE c1;
END;
Hopefully this is just an exercise, as it's not a very efficient way to do the updates.