I am trying to fill a database with random data, but the primary key needs to still be unique. I can fill the database with random data, but now I am trying to solve the primary key issue.
When running this code I get errors.
set SERVEROUTPUT on
create or replace
PROCEDURE fillDatabase(TableIn IN varchar2, Amount IN NUMBER) IS
columnData varchar2(50);
columnNR number(10);
str varchar2(500);
sqlStatement varchar2(500);
l_ran_time TIMESTAMP;
intlol NUMBER(38);
prmname varchar2(50);
prmtab varchar2(50);
prmkey number(10);
temp number(30);
tempstr varchar2(50);
lolnr number(10);
strq varchar2(50);
BEGIN
lolnr := 1;
select count(*) into columnNR
from user_tab_columns where table_name=TableIn;
FOR counter IN 1..Amount
LOOP
sqlStatement := 'insert into '|| TableIn ||' values (';
FOR counter2 IN 1..columnNR
LOOP
SELECT cols.table_name, cols.column_name into prmtab, prmname
FROM all_constraints cons, all_cons_columns cols
WHERE cols.table_name = TableIn
AND cons.constraint_type = 'P'
AND cons.constraint_name = cols.constraint_name
AND cons.owner = cols.owner
ORDER BY cols.table_name, cols.position;
tempstr := 'select count(*) into temp from '|| prmtab;
dbms_output.put_line('test');
dbms_output.put_line(temp);
EXECUTE IMMEDIATE tempstr;
IF temp = 0
THEN
strq := 'SELECT max(' || prmname || ') into prmkey from '|| prmtab || ' order by '|| prmname;
dbms_output.put_line(strq);
EXECUTE IMMEDIATE strq;
END IF;
select dbms_random.value(0, 20) into intlol from dual;
select dbms_random.string('U', 20) into str from dual;
SELECT SYSDATE + dbms_random.value(0, SYSDATE - SYSDATE+1)
INTO l_ran_time
FROM dual;
Select DATA_TYPE INTO columnData
FROM user_tab_columns
WHERE table_name= TableIn
AND COLUMN_ID = counter2;
dbms_output.put_line(columnData);
CASE
WHEN columnData = 'VARCHAR2' THEN sqlStatement := sqlStatement ||''''|| str ||''', ';
WHEN columnData = 'NUMBER' THEN sqlStatement := sqlStatement || intlol ||', ';
WHEN columnData = 'TIMESTAMP(6)' THEN sqlStatement := sqlStatement ||''''|| l_ran_time ||''', ';
ELSE sqlStatement := sqlStatement || NULL || ', ';
END CASE;
END LOOP;
sqlStatement := SUBSTR(sqlStatement, 0, LENGTH(sqlStatement) -2);
sqlStatement := sqlStatement || ')';
dbms_output.put_line(sqlStatement);
EXECUTE IMMEDIATE sqlStatement;
END LOOP;
END fillDatabase;
Can you guys help me solve this?
use the pattern
str := 'select x from t where...';
execute immediate str into var;
instead of
str := 'select x into var from t where...';
execute immediate str;
Related
delimiter $$
CREATE OR REPLACE PROCEDURE `populate_audit_helper`(
IN in_db_name VARCHAR(100),
IN in_tbl_name VARCHAR(100),
IN in_row_id INT(10)
)
BEGIN
DROP TEMPORARY TABLE IF EXISTS t_agents;
SET #SQL := CONCAT('CREATE TEMPORARY TABLE t_agents AS SELECT * FROM `', in_tbl_name, '` WHERE row_id = ', in_row_id);
PREPARE stmt FROM #SQL;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
BEGIN
DECLARE c_data CURSOR FOR SELECT * FROM t_agents;
BEGIN
DECLARE rec ROW TYPE OF c_data;
DECLARE done INT DEFAULT FALSE;
DECLARE col CHAR(40);
DECLARE val TEXT(10000);
DECLARE c_columns CURSOR FOR
SELECT column_name
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = in_db_name
AND table_name = in_tbl_name
ORDER BY ordinal_position;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN c_columns;
OPEN c_data;
FETCH c_data INTO rec;
SET #create_list := "";
read_loop: LOOP
FETCH c_columns INTO col;
IF done THEN
LEAVE read_loop;
END IF;
-- SELECT rec.col; -- <<====
SET #create_list := CONCAT(#create_list, "'", col, "', '", rec.name, "', ");
END LOOP read_loop;
SET #SQL := CONCAT ("INSERT INTO x1 SET dynamic_cols_l1 = COLUMN_CREATE(", TRIM(TRAILING ", " FROM #create_list), ")");
PREPARE stmt FROM #SQL;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
CLOSE c_columns;
CLOSE c_data;
END;
END;
END$$
In the above procedure, i am looping through all the columns in a table and i am planning to build a query to populate a table with dynamic column.
Is there any way to substitute the value of col in the line highlighted in above procedure?
Eg. the value stored in col is 'name'. So the output required is SELECT rec.name
Update
Somehow i managed to write a dirty solution without the use of ROW TYPE and with single cursor. It works so, so far i did not find any problem. Pls guide me if there is any better way.
delimiter $$
CREATE OR REPLACE PROCEDURE `populate_audit_helper_new3`(
IN in_db_name VARCHAR(100),
IN in_tbl_name VARCHAR(100),
IN in_row_id INT(10)
)
proc: BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE col CHAR(40);
DECLARE val TEXT(10000);
DECLARE q TEXT(100000);
DECLARE c_columns CURSOR FOR
SELECT column_name
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = in_db_name
AND table_name = in_tbl_name
ORDER BY ordinal_position;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN c_columns;
SET q = "";
SET #val = "";
read_loop: LOOP
FETCH c_columns INTO col;
IF done THEN
LEAVE read_loop;
END IF;
SET q = CONCAT(q, "'", col, "','|', IFNULL(QUOTE(", col, "), ''),'$'");
END LOOP read_loop;
SET q = TRIM(TRAILING "," FROM q);
SET q = CONCAT("CONCAT(", q, ") INTO #val");
EXECUTE IMMEDIATE CONCAT("SELECT ", q , " FROM ", in_tbl_name, " WHERE row_id = ", in_row_id);
IF #val = '' THEN
LEAVE proc;
END IF;
SELECT CONCAT("'", REPLACE(#val, "|", "',"), "'") INTO #val;
SELECT REPLACE(#val, "$", ",") INTO #val;
SELECT TRIM(TRAILING ",'" FROM #val) INTO #val;
EXECUTE IMMEDIATE CONCAT("INSERT INTO x1 SET dynamic_cols_l1 = COLUMN_CREATE(", #val, ")" );
CLOSE c_columns;
END$$
As far as I've seen in mariadb documentation, you can't do it directly. There's no loop support for the ROW TYPE also. However what I would suggest to do is to leave only INFORMATION_SCHEMA.COLUMNS cursor and generate dynamic selects like
read_loop: LOOP
FETCH c_columns INTO col;
IF done THEN
LEAVE read_loop;
END IF;
SET #SQL := CONCAT('SELECT ', col, ' into #colval FROM `', in_tbl_name, '` WHERE row_id = ', in_row_id);
PREPARE stmt FROM #SQL;
execute stmt using col;
DEALLOCATE PREPARE stmt;
SET #create_list := CONCAT(#create_list, "'", col, "', '", #colval, "', ");
END LOOP read_loop;
As you're splitting only one table row it will be fine enough.
UPDATE: with additional info it can be done like that. After rethinking it:
set #SQL := 'SELECT CONCAT(';
read_loop: LOOP
FETCH c_columns INTO col;
IF done THEN
LEAVE read_loop;
END IF;
SET #SQL := CONCAT(#SQL, "'\\'", col, "'\\', \\''", col, "'\\', '");
END LOOP read_loop;
set #SQL := substring(#SQL, 1, CHAR_LENGTH(#SQL) - 3);
set #SQL := concat(#SQL, ') into #create_list FROM `', in_tbl_name, '` WHERE row_id = ', in_row_id);
PREPARE stmt FROM #SQL;
execute stmt using col;
DEALLOCATE PREPARE stmt;
#alexey Thanks for your support. I tweaked your answer a bit to add support for null and escape character. Below is the final procedure
delimiter $$
CREATE OR REPLACE PROCEDURE `populate_audit_helper_new5`(
IN in_db_name VARCHAR(100),
IN in_tbl_name VARCHAR(100),
IN in_row_id INT(10)
)
proc: BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE create_list TEXT(10000);
DECLARE col TEXT(10000);
DECLARE c_columns CURSOR FOR
SELECT column_name
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = in_db_name
AND table_name = in_tbl_name
ORDER BY ordinal_position;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN c_columns;
set #SQL := 'SELECT CONCAT(\'';
read_loop: LOOP
FETCH c_columns INTO col;
IF done THEN
LEAVE read_loop;
END IF;
SET #SQL := CONCAT(#SQL, "\\'", col, "\\'\', \', \', IFNULL(QUOTE(", col, "), \'\'), '\, ");
END LOOP read_loop;
set #SQL := substring(#SQL, 1, CHAR_LENGTH(#SQL) - 5);
set #SQL := concat(#SQL, ') into #create_list FROM `', in_tbl_name, '` WHERE row_id = ', in_row_id);
PREPARE stmt FROM #SQL;
execute stmt;
DEALLOCATE PREPARE stmt;
EXECUTE IMMEDIATE CONCAT("INSERT INTO x1 SET dynamic_cols_l1 = COLUMN_CREATE(", #create_list, ")" );
CLOSE c_columns;
END$$
I have source_table in different 22 schemas and need procedure to create for bulk collect and insert into same target table in oracle stored procedure.
I'm trying and not getting records inserted getting error ORA-00911: invalid character but there is all column from select cursor and traget_table are same in order.
CREATE OR REPLACE PROCEDURE proc_bulk_circle(p_limit IN PLS_INTEGER DEFAULT 10000,
p_activity_date IN DATE,
p_circle IN VARCHAR2) AS
CURSOR act_cur IS
SELECT activity_date,
circle
FROM circle_load_control
WHERE activity_date = p_activity_date
AND circle = circle;
TYPE type_i6 IS TABLE OF act_cur%ROWTYPE INDEX BY BINARY_INTEGER;
i_tab6 type_i6;
v_count NUMBER := 0;
lv_circle VARCHAR2(2);
lv_schema VARCHAR2(20);
TYPE rc IS REF CURSOR;
con_sap_cur rc;
TYPE con_sap_resp IS TABLE OF target_table%ROWTYPE INDEX BY BINARY_INTEGER;
i_tab1 con_sap_resp;
lv_sql_stmt VARCHAR2(32767);
BEGIN
IF p_circle = 'MUM'
THEN
lv_circle := 'MU';
lv_schema := 'MUMBAI';
ELSIF p_circle = 'MAH'
THEN
lv_circle := 'MH';
lv_schema := 'MHRSTR';
ELSE
lv_circle := NULL;
END IF;
FOR myindex IN act_cur
LOOP
i_tab6(v_count) := myindex;
v_count := v_count + 1;
END LOOP;
FOR myindex IN i_tab6.first .. i_tab6.last
LOOP
IF i_tab6(myindex).activity_date = p_activity_date
AND i_tab6(myindex).circle = p_circle
THEN
BEGIN
lv_sql_stmt := 'SELECT acc_id code,
cust_id c_id,
addr_1 address2,
addr_2 address3,
addr_3 address4,
(SELECT SUM(abc) FROM ' || lv_schema || '.details WHERE <some condition with t1> GROUP BY <columns>) main_charges,
(SELECT SUM(extra_charge) FROM ' || lv_schema || '.details WHERE <some condition with t1> GROUP BY <columns>) extra_charges
FROM ' || lv_schema || '.main_source_details t1
WHERE t1.activity_date = ''' || p_activity_date || ''';';
OPEN con_sap_cur FOR lv_sql_stmt;
LOOP
FETCH con_sap_cur BULK COLLECT
INTO i_tab1 LIMIT p_limit;
FORALL i IN 1 .. i_tab1.count
INSERT INTO target_table (column list....)
VALUES(I_TAB1(i).col1,......;
EXIT WHEN con_sap_cur%NOTFOUND;
END LOOP;
COMMIT;
CLOSE con_sap_cur;
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line('ERR target_table: ' || SQLCODE || '-' || SQLERRM);
END;
ELSE
dbms_output.put_line(p_activity_date || ' DATE IS NOT MATCH');
END IF;
END LOOP;
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line(SQLCODE || ' ' || SQLERRM);
END proc_bulk_circle;
/
I believe this comes down to you having a ; in your definition of the sql (see below line)
WHERE t1.activity_date = ''' || p_activity_date || ''';';
when you are defining SQL for dynamic use (and opening a cursor this way is dynamic) you do not include the ;
To show this I have done a shorter example. The below will error in the same way as yours.
declare
v_sql varchar2(100) default 'select ''X'' from dual;';
TYPE rc IS REF CURSOR;
v_cur rc;
type l_tab_type is table of varchar2(1);
l_tab l_tab_type;
begin
open v_cur for v_sql;
loop
fetch v_cur bulk collect into l_tab;
exit;
end loop;
CLOSE v_cur;
end;
/
but simply remove the ; from the line
v_sql varchar2(100) default 'select ''X'' from dual;';
end it all works fine, fixed example below.
declare
v_sql varchar2(100) default 'select ''X'' from dual';
TYPE rc IS REF CURSOR;
v_cur rc;
type l_tab_type is table of varchar2(1);
l_tab l_tab_type;
begin
open v_cur for v_sql;
loop
fetch v_cur bulk collect into l_tab;
exit;
end loop;
CLOSE v_cur;
end;
/
You're doing an awful lot of work here, if your purpose is to insert some rows.
Instead, you could do the insert and select in one go, something like:
CREATE OR REPLACE PROCEDURE proc_bulk_circle(p_activity_date IN DATE,
p_circle IN VARCHAR2) AS
lv_circle VARCHAR2(2);
lv_schema VARCHAR2(20);
v_query CLOB;
e_table_does_not_exist EXCEPTION;
PRAGMA EXCEPTION_INIT(e_table_does_not_exist, -00942);
BEGIN
IF p_circle = 'MUM'
THEN
lv_circle := 'MU';
lv_schema := 'MUMBAI';
ELSIF p_circle = 'MAH'
THEN
lv_circle := 'MH';
lv_schema := 'MHRSTR';
END IF;
IF lv_schema IS NOT NULL
THEN
-- asserting the schema name to avoid sql injection
-- also using a bind variable for the activity_daate predicates
v_query := 'INSERT INTO target_table (<column list>)' || CHR(10) ||
' WITH main_dets AS (SELECT acc_id,' || CHR(10) ||
' cust_id,' || CHR(10) ||
' addr_1,' || CHR(10) ||
' addr_2,' || CHR(10) ||
' addr_3,' || CHR(10) ||
' (SELECT SUM(abc) FROM ' || dbms_assert.simple_sql_name(lv_schema) || '.details WHERE <some condition with t1>) main_charges,' || CHR(10) || -- no need for the group by
' (SELECT SUM(extra_charge) FROM ' || dbms_assert.simple_sql_name(lv_schema) || '.details WHERE <some condition with t1>) extra_charges' || CHR(10) || -- no need for the group by
' FROM ' || dbms_assert.simple_sql_name(lv_schema) || '.main_source_details t1' || CHR(10) ||
' WHERE activity_date = :p_activity_date)' || CHR(10) ||
' circles AS (SELECT activity_date,' || CHR(10) ||
' circle' || CHR(10) ||
' FROM circle_load_control' || CHR(10) ||
' WHERE activity_date = :p_activity_date' || CHR(10) ||
' AND circle = circle)' || CHR(10) || -- did you really mean circle = circle here? This is equivalent to 1=1 (unless circle is null) and is therefore pretty irrelevant! If you want to exclude rows with null values, use "circle is not null" instead
' SELECT md.acc_id,' || CHR(10) ||
' md.cust_id,' || CHR(10) ||
' md.addr_1,' || CHR(10) ||
' md.addr_2,' || CHR(10) ||
' md.addr_3,' || CHR(10) ||
' md.main_charges,' || CHR(10) ||
' md.extra_charges' || CHR(10) ||
' FROM main_dets md' || CHR(10) ||
' CROSS JOIN circles c';
EXECUTE v_query USING p_activity_date, p_activity_date;
COMMIT;
ELSE
raise_application_error(-20001, 'Invalid circle specified: "' || p_circle || '"');
END IF;
END proc_bulk_circle;
/
(N.B. untested.)
I've assumed that activity_date and circle in circle_load_control aren't unique; if they are, you could avoid the cross join and just have an implicit cursor to fetch the row prior to doing the IF p_circle = ... checks.
Create a procedure called proc_video_search to search for a video and display the name, copy ID, format, and status of the video’s copies. In addition, the checkout dates and due dates are also displayed for unreturned copies. The damaged copies (Status = 'D') are excluded in your output. Sort your output by the video name (Name) and then the copy ID (CopyID).
$ CREATE OR REPLACE PROCEDURE proc_video_search (
p_VideoName VARCHAR2,
p_FormatName VARCHAR2 DEFAULT NULL) as
v_Count NUMBER;
v_TotalCopies NUMBER; v_Avalb NUMBER;v_FormatName VARCHAR2(100);
v_VideoName VARCHAR2(100); v_CopyID VARCHAR2(100);v_DueDate DATE;
v_Status VARCHAR2(100); v_CheckoutDate DATE;
CURSOR asdf IS
SELECT T_VIDEO.Name, T_COPY.CopyID, Status,T_VIDEO_FORMAT.NAME
FROM T_VIDEO
INNER JOIN T_COPY ON T_VIDEO.VideoID = T_COPY.VideoID
INNER JOIN T_VIDEO_FORMAT ON T_VIDEO_FORMAT.FormatID =
T_VIDEO.FormatID
WHERE Status !='D' AND UPPER(T_VIDEO.Name) like '%' ||
UPPER(p_VideoName) || '%'
OR UPPER(T_VIDEO_FORMAT.NAME)= UPPER(p_FormatName)
ORDER BY T_VIDEO.Name, T_COPY.CopyID;
BEGIN
SELECT COUNT(*)
INTO v_Count
FROM T_VIDEO
WHERE UPPER(T_VIDEO.Name) like '%' || UPPER(p_VideoName) || '%' ;
IF v_count = 0 THEN
DBMS_OUTPUT.PUT_LINE('**** '||v_Count|| ' results found for ' ||
p_VideoName||'. *****');
RETURN;
END IF;
SELECT count(T_COPY.CopyID) INTO v_TotalCopies
FROM T_COPY INNER JOIN T_VIDEO ON T_COPY.VideoID = T_VIDEO.VideoID
INNER JOIN T_VIDEO_FORMA ON T_VIDEO_FORMAT.FormatID =
T_VIDEO.FormatID
WHERE Status !='D' AND UPPER(T_VIDEO.Name) like '%' ||
UPPER(p_VideoName) ||'%'
OR UPPER(T_VIDEO_FORMAT.NAME)=UPPER(p_FormatName);
SELECT count(T_COPY.CopyID)INTO v_Avalb FROM T_COPY
INNER JOIN T_VIDEO ON T_COPY.VideoID = T_VIDEO.VideoID
INNER JOIN T_VIDEO_FORMAT ON T_VIDEO_FORMAT.FormatID =
T_VIDEO.FormatID
WHERE Status ='A' AND UPPER(T_VIDEO.Name) like '%' ||
UPPER(p_VideoName) ||'%'
OR UPPER(T_VIDEO_FORMAT.NAME)=UPPER(p_FormatName);
IF v_TotalCopies >=0 THEN
IF p_FormatName IS NULL THEN
DBMS_OUTPUT.PUT_LINE(v_TotalCopies||' results found for '||
p_VideoName||' . (Available copies:'|| v_Avalb|| ')' );
ELSE
DBMS_OUTPUT.PUT_LINE(v_TotalCopies||' results found for '||
p_VideoName||'('|| p_FormatName||') . (Available copies:'||
v_Avalb|| ')' );
end if;
OPEN asdf;
LOOP
FETCH asdf INTO v_VideoName, v_CopyID, v_Status,
v_FormatName ; exit when asdf%NOTFOUND ;
SELECT COUNT(CheckoutDate)
INTO v_Count FROM T_RENTAL WHERE CopyID = v_CopyID;
IF v_Count = 1 THEN
SELECT CheckoutDate,DueDate
INTO v_CheckoutDate,v_DueDate
FROM T_RENTAL
WHERE CopyID = v_CopyID;
end if;
DBMS_OUTPUT.PUT_LINE(RPAD('-', 53, '-'));
DBMS_OUTPUT.PUT_LINE(RPAD('Name:',30) || RPAD(v_VideoName,15));
DBMS_OUTPUT.PUT_LINE(RPAD('CopyID:',30) || RPAD(v_CopyID,15));
DBMS_OUTPUT.PUT_LINE(RPAD('Format:',30) ||
RPAD(v_FormatName,15));
IF v_Status = 'A' THEN v_Status := 'Available';END IF;
IF v_Status = 'R' THEN v_Status := 'Rented'; END IF;
DBMS_OUTPUT.PUT_LINE(RPAD('Status:',30) || RPAD(v_Status,15));
IF v_Status ='Available' THEN
DBMS_OUTPUT.PUT_LINE(RPAD('CheckoutDate:',30)
||'****************************');
DBMS_OUTPUT.PUT_LINE(RPAD('DueDate:',30)
||'****************************');
ELSE
DBMS_OUTPUT.PUT_LINE(RPAD('CheckoutDate:',30)
||RPAD(TO_CHAR(v_CheckoutDate, 'DD-MON-YYYY'),15));
DBMS_OUTPUT.PUT_LINE(RPAD('DueDate:',30) ||RPAD(TO_CHAR(
v_DueDate, 'DD-MON-YYYY'),15));
END IF; END LOOP; CLOSE asdf; END IF; END proc_video_search ;
EXEC proc_video_search('ANOTHER', 'DVD')`
enter image description here
The problem is with this line of your cursor asdf
OR UPPER(T_VIDEO_FORMAT.NAME)= UPPER(p_FormatName)
Because it says OR, the query can choose to ignore this criteria if it evaluates to false. That is why you are getting results with all the formats; it ignores the filtering.
You have to wrap the OR statement in another AND clause, like so
WHERE Status !='D'
AND UPPER(T_VIDEO.Name) like '%' || UPPER(p_VideoName) || '%'
AND ( /* OR clause here */ )
And then you can handle the case of p_formatName being null or not.
I have a PL/SQL stored function script I am writing and I have run into a problem.
I need to find ALL Applicants who possess ALL the skills needed for a task and display them.
I have a method where I have individually read in Applicant's skills into a VARCHAR2 string and all the Skills required into another. If I could separate these strings into individual words I could compare the two with LIKE '% <data> %'.
How would I go about this (or what is an alternative method)?
CREATE OR REPLACE FUNCTION FUBARR(num IN NUMBER) RETURN VARCHAR IS
string_position VARCHAR2(128);
string_applicant VARCHAR2(128);
string_results VARCHAR2(128);
BEGIN
string_position := '';
string_applicant := '';
FOR SKILLS_row IN (SELECT sname FROM SNEEDED WHERE pnumber = num)
LOOP
string_position := string_position || SKILLS_row.sname || ' ';
END LOOP;
FOR EVERYBODY_row IN (SELECT UNIQUE anumber FROM SPOSSESSED ORDER BY anumber)
LOOP
FOR APPLICANTS_row IN (SELECT sname FROM SPOSSESSED WHERE SPOSSESSED.anumber = EVERYBODY_row.anumber)
LOOP
string_applicant := string_applicant || APPLICANTS_row.sname || ' ';
END LOOP;
--DBMS_OUTPUT.PUT_LINE(EVERYBODY_row.anumber || ' ' || string_applicant);
--IF blaah != LIKE BLAh
IF
string_applicant := '';
END LOOP;
--DBMS_OUTPUT.PUT_LINE(string_position);
--RETURN (string_position);
RETURN('help');
END FUBARR;
/
why not simply select all spossesed - records, where the number of associated skills of the desired num is equal to the plain number of sneeded - skills of that num:
SELECT *
FROM SPOSSESSED sp
WHERE (SELECT COUNT(*) FROM SNEEDED s
WHERE s.pnumber = num) =
(SELECT COUNT(*) FROM SNEEDED s
JOIN SPOSSESSED p ON p.sname = s.sname
WHERE s.pnumber = num and p.anumber = sp.anumber)
or using the ANY construct:
SELECT sp.anumber, COUNT(*)
FROM SPOSSESSED sp
WHERE sp.sname = ANY (SELECT s.sname FROM SNEEDED s WHERE s.pnumber = num)
GROUP BY sp.anumber
I wrote this code and it works ok:
declare
cursor c_emp is
select last_name, first_name from employees;
type c_list is table of employees.last_name%type index by binary_integer;
type c_list2 is table of employees.first_name%type index by binary_integer;
last_list c_list;
first_list c_list2;
counter integer := 0;
begin
for i in c_emp loop
counter := counter + 1;
last_list(counter) := i.last_name;
first_list(counter) := i.first_name;
dbms_output.put_line('Employee(' || counter || '): ' || last_list(counter) || ' ' || first_list(counter));
end loop;
end;
/
This time I am trying to make procedure with parameters that I can insert the table name and column into the cursor. And I have tried with this :
create or replace procedure show_data(tab_name in varchar2, data_list in varchar2)
is
str varchar2(100);
str2 varchar2(100);
column_name varchar2(100);
begin
str := 'select ' || data_list || ' from ' || tab_name;
for vRec in str loop
dbms_output.put_line(str);
end loop;
end;
/
It gave a error which the str is not a cursor. I am not sure that if cursor can be done in this way, but from the error it seems it can't.
Which part of my code is wrong, or because I didn't declare my cursor? But if i declare my cursor, I can't get the parameter by using dynamic sql way.
The below should produce the same result as your original PL/SQL block. Note that the table_name can be dynamic and so can the data_list but you need to know the column names in the data_list to be able to fetch from the cursor and then print them.
create or replace procedure show_data(tab_name in varchar2
, data_list in varchar2)
is
str varchar2(100);
str2 varchar2(100);
column_name varchar2(100);
TYPE cursor_ref IS REF CURSOR;
vRec_cursor cursor_ref;
counter integer = 0;
last_name employee.last_name%TYPE;
first_name employee.first_name%TYPE;
begin
str := 'select ' || data_list || ' from ' || tab_name;
open vRec_cursor for str;
loop
FETCH vRec_cursor INTO last_name, first_name;
EXIT WHEN vRec_cursor%NOTFOUND;
dbms_output.put_line('Employee(' || counter || '): ' || last_name || ' ' || first_name;
counter = counter + 1;
end loop;
end;
/
NOTE: Haven't run the above code yet