ODBC ARD and APD management on SQLExecDirect and SQLExecute - odbc

I got the following code:
SQLRETURN ret;
SQLHSTMT stmt, stmt2;
SQLHDESC Ard, Apd;
/* Alloc code here */
SQLExecDirect(stmt, query, SQL_NTS);
SQLExecDirect(stmt2, query2, SQL_NTS);
/* Binding Column in stmt */
SQLGetStmtAttr(stmt, SQL_ATTR_APP_ROW_DESC, &Ard, 0, NULL);
SQLGetStmtAttr(stmt2, SQL_ATTR_APP_PARAM_DESC, &Apd, 0, NULL);
SQLCopyDesc(Ard, Apd);
/* Do stuff to copy into stmt2 table stmt table */
SQLExecDirect(stmt, query3, SQL_NTS); //select another table
/* Do I need here to rebind the APD on the ARD with SQLCopyDesc()*/
And I don't understand what is happening to the ARD of stmt after SQLExecDirect the second time.
Do the binded columns are unbinded from the Statement? Or the binded column stay binded?
More simply, does SQLExecDirect or SQLExecute reset the ARD, APD, IPD, IRD of the Statement?
And what happend if another statement is linked with one of this Descriptor with SQLSetStmtAttr?

Related

How to Raise exceptions for a failed condition used in select statement of cursor

I am a plsql beginner, I found that i am not getting any error if i give an invalid data into select statement of cursor.
I want to raise an exception if the given data for condition not matches with data in table.Could anyone help me in how to check the condition in the begin block to raise exception.
You are not able to add any exception handling to the SQL within the Cursor. Based on the limited knowledge gained from reading your question I have come up with the below.
My example shows you how to work with a Cursor and how to Declare your own Exceptions. I'm using a CTE to create the fake data. The Exception I am checking for are names that are too long. When the Cursor pulls back first names that exceed 20 characters the too_long custom exception is raised.
I add the data that is pulled back from the Cursor to a collection. Then, using an IF statement I check for the character lengths of the first names. If any First Names are found to be greater than 20 characters Raise the too_long Exception.
DECLARE
/* Declare Exception */
too_long EXCEPTION;
/* Declare Cursor */
CURSOR cur_my_fake_data IS
WITH fake_data AS
(
SELECT 1001 AS p_key, 'Jacob Brian Anderson the First' AS f_name FROM dual UNION ALL
SELECT 1021 AS p_key, 'Bob' AS f_name FROM dual UNION ALL
SELECT 1025 AS p_key, 'Ryan' AS f_name FROM dual
)
SELECT p_key, f_name
FROM fake_data
;
TYPE t_my_fake_data IS TABLE OF cur_my_fake_data%ROWTYPE; /* Declare Type based off of Cursor */
c_my_fake_data t_my_fake_data := t_my_fake_data(); /* Declare Collection and initialize it */
BEGIN
/* Open Cursor and fill collection with data from Cursor */
OPEN cur_my_fake_data;
FETCH cur_my_fake_data BULK COLLECT INTO c_my_fake_data; /* Fill Collection with data from Cursor */
CLOSE cur_my_fake_data;
/* Loop thru collection */
FOR r IN c_my_fake_data.FIRST .. c_my_fake_data.LAST
LOOP
IF LENGTH(c_my_fake_data(r).f_name) > 20 THEN
dbms_output.put_line('LENGTH: ' || LENGTH(c_my_fake_data(r).f_name));
RAISE too_long;
END IF;
END LOOP;
EXCEPTION
WHEN too_long THEN
dbms_output.put_line('More Than 20 Characters');
WHEN OTHERS THEN
DBMS_OUTPUT.put_line('Error '||TO_CHAR(SQLCODE)||': '||SQLERRM);
CLOSE cur_my_fake_data;
END
;

Error while compiling the package in PL/SQL. Error(7,1): PLS-00103: Encountered the symbol "CREATE"

Error(7,1): PLS-00103: Encountered the symbol "CREATE". I tried to put
/ before create but then error was Error(6,1): PLS-00103: Encountered
the symbol "/" .
I am new to PL/SQL programming, could you please help on this.
CREATE OR REPLACE PACKAGE EMP_BULK_INSERT AS
PROCEDURE Bulk_Insert;
END EMP_BULK_INSERT;
/* package body */
CREATE OR REPLACE PACKAGE BODY EMP_BULK_INSERT AS
PROCEDURE Bulk_Insert
AS
/* select all records from source table */
CURSOR kt_test_cur IS
SELECT empid
, empband
, empname
, workexp
, salary
from kt_test;
/* create nested table type and variable that will hold BIG_TABLE's records */
TYPE kt_test_ntt IS TABLE OF kt_test_cur%ROWTYPE;
l_kt_test kt_test_ntt;
BEGIN
/* open pointer to SELECT statement */
OPEN kt_test_cur;
/* collect data in the collection */
FETCH kt_test_cur BULK COLLECT INTO l_kt_test;
/* close the pointer */
CLOSE kt_test_cur;
/* print size of the collection */
DBMS_OUTPUT.PUT_LINE('Nested table holds: ' || TO_CHAR(l_kt_test.COUNT) || ' records.');
/* write data down to target table */
FORALL indx IN l_kt_test.FIRST..l_kt_test.LAST
INSERT INTO kt1_test(empid,empband,empname,workexp,salary)
VALUES (l_kt_test(indx).empid,l_kt_test(indx).empband,l_kt_test(indx).empname,l_kt_test(indx).workexp,l_kt_test(indx).salary);
DBMS_OUTPUT.PUT_LINE('Number of rows inserted ' || SQL%ROWCOUNT || ' rows');
COMMIT;
END Bulk_Insert;
END EMP_BULK_INSERT;
The "/" needs to be on a blank line, all by itself.
Like this:
CREATE OR REPLACE PACKAGE abc AS
...
END;
/
CREATE OR REPLACE PACKAGE BODY abc AS
...
END;
/
The meaning of the "/" is to execute the command buffer. In this case it executes the previous PL/SQL block.
For a more in depth discussion on this topic, see here on
stack overflow
If you want to manipulate the package vie the dedicated object viewa of SQL Developer you have to separate package def and body and use the "/" in neither:
First compile the package spec (without "/"). Then open the body in a separate tab page with the small package icon (left from the compile icon). Edit your body code and compile there (again, without the "/").

How to get value from nested table in pl sql table

I have created nested table as follow:
CREATE OR REPLACE TYPE EMP_NO_NAME
AS OBJECT
(
EMPNO NUMBER(4),
ENAME VARCHAR2(20),
JOB VARCHAR2(20),
MGR NUMBER(5),
HIREDATE DATE,
SAL NUMBER(7,2)
);
CREATE OR REPLACE TYPE EMP_TABLE IS TABLE OF EMP_NO_NAME;
-----------------------
CREATE TABLE NESTED_EMP
(
DEPTNO NUMBER(2) ,
EMPLOYEE EMP_TABLE
)
NESTED TABLE EMPLOYEE STORE AS NESTED_EMPLOYEE;
INSERT INTO NESTED_EMP (DEPTNO,EMPLOYEE)
VALUES (10,EMP_TABLE(EMP_NO_NAME(7839,'KING','PRESIDENT',NULL,'17-NOV-81',5000),
EMP_NO_NAME(7782,'CLARK','MANAGER',7839,'09-JUN-81',2450),
EMP_NO_NAME(7934,'MILLER','CLERK',7782,'23-JAN-82',1300)
)
);
INSERT INTO NESTED_EMP (DEPTNO,EMPLOYEE)
VALUES (20,EMP_TABLE(EMP_NO_NAME(7566,'JONES','MANAGER',7839,'02-APR-81',2975),
EMP_NO_NAME(7902,'FORD','ANALYST',7566,'03-DEC-81',3000),
EMP_NO_NAME(7369,'SMITH','CLERK',7902,'17-DEC-80',800),
EMP_NO_NAME(7788,'SCOTT','ANALYST',7566,'09-DEC-82',3000),
EMP_NO_NAME(7876,'ADAMS','CLERK',7788,'12-JAN-83',1100)
)
);
INSERT INTO NESTED_EMP (DEPTNO,EMPLOYEE)
VALUES (20,EMP_TABLE(EMP_NO_NAME(7698,'BLAKE','MANAGER',7839,'01-MAY-81',2850),
EMP_NO_NAME(7654,'MARTIN','SALESMAN',7698,'28-SEP-81',1250),
EMP_NO_NAME(7499,'ALLEN','SALESMAN',7698,'20-FEB-81',1600),
EMP_NO_NAME(7844,'TURNER','SALESMAN',7698,'08-SEP-81',1500),
EMP_NO_NAME(7900,'JAMES','CLERK',7698,'03-DEC-81',950),
EMP_NO_NAME(7521,'WARD','SALESMAN',7698,'22-FEB-81',1250)
)
);
Now I getting the value of nested table in plsql:
DECLARE
CURSOR EMPLOYEE IS
select p.* from NESTED_EMP p1 ,table(p1.employee) p;
V_EMP EMP_TABLE;
BEGIN
FOR V_EMP IN EMPLOYEE
LOOP
EXIT WHEN EMPLOYEE%NOTFOUND;
END LOOP;
FOR MYINDEX IN V_EMP.FIRST..V_EMP.LAST
LOOP
DBMS_OUTPUT.PUT_LINE(V_EMP(MYINDEX).ENAME);
END LOOP;
END;
/
END;
Error report:
ORA-06531: Reference to uninitialized collection ORA-06512: at line 10
06531. 00000 - "Reference to uninitialized collection"
*Cause: An element or member function of a nested table or varray
was referenced (where an initialized collection is needed)
without the collection having been initialized.
*Action: Initialize the collection with an appropriate constructor
or whole-object assignment.
How to get nested table value in plsql table ?
The problem with your code is that V_EMP is not actually of type EMP_TABLE. Rather, it's an EMPLOYEE.ROWTYPE. When you initialize a cursor for loop, the variable is automatically made an appropriate ROWTYPE, overriding any previous declarations.
The good news is that, since you've already referenced the nested table in the query, you don't need to do so in the loop (it's already been exploded). Your PL/SQL can be vastly simplified:
DECLARE
CURSOR employee IS
SELECT p.*
FROM nested_emp p1 CROSS JOIN TABLE (p1.employee) p;
BEGIN
FOR v_emp IN employee LOOP
DBMS_OUTPUT.put_line (v_emp.ename);
END LOOP;
END;
/
You'll notice the EXIT WHEN was removed as well. A cursor for loop terminates automatically after the last record.
An alternative would be to not explode the nested table in the query. Then you would need two loops:
DECLARE
CURSOR employee IS
SELECT p.*
FROM nested_emp p;
BEGIN
FOR v_emp IN employee LOOP
for i in v_emp.employee.first..v_emp.employee.last loop
DBMS_OUTPUT.put_line (v_emp.employee(i).ename);
end loop;
END LOOP;
END;
/

Comparing column in PL/SQL

I have 2 tables, Driver and Mechanic and in both table they have a same column Employee#
How do i check both tables using PL/SQL so that an employee in Driver table cannot appear in the Mechanic table by comparing the employee#. And if it happens, it would display a message "Employee# cant be both driver and mechanic!"
I do know that i could simply just compare both table using:
SELECT Employee#
FROM Driver
INTERSECT
SELECT Employee#
FROM Mechanic
But its a requirement for me to use PL/SQL.
I've tried using cursor but i cant seem to make it run through the entire column. Here are my code:
declare
cursor c1 is select employee# from driver;
cursor c2 is select employee# from mechanic;
driverenum number(30);
mechanicenum number(30);
begin
open c1;
fetch c1 into driverenum;
close c1;
open c2;
fetch c2 into mechanicenum;
close c2;
if driverenum in (mechanicenum) then
dbms_output.put_line(driverenum);
end if;
end;
/
If the only requirement is that you use PL/SQL
DECLARE
TYPE emp_nt IS TABLE OF driver.employee#%type;
l_drivers emp_nt;
l_mechanics emp_nt;
l_both emp_nt;
BEGIN
SELECT employee#
BULK COLLECT INTO l_drivers
FROM driver;
SELECT employee#
BULK COLLECT INTO l_mechanics
FROM mechanic;
l_both := l_drivers MULTISET INTERSECT l_mechanics;
FOR i IN 1 .. l_both.count
LOOP
dbms_output.put_line( 'Employee ' || l_both(i) ||
' is employed as both a driver and a mechanic' );
END LOOP;
END;
This approach would potentially occupy quite a bit of space in the PGA if there are a large number of rows in either table. But normally it would be almost as efficient as the SQL solution.

How to drop multiple databases based on a prefix string

example_ or e_
I want to drop all databases that match the prefix e_, so that e_database1, e_database2 and so forth are dropped.
Commands that do not work:
mysql drop database e_%
mysql drop database e_*
I'm not looking for all the tables in a given database, but all the databases in a given MySQL server.
You could do this with a stored proc like this:
/* Start stored proc */
DELIMITER //
DROP PROCEDURE IF EXISTS db_clean_up //
CREATE PROCEDURE db_clean_up
(
)
BEGIN
declare done bit default false;
declare deleted varchar(255);
-- Drop DBs
DECLARE cur1 CURSOR FOR SELECT
SCHEMA_NAME
FROM information_schema.SCHEMATA
WHERE SCHEMA_NAME LIKE 'db_prefix%';
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN cur1;
createLoop: LOOP
FETCH cur1 INTO deleted;
IF done THEN
LEAVE createLoop;
END IF;
SET #query = CONCAT('DROP DATABASE `', deleted, '`;');
PREPARE stmt1 FROM #query;
EXECUTE stmt1;
END LOOP createLoop;
CLOSE cur1;
END //
delimiter ;
/* End stored proc */

Resources