I am using the Oracle SQL Developer Scratch Editor tool. There are a couple of small errors, I am not sure how to fix.
--SQLDEV:Following Line Not Recognized
WHILE #I <= #PAT_CNT
--SQLDEV:Following Line Not Recognized
BEGIN
SELECT PAT_NUMBER ,
PATIENT_ID ,
MRN ,
LAST_NAME ,
FIRST_NAME ,
UNIT_CODE ,
NURSING_UNIT ,
SDT ,
EDT ,
SHIFT_CODE ,
START_TIME ,
END_TIME
INTO v_PAT_NUMBER,
v_PATIENT_ID,
v_MRN,
v_LAST_NAME,
v_FIRST_NAME,
v_UNIT_CODE,
v_NURSING_UNIT,
v_SDT,
v_EDT,
v_SHIFT_CODE,
v_START_TIME,
v_END_TIME
FROM tt_v_PATS
WHERE PKEY = v_I;
BEGIN
v_L_CUR_DATE := TRUNC_DATE(v_P_START_DATE) ;
END;
--SQLDEV:Following Line Not Recognized
WHILE #L_CUR_DATE <= OPTC.TRUNC_DATE(#P_END_DATE)
--SQLDEV:Following Line Not Recognized
BEGIN
It's not obvious to me exactly what you are attempting to do here...
In PL/SQL, variables are declared in the DECLARE section of an anonymous PL/SQL block or in the IS/ AS section of a named PL/SQL block (a stored procedure or a stored function). In PL/SQL, a BEGIN needs to be paired with an END-- I see a bunch of BEGIN statements in your code with no associated END. And in PL/SQL, you don't use # to refer to variables.
Something like this is syntactically valid, it's not obvious that it does what you want, however.
DECLARE
v_PAT_NUMBER tt_v_PATS.PAT_NUMBER%type;
v_PATIENT_ID tt_v_PATS.PATIENT_ID%type;
v_MRN tt_v_PATS.MRN%type;
v_LAST_NAME tt_v_PATS.LAST_NAME%type;
v_FIRST_NAME tt_v_PATS.FIRST_NAME%type;
v_UNIT_CODE tt_v_PATS.UNIT_CODE%type;
v_NURSING_UNIT tt_v_PATS.NURSING_UNIT%type;
v_SDT tt_v_PATS.SDT%type;
v_EDT tt_v_PATS.EDT%type;
v_SHIFT_CODE tt_v_PATS.SHIFT_CODE%type;
v_START_TIME tt_v_PATS.START_TIME%type;
v_END_TIME tt_v_PATS.END_TIME%type;
v_pat_cnt integer := 100;
BEGIN
FOR i IN 1 .. v_pat_cnt
LOOP
SELECT PAT_NUMBER ,
PATIENT_ID ,
MRN ,
LAST_NAME ,
FIRST_NAME ,
UNIT_CODE ,
NURSING_UNIT ,
SDT ,
EDT ,
SHIFT_CODE ,
START_TIME ,
END_TIME
INTO v_PAT_NUMBER,
v_PATIENT_ID,
v_MRN,
v_LAST_NAME,
v_FIRST_NAME,
v_UNIT_CODE,
v_NURSING_UNIT,
v_SDT,
v_EDT,
v_SHIFT_CODE,
v_START_TIME,
v_END_TIME
FROM tt_v_PATS
WHERE PKEY = i;
<<do something with the local variables>>
END LOOP;
END;
Of course, you can also use record types to simplify your code a bit
DECLARE
v_PATS_REC tt_v_PATS%rowype;
v_pat_cnt integer := 100;
BEGIN
FOR i IN 1 .. v_pat_cnt
LOOP
SELECT *
INTO v_PATS_REC
FROM tt_v_PATS
WHERE PKEY = i;
<<do something with the record>>
END LOOP;
END;
Related
I have a query
SELECT originating_timestamp
FROM sys.x$dbgalertext
WHERE message_text LIKE '%Starting up%'
and to_char(ORIGINATING_TIMESTAMP,'DD-MON-YY') = to_char(systimestamp,'DD-MON-YY');
The output in a linux script is as below:
09-OCT-17 04.59.33.758 AM -05:00 09-OCT-17 05.03.22.645 AM -05:00
there are two rows above each starting by date.
I would like to have the output like
09-OCT-17 04.59.33.758 AM -05:00;09-OCT-17 05.03.22.645 AM -05:00
This is just two rows, there can be many more, I would like it so that every row is separated via delimiter.
I have tried few options like
1) using listagg:
select listagg(originating_timestamp,', ') within group(order by originating_timestamp) csv
from sys.x$dbgalertext
WHERE message_text LIKE '%Starting up%'
and to_char(ORIGINATING_TIMESTAMP,'DD-MON-YY') = to_char(systimestamp,'DD-MON-YY');
But this gives error:
ERROR at line 1:
ORA-01489: result of string concatenation is too long
2) using XMLAGG:
SELECT RTRIM(XMLAGG(XMLELEMENT(E,originating_timestamp,';').EXTRACT('//text()') ORDER BY originating_timestamp).GetClobVal(),',') AS LIST
FROM sys.x$dbgalertext
WHERE message_text LIKE '%Starting up%'
and to_char(ORIGINATING_TIMESTAMP,'DD-MON-YY')= to_char(systimestamp,'DD-MON-YY');
But the output is like :
2017-10-09T04:59:33.758-05:00;2017-10-09T05:03:22.645-05:00;
Which is also not correct.
e.g. Select username from dba_users. Suppose there are 10 users, i want those 10 usernames to be separated via delimiter.
Below two anonymous block example which may be helpful to solve your problem :
SET SERVEROUTPUT ON SIZE 1000000
DECLARE
res VARCHAR2(100);
tmp VARCHAR2(100);
BEGIN
FOR i IN 1..10 LOOP
IF i != 10 THEN
select 'a' ||',' into tmp from dual;
ELSE
select 'a' into tmp from dual;
END IF;
res := concat(res,tmp);
END LOOP;
DBMS_OUTPUT.PUT_LINE(res);
END;
/
Second :
SET SERVEROUTPUT ON SIZE 1000000
DECLARE
res VARCHAR2(1000);
tmp VARCHAR2(1000);
BEGIN
FOR i IN 1..10 LOOP
IF i != 10 THEN
select TO_CHAR(sysdate,'DD-MON-YYYYHH24:MI') || ',' into tmp from dual;
ELSE
select TO_CHAR(sysdate,'DD-MON-YYYYHH24:MI') into tmp from dual;
END IF;
res := concat(res,tmp);
END LOOP;
DBMS_OUTPUT.PUT_LINE(res);
END;
/
You should replace my query select with yours and of course the format of date you wanna get and replace comma with your desire character .
To make it working properly i wanna suggest you to create a cursor :
SET SERVEROUTPUT ON SIZE 1000000
DECLARE
res VARCHAR2(100);
tmp VARCHAR2(100);
cont NUMBER;
CURSOR C_1 IS
SELECT to_char(originating_timestamp,'your date format')
originating_timestamp
FROM sys.x$dbgalertext
WHERE message_text LIKE '%Starting up%'
and to_char(ORIGINATING_TIMESTAMP,'DD-MON-YY') = to_char(systimestamp,'DD-MON-YY');
BEGIN
cont := 0;
FOR C_row IN C_1 LOOP
IF cont != C_1%rowcount THEN
tmp := C_row.originating_timestamp ||',';
res := concat(res,tmp);
ELSE
tmp := C_row.originating_timestamp;
res := concat(res,tmp);
END IF;
cont := cont +1;
END LOOP;
DBMS_OUTPUT.PUT_LINE(res);
END;
/
Let me start off by saying I'm open to other ways of doing this, but right now, this is all I've been able to come up with.
I'm working within a package.procedure and I am using a table type as an array/list to store IDs of records that match search criteria. Once I have compiled the list, I want to open a cursor that selects from a table where the record ID is in my list.
Data structure:
TYPE i_array IS TABLE OF t_table.id_column%TYPE INDEX BY PLS_INTEGER;
lt_results i_array;
ln_count pls_integer;
Populating list:
ln_count := 0;
for recs in (select id_column from t_child where other_column = ls_criteria)
loop
ln_count := ln_count + 1;
lt_results(ln_count);
end loop;
Opening cursor and accessing list:
open cur for
select col_a,
col_b,
col_c
from t_table
where id_column in (select lt_results(level)
from dual
connect by level <= ln_count);
If using Oracle 12C you can use a nested table collection in the package like this:
create or replace package test is
TYPE i_array IS TABLE OF t_child.id_column%TYPE;
procedure run;
end;
create or replace package body test is
procedure run is
lt_results i_array := i_array();
cur sys_refcursor;
begin
for r in (select id_column from t_child where other_column = ls_criteria) loop
lt_results.extend;
lt_results(lt_results.count) := r.id_column ;
end loop;
open cur for
select col_a,
col_b,
col_c
from t_table
where id_column in (select column_value from table(lt_results));
end run;
end;
Prior to 12C the type i_array would need to be in the database:
create type i_array is table of varchar2(10); -- For example
You could use a handy pre-existing collection like SYS.KU$_VCNT instead of creating your own.
BTW Note that this:
for r in (select id_column from t_child where other_column = ls_criteria) loop
lt_results.extend;
lt_results(lt_results.count) := r.id_column ;
end loop;
can be replaced more efficiently by this:
select id_column
bulk collect into lt_results
from t_child where other_column = ls_criteria;
In my Pl/Sql code , I have three variables v_var1 , v_operand , v_var2 whose values are populated based on some logic (v_var1 & v_var2 can be date , number , varchar. Associated Operand will be according to data type only). A sample would be
v_var1 = 10 , v_operand = '=' , v_var2 = 20.
Based on these value , I have to evaluate whether the condition "v_var1 -v_operand- v_var2"is true or false.
Ex :- with above values, I have to evaluate whether 10 equals 20 or not.
How can I achieve this ? Can I pass the whole string as '10 = 20' to some function and get the result as false?
One way I can think of is to write CASE statements for evaluating but can there be a better way ?
You could use dynamic SQL to do the evaluation as a filter on the dual table:
declare
v_var1 varchar2(10) := '10';
v_operand varchar2(10) := '=';
v_var2 varchar2(10) := '20';
l_result number;
begin
execute immediate 'select count(*) from dual where :var1 ' || v_operand || ' :var2'
into l_result using v_var1, v_var2;
if l_result = 1 then
dbms_output.put_line('True');
else
dbms_output.put_line('False');
end if;
end;
/
PL/SQL procedure successfully completed.
False
If the condition is true the count will get 1, otherwise it will get 0, and you can then test that via the local variable you select the count into.
Holding dates and numbers as strings isn't ideal, even temporarily, but might be OK as long as you convert to/from the real data types consistently, e.g. always explicitly converting dates with to_date and to_char and specifying the format masks.
My Problem is that I want to execute a dynamic SQL-Query within PL/SQL where I have a List of IDs as my Array Bind.
In the Oracle-Documentation I found some Examples how to join Lists of Numbers to an DML-Statement. (http://docs.oracle.com/cd/B28359_01/appdev.111/b28419/d_sql.htm#i996963)
Now I am trying to make the same thing for Select-Statements.
I know that I can use Array-Binds for the execute immediate-Statement. But this has the disadvantage that I must know the exact number of Bind-Variables before executing the Statement. That is the reason why I have to use dbms_sql.
The following Example Returns only one Row, but it should return 3 rows. Does anyone know what the Problem with my Example is?
--TestData:
CREATE TABLE PERSON AS
SELECT LEVEL AS ID, 'Person_'||LEVEL AS NAME
FROM DUAL CONNECT BY LEVEL <= 5;
declare
p_ids dbms_sql.number_table;
c number;
dummy NUMBER;
p_name varchar2(100);
begin
p_ids(1) := 2;
p_ids(2) := 3;
p_ids(3) := 4;
--
c := DBMS_SQL.OPEN_CURSOR;
DBMS_SQL.PARSE(c, 'select name from PERSON where id in(:num_array)', DBMS_SQL.NATIVE);
dbms_sql.define_column(c, 1, p_name, 100);
DBMS_SQL.BIND_ARRAY(c, ':num_array', p_ids);
dummy := DBMS_SQL.EXECUTE(c);
--
loop
exit when dbms_sql.fetch_rows(c) <=0;
dbms_sql.column_value(c, 1, p_name);
dbms_output.put_line(p_name);
end loop;
DBMS_SQL.CLOSE_CURSOR(c);
end;
Here is my current solution for binding multiple values to a Select Statement, maybe someone can need it:
--TestData:
CREATE TABLE PERSON AS
SELECT LEVEL AS ID, 'Person_'||LEVEL AS NAME
FROM DUAL CONNECT BY LEVEL <= 5;
declare
c number;
dummy NUMBER;
p_name varchar2(100);
xml$ varchar2(1000);
begin
--Generate a XML-List instead of dbms_sql.number_table:
xml$ := '<ids><id>2</id><id>3</id><id>4</id></ids>';
--
c := dbms_sql.open_cursor;
--Using XML-Functions for extracting the Values from the XML-String
DBMS_SQL.PARSE(c, 'select name
from PERSON
where id in(select extractvalue(value(x), ''id'')
from table(xmlsequence(xmltype(:ids).extract(''ids/*'')))x)'
, DBMS_SQL.NATIVE);
dbms_sql.define_column(c, 1, p_name, 100);
DBMS_SQL.BIND_variable(c, ':ids', xml$);
dummy := DBMS_SQL.EXECUTE(c);
--
loop
exit when dbms_sql.fetch_rows(c) <=0;
dbms_sql.column_value(c, 1, p_name);
dbms_output.put_line(p_name);
end loop;
DBMS_SQL.CLOSE_CURSOR(c);
end;
I have function (used within a view) with the end result being a list of unique values from a row within a table of comma separated values.
Essentially given a table of:
studentid classes
12345 MATH 1301, HIST 1301, POLS 1301
57495 MATH 2309, HIST 1301
39485 MATH 1301, HIST 1301
I want to see
MATH 1301
MATH 2309
HIST 1301
POLS 1301
The below code works perfect if the source table is small, but when looking at a table of 30,000 rows I get the following error. ORA-06532: Subscript outside of limit
I'm pretty sure my problem is the collection is getting too large since it's getting duplicate values. The duplicate values in themselves only become a problem when the collection becomes too large. How do I keep the duplicate values out of the collection?
I've tried childnames.exists(element) but I believe this only works for seeing if there exists an element at the index value element correct? of I've looked at member of but I don't understand how to implement it.. Is there an easy way to check if a collection element exists? Or am I over looking something simple? Is there a different type other than odcivarchar2list that would allow a larger collection?
CREATE OR REPLACE FUNCTION unique_values_from_csv ( p_del VARCHAR2 := ',')
RETURN SYS.odcivarchar2list
IS
childnames SYS.odcivarchar2list := sys.odcivarchar2list ();
tempvar VARCHAR2(255);
l_idx PLS_INTEGER;
l_list2 VARCHAR2(32767) ;
l_value VARCHAR2(32767);
CURSOR tablewalker_cur
IS
SELECT distinct classes
FROM studentclasses;
BEGIN
FOR recordwalker_rec IN tablewalker_cur
LOOP
l_list2 := recordwalker_rec.classes;
LOOP
l_idx := INSTR (l_list2, p_del);
IF l_idx > 0
THEN
childnames.EXTEND;
tempvar := (LTRIM (RTRIM (SUBSTR (l_list2, 1, l_idx - 1))));
childnames (childnames.COUNT) := tempvar;
l_list2 := SUBSTR (l_list2, l_idx + LENGTH (p_del));
end if;
childnames.EXTEND;
childnames (childnames.COUNT) := (LTRIM (RTRIM (l_list2)));
EXIT;
END LOOP;
END LOOP;
RETURN childnames;
END unique_values_from_csv;
/
create or replace function unique_values_from_csv(p_del varchar2 := ',')
return sys.dbms_debug_vc2coll is
childnames sys.dbms_debug_vc2coll := sys.dbms_debug_vc2coll();
l_idx pls_integer;
l_list2 varchar2(32767) ;
procedure add_if_not_member(new_element varchar2) is
begin
if new_element not member of childnames then
childnames.extend;
childnames(childnames.count) := new_element;
end if;
end;
begin
for recordwalker_rec in (select distinct classes from studentclasses)
loop
l_list2 := recordwalker_rec.classes;
loop
l_idx := instr (l_list2, p_del);
if l_idx > 0 then
add_if_not_member(trim(substr (l_list2, 1, l_idx - 1)));
l_list2 := substr(l_list2, l_idx + length(p_del));
else
add_if_not_member(trim(l_list2));
exit;
end if;
end loop;
end loop;
return childnames;
end unique_values_from_csv;
/
Used SYS.DBMS_DEBUG_VC2COLL, which is a TABLE OF VARCHAR2(1000) and should support any number of elements. Although l_list2 varchar2(32767) will limit the results.
MEMBER OF is the correct condition.
Added an ELSE - the original function was only splitting the list in two.
Removed the cursor - for such a small query, another level of indirection isn't worth it.
Used TRIM() instead of LTRIM(RTRIM())
The best solution would be to throw out this function and stop storing non-atomic data in your database.
Here's some sample data and a query using the function:
create table studentclasses(studentid number, classes varchar2(4000));
insert into studentclasses
select 12345, 'MATH 1301,HIST 1301,POLS 1301' from dual union all
select 57495, 'MATH 2309,HIST 1301' from dual union all
select 39485, 'MATH 1301,HIST 1301' from dual;
commit;
select unique_values_from_csv from dual;
COLUMN_VALUE
MATH 1301
HIST 1301
POLS 1301
MATH 2309
select distinct
regexp_substr(classes, '([^,]+)(,\s*|$)', 1, occ, '', 1) as class
from
studentclasses,
(
select level as occ
from dual
connect by level <= (
select max(regexp_count(classes, ','))+1
from studentclasses
)
)
where
regexp_substr(classes, '([^,]+)(,\s*|$)', 1, occ, '', 1) is not null
order by 1