I am new to PL/SQL and just want to know what I can do here.
I have created a table that loops a counter up to 10 that is displayed in the table data.
How do I achieve it so I can count to 1-10 but exclude a number such a 5 so that it displays 1, 2, 3, 4, 6, 7, 8, 9, 10?
Current code is as follows;
DROP TABLE COUNTER
CREATE TABLE COUNTER (
COUNTER VARCHAR2(60)
);
DECLARE V_COUNTER NUMBER(2) := 1;
BEGIN
LOOP
INSERT INTO COUNTER (COUNTER)
VALUES (V_COUNTER);
V_COUNTER := V_COUNTER + 1
EXIT WHEN V_COUNTER = 11;
END LOOP;
END;
Table data;
1 1
2 2
3 3
4 4
5 5
6 6
7 7
8 8
9 9
10 10
Something like:
DROP TABLE COUNTER
CREATE TABLE COUNTER (COUNTER VARCHAR2(60));
BEGIN
FOR i IN 1 .. 10 LOOP
IF i <> 5 THEN
INSERT INTO COUNTER (COUNTER) VALUES (i);
END IF;
END LOOP;
END;
/
In general it's better to process such statements in bulk and avoid context switching ( here's more info on this topic.
DECLARE
TYPE t_values IS
TABLE OF NUMBER;
l_values t_values;
BEGIN
SELECT
level counter
BULK COLLECT
INTO l_values
FROM
dual
WHERE
level NOT IN (
6,
7
)
CONNECT BY
level <= 11;
FORALL i IN l_values.first..l_values.last
INSERT INTO counter VALUES ( l_values(i) );
END;
/
if code is simple enough you can always use plain insert stmt.
INSERT INTO counter
SELECT
level
FROM
dual
WHERE
level NOT IN (
6,
7
)
CONNECT BY
level <= 11;
Related
I asked a similar question a few days ago,
PostGIS: how to split linestrings into their individual segments?
Hut further, I want to know more, if I want to split a line into a set of line segments according to every N points,how can I do this?
For example:
CREATE TABLE t (gid int, geom geometry(linestring,4326));
INSERT INTO t VALUES
(1,'SRID=4326;LINESTRING(1 1,2 2,3 3,4 4,5 5,6 6,7 7,8 8,9 9)'),
(2,'SRID=4326;LINESTRING(1 1,2 2, 3 3, 4 4,5 5)'),
(3,'SRID=4326;LINESTRING(1 1, 2 2)');
If N=3 ,then I hope the result is like this:
MultiLineString( (1 1, 2 2, 3 3), (4 4, 5 5, 6 6), (7 7, 8 8, 9 9) )
MultiLineString( (1 1, 2 2, 3 3), (4 4, 5 5) )
MultiLineString( (1 1, 2 2) )
And further, if I want to reverse the operation, merge line segments into one line, how can I do this?
Just use ST_MakeLine as a function and change the window accordingly, e.g. CURRENT ROW AND 2 FOLLOWING for the current row and the next two:
WITH j AS (
SELECT gid, ST_MakeLine(j.geom) OVER w AS line
FROM t, ST_DumpPoints(geom) j (path,geom)
WINDOW w AS (PARTITION BY gid ORDER BY j.path
ROWS BETWEEN CURRENT ROW AND 2 FOLLOWING)
)
SELECT gid, ST_AsText(ST_Collect(line)) AS geom FROM j
GROUP BY gid;
If you wish to rather split the LineStrings in 3 points chuncks, consider using PL/pgSQL:
DO $$
DECLARE
i record; j record;
line_partition geometry(linestring,4326) := 'SRID=4326;LINESTRING EMPTY';
multiline geometry(multilinestring,4326) := 'SRID=4326;MULTILINESTRING EMPTY';
BEGIN
FOR i IN SELECT gid,geom AS geom FROM t LOOP
FOR j IN SELECT * FROM ST_DumpPoints(i.geom) p (path,point) LOOP
line_partition:=ST_MakeLine(line_partition,j.point);
IF ST_NumPoints(line_partition)=3 THEN
multiline := ST_Union(multiline,ST_Multi(line_partition));
line_partition := 'SRID=4326;LINESTRING EMPTY';
END IF;
END LOOP;
IF ST_NumPoints(line_partition)<3 THEN
multiline := ST_Union(multiline,ST_Multi(line_partition));
line_partition := 'SRID=4326;LINESTRING EMPTY';
END IF;
INSERT INTO t2 (gid,geom) VALUES (i.gid,multiline);
multiline := 'SRID=4326;MULTILINESTRING EMPTY';
END LOOP;
END;
$$;
To reverse the operation, assuming you stored this information in a another table, just use ST_MakeLine with a GROUP BY gid
SELECT gid, ST_AsText(ST_MakeLine(DISTINCT j.geom))
FROM t2,ST_DumpPoints(geom) j (path,geom)
GROUP BY gid;
Demo: db<>fiddle
I have this trouble
declare
cursor tab is
select symbol, SDO_GEOM.SDO_AREA(geom, 0.1)
from budynki2
order by 2 desc;
begin
for x in tab loop
dbms_output.put_line(x.symbol || ' ' ||x.geom);
end loop;
end;
Still I get a error that PLS-00302: component 'GEOM' must be declared
I don't know how I can get in to values of second's column in my curosor.
;((
Try to use
declare
cursor tab is
select symbol, SDO_GEOM.SDO_AREA(b.geom, 0.1)
from budynki2 b
order by 2 desc;
begin
for x in tab loop
dbms_output.put_line(x.symbol || ' ' ||x.geom);
end loop;
end;
by aliasing your table budynki2 as b, and prefix your column geom with this alias as b.geom. Assuming you have a column named geom in your table of type SDO_GEOMETRY, you may have another object, in your schema, whose name is geom, and that ambigious definition causes this error.
Still I get a error that PLS-00302: component 'GEOM' must be declared
In your cusor there is no column name GEOM (column still present in table but cursor not able to identify) and that is what Oracle reporting. You just need to alias your SDO_GEOM.SDO_AREA as GEOM and rest should work.
DECLARE
CURSOR TAB
IS
SELECT SYMBOL,
SDO_GEOM.SDO_AREA (GEOM, 0.1) AS GEOM
FROM BUDYNKI2
ORDER BY 2 DESC;
BEGIN
FOR X IN TAB
LOOP
DBMS_OUTPUT.PUT_LINE (X.SYMBOL || ' ' || X.GEOM);
END LOOP;
END;
See Demo:
Table:
SQL> CREATE TABLE cola_markets (
2 mkt_id NUMBER PRIMARY KEY,
3 name VARCHAR2(32),
4 shape SDO_GEOMETRY);
Table created.
SQL> INSERT INTO cola_markets VALUES(
2 1,
3 'cola_a',
4 SDO_GEOMETRY(
5 2003,
6 NULL,
7 NULL,
8 SDO_ELEM_INFO_ARRAY(1,1003,3),
9 SDO_ORDINATE_ARRAY(1,1, 5,7)
10 )
11 );
1 row created.
SQL> INSERT INTO cola_markets VALUES(
2 2,
3 'cola_b',
4 SDO_GEOMETRY(
5 2003,
6 NULL,
7 NULL,
8 SDO_ELEM_INFO_ARRAY(1,1003,1),
9 SDO_ORDINATE_ARRAY(5,1, 8,1, 8,6, 5,7, 5,1)
10 )
11 );
1 row created.
SQL> INSERT INTO cola_markets VALUES(
2 3,
3 'cola_c',
4 SDO_GEOMETRY(
5 2003,
6 NULL,
7 NULL,
8 SDO_ELEM_INFO_ARRAY(1,1003,1),
9 SDO_ORDINATE_ARRAY(3,3, 6,3, 6,5, 4,5, 3,3)
10 )
11 );
1 row created.
SQL> commit;
Block:
Error Simulation:
You need to note that even though my table has column named SHAPE, when i used it in for loop , it produced error. Same as your case.
SQL> DECLARE
2 CURSOR TAB
3 IS
4 SELECT name, SDO_GEOM.SDO_AREA(shape, 0.005)
5 FROM cola_markets
6 ORDER BY 2 DESC;
7 BEGIN
8 FOR X IN TAB
9 LOOP
10 DBMS_OUTPUT.PUT_LINE (X.NAME || ' ' || X.shape);
11 END LOOP;
12 END;
13 /
DBMS_OUTPUT.PUT_LINE (X.NAME || ' ' || X.shape);
*
ERROR at line 10:
ORA-06550: line 10, column 52:
PLS-00302: component 'SHAPE' must be declared
ORA-06550: line 10, column 11:
PL/SQL: Statement ignored
Remedy
SQL> DECLARE
2 CURSOR TAB
3 IS
4 SELECT name,
SDO_GEOM.SDO_AREA(shape, 0.005) AS GEOM --<-Alised the column and problem is resolved
5 FROM cola_markets
6 ORDER BY 2 DESC;
7 BEGIN
8 FOR X IN TAB
9 LOOP
10 DBMS_OUTPUT.PUT_LINE (X.NAME || ' ' || X.GEOM);
11 END LOOP;
12 END;
13 /
cola_a 24
cola_b 16.5
cola_c 5
PL/SQL procedure successfully completed.
I am trying to write a query to generates 1000 rows, I have a table called CCHOMEWORK with 2 columns, ID integer (PK) and StudentID varchar which contains the value for all the 1000 rows.
I tried this, but I keep getting errors and does not work
SET #MyCounter = 1
WHILE #MyCounter < 1000
BEGIN
INSERT INTO CCHOMEWORK
(ID)
VALUES
#MyCounter)
set #MyCounter = #MyCounter + 1;
END
This will create 1000 rows:
SELECT LEVEL
FROM DUAL
CONNECT BY LEVEL <= 1000
You can include it in your insert with:
INSERT INTO CCHOMEWORK (ID)
SELECT LEVEL
FROM DUAL
CONNECT BY LEVEL <= 1000
However, if you want to insert multiple sequential IDs you might be better using a sequence:
CREATE SEQUENCE CCHOMEWORK__ID__SEQ
/
Then:
INSERT INTO CCHOMEWORK (ID)
SELECT CC_HOMEWORK__ID__SEQ.NEXTVAL
FROM DUAL
CONNECT BY LEVEL <= 1000;
Or:
BEGIN
FOR i IN 1 .. 1000 LOOP
INSERT INTO CCHOMEWORK (ID) VALUES ( CC_HOMEWORK__ID__SEQ.NEXTVAL );
END LOOP;
END;
/
Syntax for Oracle database (using PL/SQL):
DECLARE
MyCounter NUMBER := 1;
BEGIN
LOOP
EXIT WHEN MyCounter> 1000;
INSERT INTO CCHOMEWORK (ID)
VALUES(MyCounter);
MyCounter := MyCounter+1;
END LOOP;
COMMIT;
END;
/
In my query I'm using a for loop, which displays 1000 three times. I have to increment 1000 for each iteration of the loop, i.e. 1001, 1002,.. same number three times i.e. I want to add into my table 1000,1000,1000,1001,1001,1001 and 1002,1002,1002,
declare
CPName varchar(20) :=1000;
a number;
begin
for a in 1 .. 3 loop
insert into clients values (CPName,null,null);
end loop;
end;
How can I do this?
CPName is a VARCHAR; I assume you want this to be a number, in which case you just add it on.
There's no need to define the variable a either, it's implicitly declared by the LOOP. I would call this i as it's a more common name for an index variable.
declare
CPName integer := 1000;
begin
for i in 1 .. 3 loop
insert into clients values (CPName + i, null, null);
end loop;
end;
You can do this all in a single SQL statement though; there's no need to use PL/SQL.
insert into clients
select 1000 + i, null, null
from dual
cross join ( select level as i
from dual
connect by level <= 3 )
Based on your comments you actually want something like this:
insert into clients
with multiply as (
select level - 1 as i
from dual
connect by level <= 3
)
select 1000 + m.i, null, null
from dual
cross join multiply m
cross join multiply
This will only work if you want the same number of records as you want to increase so maybe you'd prefer to do it this way, which will give you a lot more flexibility:
insert into clients
with increments as (
select level - 1 as i
from dual
connect by level <= 5
)
, iterations as (
select level as j
from dual
connect by level <= 3
)
select 1000 + m.i, null, null
from dual
cross join increments m
cross join iterations
Using your LOOP methodology this would involve a second, interior loop:
declare
CPName integer := 1000;
begin
for i in 1 .. 3 loop
for j in 1 .. 3 loop
insert into clients values (CPName + i, null, null);
end loop;
end loop;
end;
We want to populate a cursor in a procedure that is populated from a select statement in a table.
We created a table named stored_sql_statments with 2 columns, Created_date & Sql_statement.
In that table we will insert a select statement that selects other data from the database based on the clients needs.
Example:
insert into stored_sql_statments
( Created_date , Sql_statement)
values('2/1/2011', 'Select Client_idn , something_neat from cool_table where animal = 'dog' ')
Then in the procedure we have a bunch of code that does what it needs to do, which will never change, but the select statement we have in the cursor changes periodically. We always need to return 2 fields but the rest of the select statement changes.
So now we need to populate the cursor in the procedure with what the select statement coming form the table.
If it was returning only 1 row we have:
declare
x varchar2(600);
rec1 number(10);
rec2 varchar2(15);
begin
execute immediate select Sql_statement into x from stored_sql_statments where created_date = '2/1/2011';
execute immediate x into rec1, rec2;
...
This works, but we don't need it to go into 2 variables we need it to go into a cursor. The real select statement (the above code is just a simple example of what we need to do) is bringing back thousands of records so we need to use a cursor.
Hope this all makes sense
So if anyone knows how to do this, it would be appreciated.
Are you trying to dynamically populate a ref cursor? if so that isn't difficult to do:
set serveroutput on
declare
sql1 varchar2(500);
sql2 varchar2(500);
procedure runProcess(sqlstatement IN varchar2)
AS
refcrs sys_refcursor;
DTE DATE;
LEVELB NUMBER;
BEGIN
dbms_output.put_line(sqlstatement);
open refcrs for
sqlstatement; -- use 'using' to bind those variables
loop
fetch refcrs into DTE, LEVELB;
exit when refcrs%notfound;
dbms_output.put_line(TO_CHAR(DTE,'MMDDYYYY') || '/' || LEVELB);
end loop;
CLOSE REFCRS;
END runProcess;
begin
sql1 := 'select (sysdate - level) a, level b from dual connect by level < 5';
sql2:= 'select (sysdate + level) a, -level b from dual connect by level < 5';
runProcess(SQL1);
runProcess(SQL2);
end ;
/**
select (sysdate - level) a, level b from dual connect by level < 5
02102011/1
02092011/2
02082011/3
02072011/4
select (sysdate + level) a, -level b from dual connect by level < 5
02122011/-1
02132011/-2
02142011/-3
02152011/-4
**/
As you can see, I am dynamically executing two different select statements in the same procedure and outputting their results.
SQL> drop table stack_overflow;
Table dropped.
SQL> create table stack_overflow (created_date date constraint stack_overflow_pk primary key
2 , sql_statement varchar2(4000) not null)
3 /
Table created.
SQL> drop table source_data;
Table dropped.
SQL> create table source_data (vc varchar2(10) null
2 , n number);
Table created.
SQL> insert into source_data values ('a', 100);
1 row created.
SQL> insert into source_data values ('a', 0);
1 row created.
SQL> insert into source_data values ('b', 50);
1 row created.
SQL> insert into source_data values ('c', null);
1 row created.
SQL> insert into stack_overflow values (sysdate - 3/24, 'select vc, sum(n)
2 from source_data
3 group by vc
4 order by vc asc');
1 row created.
SQL> insert into stack_overflow values (sysdate - 2/24 , 'select vc, avg(n)
2 from source_data
3 group by vc
4 order by vc desc');
1 row created.
SQL> insert into stack_overflow values (sysdate - 1/24 , 'select vc, count(*)
2 from source_data
3 group by vc');
1 row created.
SQL> insert into stack_overflow values (sysdate, 'select vc, count(n)
2 from source_data
3 group by vc');
1 row created.
SQL> commit;
Commit complete.
SQL> declare
2 type stack_overflow_type is record (col_1 varchar2(10), col_2 number);
3 type stack_overflow_cur_type is ref cursor return stack_overflow_type;
4 cursor sql_statement_cur is select sql_statement
5 from stack_overflow
6 order by created_date;
7 --
8 function get_cursor_by_date (i_created_date in date) return stack_overflow_cur_type is
9 l_return_cur sys_refcursor; -- stack_overflow_cur_type;
10 l_sql_statement stack_overflow.sql_statement%TYPE;
11 begin
12 select sql_statement into l_sql_statement
13 from stack_overflow
14 where created_date = i_created_date;
15 --
16 open l_return_cur for l_sql_statement;
17 return l_return_cur;
18 end get_cursor_by_date;
19 --
20 procedure process_and_close_cursor (i_cursor in stack_overflow_cur_type) is
21 l_current_rec stack_overflow_type;
22 begin
23 loop
24 fetch i_cursor into l_current_rec;
25 exit when i_cursor%NOTFOUND;
26 dbms_output.put_line('col_1: ' || l_current_rec.col_1
27 || ' col_2: ' || to_number(l_current_rec.col_2));
28 end loop;
29 --
30 close i_cursor;
31 end;
32 --
33 begin
34 for l_row in (select created_date
35 from stack_overflow
36 order by created_date)
37 loop
38 dbms_output.put_line('Processing the SQL statement created on: '
39 || to_char(l_row.created_date, 'YYYY-MM-DD HH24:Mi:SS'));
40 --
41 process_and_close_cursor(get_cursor_by_date(l_row.created_date));
42 --
43 dbms_output.new_line;
44 end loop;
45 end;
46 /
Processing the SQL statement created on: 2011-02-11 10:01:16
col_1: a col_2: 100
col_1: b col_2: 50
col_1: c col_2:
Processing the SQL statement created on: 2011-02-11 11:01:16
col_1: c col_2:
col_1: b col_2: 50
col_1: a col_2: 50
Processing the SQL statement created on: 2011-02-11 12:01:17
col_1: a col_2: 2
col_1: b col_2: 1
col_1: c col_2: 1
Processing the SQL statement created on: 2011-02-11 13:01:17
col_1: a col_2: 2
col_1: b col_2: 1
col_1: c col_2: 0
PL/SQL procedure successfully completed.