Im trying to write a PL/SQL Code which should ask if something is there count it and
if tmp > 0
so if the object = 'VALID' then execute the rest of the sql statements if not skip everything in the Then case. When I Execute my code it dont execute the code.
DECLARE
tmp INT;
BEGIN
SELECT COUNT(*)
INTO tmp
FROM CDB_registry
WHERE Status = 'VALID';
IF tmp > 0 THEN
Col c.Status Format A6
Col Containers Format A20
Select c.Status, v.name as Containers
From Cdb_Ols_Status c, V\$Containers v
Where c.con_id = v.con_id
And c.Name = 'OLS_DIRECTORY_STATUS'
Order By v.Con_ID;
prompt ****************************************************************************************
DECLARE
tmp INT;
BEGIN
SELECT COUNT(*)
INTO tmp
FROM Cdb_Ols_Status
WHERE Status = 'TRUE'
AND name = 'OLS_DIRECTORY_STATUS';
IF tmp > 0 THEN
DBMS_Output.put_line('The Output is true');
ELSE
DBMS_Output.put_line('The Output is wrong');
END IF;
END;
/
ELSE
DBMS_Output.put_line('There is no value');
END IF;
END;
/
Here's one options; I presumed some things (read comments within code) & agree with #gsalem's comment.
You commented that
cdb_ols_status table does not exist when tmp < 0
It means that you'll have to use dynamic SQL (execute immediate) because - if table doesn't exist, code won't even compile. I guess that's why you said that code I previously posted won't work.
DECLARE
tmp INT;
BEGIN
SELECT COUNT (*)
INTO tmp
FROM cdb_registry
WHERE status = 'VALID';
IF tmp > 0
THEN
-- If this SELECT return only 1 row, you could declare local variables to fetch
-- those values INTO them.
-- Otherwise, it depends on what you want to do with them; if you'd just want to
-- DISPLAY their values (which is what your SQL*Plus COL ... FORMAT commands suggest),
-- then see whether a LOOP helps.
FOR cur_r IN ( SELECT c.status, v.name AS containers
FROM cdb_ols_status c, v$containers v
WHERE c.con_id = v.con_id
AND c.name = 'OLS_DIRECTORY_STATUS'
ORDER BY v.con_id)
LOOP
DBMS_OUTPUT.put_line (cur_r.status || ' - ' || cur_r.containers);
END LOOP;
-- The rest of the script should be ran because TMP value is greater than 0.
EXECUTE IMMEDIATE 'select count(*) from cdb_ols_status '
|| q'[where status = 'TRUE' and name = 'OLS_DIRECTORY-STATUS']'
INTO tmp;
IF tmp > 0
THEN
DBMS_OUTPUT.put_line ('The Output is true');
ELSE
DBMS_OUTPUT.put_line ('The Output is wrong');
END IF;
ELSE
DBMS_OUTPUT.put_line ('There is no value');
END IF;
END;
/
I need to find all self-intersecting linestrings in table. SDO_GEOM.VALIDATE_GEOMETRY_WITH_CONTEXT finds only self-intersecting polygons, because self-intersecting linestrings are allowed. Any ideas?
It takes a bit of work, but it's doable.
Since Oracle(11.2) failed to provide, the only option we have is to brake the line into segments and use RELATE on the segments' pairs.
Following is my own implementation (used in production code for over 3 years, over millions of geometries). I chose the pipelined approach to cover for overly big or complex geometries.
Prerequisit 1, database type:
CREATE OR REPLACE TYPE ElemGeom as object
(eid integer, egeom mdsys.sdo_geometry, egtype integer, eelemnum integer, evertnum integer, earea number, elength number);
CREATE OR REPLACE TYPE ElemGeomTbl as table of ElemGeom;
Prerequisit 2, splitting function:
create or replace FUNCTION LineSegments (igeom in mdsys.sdo_geometry)
RETURN ElemGeomTbl pipelined
is
seg ElemGeom := ElemGeom(null,null,null,null,null,null,null);
cursor c is select T.id, T.X ,T.Y from table(SDO_UTIL.GETVERTICES(iGEOM)) T order by 1;
type ctbl is table of c%rowtype;
carr ctbl;
seg_geom mdsys.sdo_geometry;
cnt integer:=0;
segid integer; x1 number; y1 number; x2 number; y2 number;
begin
--if igeom.sdo_gtype not in (2002,2006)
--then... if you need to catch non-linears here...
--end if;
open c;
loop
fetch c
bulk collect into carr ;
for i in carr.first .. carr.last -1
loop cnt:=cnt+1;
segid := cnt;
x1 := carr(i).X; y1 := carr(i).Y;
x2 := carr(i+1).X; y2 := carr(i+1).Y;
seg_geom:= (mdsys.sdo_geometry(2002,2100,null
,mdsys.sdo_elem_info_array(1,2,1)
,mdsys.sdo_ordinate_array(x1,y1, x2,y2)));
seg.eid:=segid;
seg.egeom:=seg_geom;
seg.egtype:=seg_geom.sdo_gtype;
pipe row(seg);
end loop;
exit when c%notfound;
end loop;
close c;
end LineSegments;
You can test its output with something like (my GEOMs are SRID 2100):
with t1 as (
select
SDO_GEOMETRY(2002,2100,NULL,
SDO_ELEM_INFO_ARRAY(1,2,1),
SDO_ORDINATE_ARRAY(290161.697,4206385.413, 290161.901,4206388.095, 290162.684,4206385.188, 290163.188,4206388.041,
290163.51,4206385.22, 290164.357,4206388.159, 290166.879,4206387.108, 290161.397,4206387.366,
290166.331,4206386.067, 290165.763,4206388.052))
as G from DUAL
)
select * from t1,table(LineSegments(g));
And the main function:
create or replace FUNCTION validate_Line
(igeom in mdsys.sdo_geometry, itol in number default null)
RETURN varchar2
is
vtol number:= nvl(itol, 1/power(10,6));
verd1 varchar2(256); verd2 varchar2(256); v varchar2(256);
begin
verd1:= sdo_geom.validate_geometry_with_context(igeom,vtol);
for r1 in ( select a.eid seg1, a.egeom geom1, b.eid seg2, b.egeom geom2
from table(LineSegments(igeom)) a, table(LineSegments(igeom)) b
where a.eid < b.eid
order by a.eid, b.eid )
loop
--I hate outputting long words, so:
v:= replace(replace(sdo_geom.relate(r1.geom1,'determine',r1.geom2, vtol)
,'OVERLAPBDYDISJOINT','OVR-BDIS'),'OVERLAPBDYINTERSECT','OVR-BINT');
if instr('EQUAL,TOUCH,DISJOINT',v) = 0 then
verd2:= verd2|| case when verd2 is not null
then ', '||r1.seg1||'-'||r1.seg2||'='||v
else r1.seg1||'-'||r1.seg2||'='||v end;
end if;
end loop;
verd1:= nvl(verd1,'NULL')
|| case when verd1 ='TRUE' and verd2 is null then null
when verd1 ='TRUE' and verd2 is not null then ' *+: '||verd2
end;
return verd1;
end validate_Line;
And its test:
with t1 as (
select
SDO_GEOMETRY(2002,2100,NULL,
SDO_ELEM_INFO_ARRAY(1,2,1),
SDO_ORDINATE_ARRAY(290161.697,4206385.413, 290161.901,4206388.095, 290162.684,4206385.188, 290163.188,4206388.041,
290163.51,4206385.22, 290164.357,4206388.159, 290166.879,4206387.108, 290161.397,4206387.366,
290166.331,4206386.067, 290165.763,4206388.052))
as G from DUAL
)
select t1.*,validate_Line(g) from t1;
This returns:
*TRUE *+: 1-7=OVR-BDIS, 1-8=OVR-BDIS, 2-7=OVR-BDIS, 2-8=OVR-BDIS, 3-7=OVR-BDIS, 3-8=OVR-BDIS, 4-7=OVR-BDIS, 4-8=OVR-BDIS, 5-7=OVR-BDIS, 5-8=OVR-BDIS, 6-9=OVR-BDIS, 7-9=OVR-BDIS*
Of course, you can modify the output to be just a flag or anything else - this is just what suited my own needs.
HTH
I want to fetch a single column into an array. I am using following code
TYPE t_column IS TABLE OF TABLE_1.COLUMN_1%TYPE INDEX BY PLS_INTEGER;
ar_column t_column;
Then the query is something like
select column_1 from table_1 where column_1 = values;
And i am trying to fetch it
OPEN cr_table FOR select_query;
LOOP
FETCH cr_table INTO ar_column LIMIT 1000;
EXIT WHEN ar_column.count = 0
END LOOP;
But for this i am getting error Error(1526,25): PLS-00597: expression 'ar_column' in the INTO list is of wrong type
try this:
declare
cursor c1 is
select last_name ls from empc;
type x is table of employees.last_name%type;
x1 x := x();
cnt integer :=0;
begin
for z in c1 loop
cnt := cnt +1;
exit when cnt > 5;
x1.extend;
x1(cnt) := z.ls;
dbms_output.put_line(cnt || ' '|| x1(cnt));
end loop;
end;
I need to call the stored function findtotalcarmodels from this PL/SQL block. The way this code is written is not the way I would do it in production, however it is an exercise in 'lateral' thinking.
SET SERVEROUTPUT ON FORMAT WRAP SIZE 12000
Declare
v_model VARCHAR2(40);
v_cost NUMBER;
v_reg VARCHAR2(10);
v_carcategory VARCHAR2(40);
v_totalcars NUMBER;
v_count DATE;
v_maxcount DATE;
v_maxdept VARCHAR2(20);
cursor carcur IS
SELECT * FROM i_car;
v_car carcur%ROWTYPE;
Cursor c_date (p_reg i_booking.registration%TYPE) IS
SELECT date_reserved
FROM i_booking
WHERE registration = p_reg;
v_date c_date%ROWTYPE;
Begin
v_totalcars := findtotalcarmodels();
FOR v_car IN carcur LOOP
If v_cost <=50000 THEN v_carcategory := 'Budget Car';
End IF;
If v_cost BETWEEN 50000 AND 100000 THEN v_carcategory := 'Standard Car';
End IF;
If v_cost >100000 THEN v_carcategory := 'Premium Car';
End If;
FOR v_date IN c_date(v_car.registration) LOOP
v_count := v_count + 1;
END LOOP;
IF v_count > v_maxcount THEN
v_maxcount := v_count;
v_maxdept := v_car.registration;
END IF;
DBMS_OUTPUT.PUT_LINE('Registration:'|| ' '|| v_car.registration);
DBMS_OUTPUT.PUT_LINE('Cost:'|| '$' ||v_car.Cost);
DBMS_OUTPUT.PUT_LINE('Model Name:'|| ' '||v_car.model_name);
DBMS_OUTPUT.PUT_LINE('Car Category:'|| ' '||v_carcategory);
DBMS_OUTPUT.PUT_LINE('Total number of Cars:'|| ' '||v_totalcars);
DBMS_OUTPUT.PUT_LINE('Most Recent Rental Date: '|| ' '||v_maxcount);
DBMS_OUTPUT.NEW_LINE;
END LOOP;
END;
I am getting the error:
v_totalcars := findtotalcarmodels();
*
ERROR at line 19:
ORA-06550: line 19, column 16:
PLS-00306: wrong number or types of arguments in call to 'FINDTOTALCARMODELS'
ORA-06550: line 19, column 1:
PL/SQL: Statement ignored
Am I calling my function correctly in the right position?
This is the function:
CREATE OR REPLACE Function findtotalcarmodels
(model_name_in IN varchar2)
RETURN NUMBER
IS
counter INTEGER := 0;
CURSOR car_count_cur IS
SELECT model_name FROM i_car WHERE model_name = model_name_in;
Rec_car_details car_count_cur%ROWTYPE;
BEGIN
OPEN car_count_cur;
LOOP
FETCH car_count_cur INTO Rec_car_details;
EXIT WHEN car_count_cur%NOTFOUND;
counter := counter + 1;
END LOOP;
CLOSE car_count_cur;
RETURN counter;
END;
Okay, so I have no idea why you're getting that error with that function. The error indicates that you're not giving the function the correct number of arguments. Judging by the function that's clearly not what's happening, or it's not the same function.
You've just changed the function call; the function requires an argument so the "incorrect" code you had in your first revision was actually correct.
However, let's put that to one side for a second and look again at what you're doing.
Your function is a count on a table. There's no need for a cursor or looping, incrementing variables or anything. You can simplify it to
select count(*) from i_car where model_name = :model_name
You never assign the variables v_count or v_maxcount a value so incrementing them will still result in a NULL. They're dates anyway, so it's a little strange to be incrementing them.
Your cursor c_date is just another count; once again no need for a loop.
The model_name variable is never initialised so your function will not return a result.
There are a lot of ways to simplify this; though I'm going to guess a few things here. Change your carcur cursor to the following:
select i.*
, case cost
when <= 50000 then 'Budget Car'
when <= 100000 then 'Standard Car'
else 'Premium Car'
end as category
, count(*) over ( partition by model_name ) as total_cars
from i_cars
This appears to enable you to remove your IF statements and your function. You can then remove your second loop by performing an outer join on i_booking (you need to add the primary key in yourself):
select i.*
, case c.cost
when <= 50000 then 'Budget Car'
when <= 100000 then 'Standard Car'
else 'Premium Car'
end as category
, count(distinct c.primary_key)
over ( partition by c.model_name ) as total_cars
, count(b.date_reserved)
over ( partition by b.registration ) as reserved_ct
from i_cars c
left outer join i_booking b
on c.registration = b.registration
I'm not 100% certain on the max stuff as it's not clear at all where it's assigned (it's not) but it looks as though you might be wanting to find the maximum count by model etc, in which case you can use a sub-query on the above cursor:
select sub.*
, max(total_cars) over () as max_cars
, max(reserved_ct) over () as max_reserved
from ( select i.*
, case c.cost
when <= 50000 then 'Budget Car'
when <= 100000 then 'Standard Car'
else 'Premium Car'
end as category
, count(distinct c.primary_key)
over ( partition by c.model_name ) as total_cars
, count(b.date_reserved)
over ( partition by b.registration ) as reserved_ct
from i_cars c
left outer join i_booking b
on c.registration = b.registration
) sub
If you then need to output it (there's rarely a need) you can loop through this single SQL statement, which gives you everything in one go:
declare
c_cursor is
select sub.*
, max(total_cars) over () as max_cars
, max(reserved_ct) over () as max_reserved
from ( select i.*
, case c.cost
when <= 50000 then 'Budget Car'
when <= 100000 then 'Standard Car'
else 'Premium Car'
end as category
, count(distinct c.primary_key)
over ( partition by c.model_name ) as total_cars
, count(b.date_reserved)
over ( partition by b.registration ) as reserved_ct
from i_cars c
left outer join i_booking b
on c.registration = b.registration
) sub
;
begin
for i in c_cursor loop
dbms_output.put_line(i.registration);
dbms_output.put_line(i.cost);
...
end loop;
end;
I've reduced a PL/SQL block and a function to a single SQL statement; it may not be spot on because there's so many unknowns but it's worth trying for yourself. Simple is almost always better.
I want to stop making the same performance mistakes and need someone way steadier on SQL statements than me on this one.
Basically I want my function:
create or replace FUNCTION SEQGEN(vinp in varchar2, iSeq in INTEGER)
RETURN VARCHAR2 is vResult VARCHAR2(32);
iBas INTEGER; iRem INTEGER; iQuo INTEGER; lLen CONSTANT INTEGER := 2;
BEGIN
iBas := length(vInp);
iQuo := iSeq;
WHILE iQuo > 0 LOOP
iRem := iQuo mod iBas;
--dbms_output.put_line('Now we divide ' || lpad(iQuo,lLen,'0') || ' by ' || lpad(iBas,lLen,'0') || ', yielding a quotient of ' || lpad( TRUNC(iQuo / iBas) ,lLen,'0') || ' and a remainder of ' || lpad(iRem,lLen,'0') || ' giving the char: ' || substr(vInp, iRem, 1)); end if;
iQuo := TRUNC(iQuo / iBas);
If iRem < 1 Then iRem := iBas; iQuo := iQuo - 1; End If;
vResult := substr(vInp, iRem, 1) || vResult;
END LOOP;
RETURN vResult;
END SEQGEN;
to be written with SQL statements only.
Something like:
WITH sequence ( vResult, lSeq ) AS
(
SELECT str, length(str) base FROM (SELECT 'abc' str FROM DUAL)
)
SELECT vResult FROM sequence WHERE lSeq < 13
if str = 'aB' output: if str = 'abC' output:
1 a a
2 B b
3 a a C
4 a B a a
5 B a a b
6 B B a C
7 a a a b a
8 a a B b b
9 a B a b C
10 a B B C a
11 B a a C b
12 B a B C C
13 B B a a a a
and if str = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ' then:
SELECT vResult FROM sequence
WHERE vResult in ('0001','0002','0009','000A','000Z',
'0010','0011','001A','ZZZZ') --you get the idea...
I have found some questions at Stackoverflow that are fairly related.
Base 36 to Base 10 conversion using SQL only and
PL/SQL base conversion without functions.
But with my current knowledge of SQL I am not quite able to hack this one...
EDITED:
Or well, it is almost like this one:
select sum(position_value) pos_val from (
select power(base,position-1) * instr('abc', digit) as position_value from (
select substr(input_string,length(input_string)+1-level,1) digit, level position, length(input_string) base
from (select 'cc' input_string from dual)
connect by level <= length(input_string)
)
)
Excepted that I want to give the pos_val as a parameter and get the input_string out...
Here is one way. Another cool usage of the model function in Oracle SQL.