"ORA-24344: success with compilation error" - plsql

i just started with pl/sql and found this error. can someone tell what the problem is.
create or replace procedure findmin(x in number, y in number, z out
number)
is
begin
if x<y then z:= x;
else z:=y;
end if;
end;
set serveroutput on;
declare
a number;
b number;
c number;
begin
a:=23;
b:=46;
findmin(a, b, c);
dbms_output.put_line('minimum of the 2 numbers is' || c);
end;

There is NO error. Might be you re not executing it correctly.
SQL> CREATE OR REPLACE PROCEDURE findmin(
2 x IN NUMBER,
3 y IN NUMBER,
4 z OUT NUMBER)
5 IS
6 BEGIN
7
8 IF x<y THEN
9 z:= x;
10
11 ELSE
12 z:=y;
13
14 END IF;
15
16 END;
17
18 /
Procedure created.
SQL> set serveroutput on;
SQL> DECLARE
2 a NUMBER;
3 b NUMBER;
4 c NUMBER;
5 BEGIN
6 a:=23;
7 b:=46;
8 findmin(a, b, c);
9 dbms_output.put_line('minimum of the 2 numbers is ' || c);
10
11 END;
12 /
minimum of the 2 numbers is 23
PL/SQL procedure successfully completed.

Related

Oracle 'MEMBER OF' function

I was trying to use MEMBER OF function but receiving error "PLS-00306: wrong number or types
of arguments in call to 'MEMBER OF'.". Please let me know, how i can use 'MEMBER OF' function
correctly, i want to avoid iteration.
Table: Employee
EMP_ID EMP_NAME EMP_BRANCH
1 Oliver 104
2 Harry 105
3 Jack 105
4 Jacob 103
DECLARE
TYPE emp_name_type IS RECORD(emp_name employee.emp_name%type);
TYPE emp_name_table_type IS TABLE OF emp_name_type;
emp_name_table_type_array emp_name_table_type;
BEGIN
select x.emp_name BULK COLLECT
into emp_name_table_type_array
from (
select e.emp_name, min(e.emp_id) as emp_id
from employee e
where e.emp_branch = 105
group by e.emp_name
) x
order by x.emp_id;
-- receiving output Harry + Jack
FOR i IN emp_name_table_type_array.FIRST .. emp_name_table_type_array.LAST LOOP
dbms_output.put_line(emp_name_table_type_array(i).emp_name);
END LOOP;
-- here i want to implement something like bellow using 'Member OF' function
IF 'Harry' MEMBER OF emp_name_table_type_array THEN
dbms_output.put_line('yes');
ELSE
dbms_output.put_line('no');
END IF;
END;
/
Thank you
You have one type declaration too many (and have to fix the first dbms_output.put_line call because of that).
SQL> DECLARE
2 TYPE emp_name_table_type IS TABLE OF employee.emp_name%type;
3 emp_name_table_type_array emp_name_table_type;
4 BEGIN
5 select x.emp_name BULK COLLECT
6 into emp_name_table_type_array
7 from (
8 select e.emp_name, min(e.emp_id) as emp_id
9 from employee e
10 where e.emp_branch = 105
11 group by e.emp_name
12 ) x
13 order by x.emp_id;
14
15 -- receiving output Harry + Jack
16 FOR i IN emp_name_table_type_array.FIRST .. emp_name_table_type_array.LAST LOOP
17 dbms_output.put_line(emp_name_table_type_array(i));
18 END LOOP;
19
20 -- here i want to implement something like bellow using 'Member OF' function
21 IF 'Harry' MEMBER OF emp_name_table_type_array THEN
22 dbms_output.put_line('yes');
23 ELSE
24 dbms_output.put_line('no');
25 END IF;
26 END;
27 /
Harry
Jack
yes
PL/SQL procedure successfully completed.
SQL>

how to resolve Warning: Trigger created with compilation errors

I'm trying to create a trigger where if the total number of customer reaches 10 then it should raise an error
This is my plsql code:
SQL> CREATE OR REPLACE TRIGGER customer_count_check
2 BEFORE INSERT OR UPDATE ON customer2
3 FOR EACH ROW
4 DECLARE
5 count_customer NUMBER(5);
6 BEGIN
7 SELECT COUNT(*) INTO count_customer FROM customer2
8 WHERE cusid = :new.cusid;
9 IF count_customer > 10 THEN
10 Raise_application_error(-20000,'Maximum customer count:' ||
11 count_customer || 'reached');
12 ENDIF;
13 END;
14 /
and this is the warning I'm getting:
Warning: Trigger created with compilation errors.
SQL> SHOW ERROR;
Errors for TRIGGER CUSTOMER_COUNT_CHECK:
LINE/COL ERROR
-------- -----------------------------------------------------------------
10/4 PLS-00103: Encountered the symbol ";" when expecting one of the
following:
if
I sometimes also wish the error messages would be more clear.
In line 12:
Wrong
ENDIF;
Right:
END IF;
Also the line counting is sometimes not so intuitive. It says 10/4 while it seems to be 12/4. But the first 2 lines are not considered part of the triggers code and so the counting is line 10 column 4.
SQL> CREATE OR REPLACE TRIGGER customer_count_check
2 BEFORE INSERT OR UPDATE ON customer2
3 FOR EACH ROW
4 DECLARE
5 count_customer NUMBER(5);
6 BEGIN
7 SELECT COUNT(*) INTO count_customer FROM customer2
8 WHERE cusid = :new.cusid;
9 IF count_customer > 10 THEN
10 Raise_application_error(-20000,'Maximum customer count:' ||
11 count_customer || 'reached');
12 END IF;
13 END;
14 /

Number of divisiors upto 10^6

I have been trying to solve this problem.
http://www.spoj.com/problems/DIV/
for calcuating interger factors, I tried two ways
first: normal sqrt(i) iteration.
int divCount = 2;
for (int j = 2; j * j <= i ; ++j) {
if( i % j == 0) {
if( i / j == j )
divCount += 1;
else
divCount += 2;
}
}
second: Using prime factorization (primes - sieve)
for(int j = 0; copy != 1; ++j){
int count = 0;
while(copy % primes.get(j) == 0){
copy /= primes.get(j);
++count;
}
divCount *= ( count + 1);}
While the output is correct, I am getting TLE. Any more optimization can be done? Please help. Thanks
You're solving the problem from the wrong end. For any number
X = p1^a1 * p2^a2 * ... * pn^an // p1..pn are prime
d(X) = (a1 + 1)*(a2 + 1)* ... *(an + 1)
For instance
50 = 4 * 25 = 2^2 * 5^2
d(50) = (1 + 2) * (1 + 2) = 9
99 = 3^2 * 11^1
d(99) = (2 + 1) * (1 + 1) = 6
So far so good you need to generate all the numbers such that
X = p1^a1 * p2^a2 <= 1e6
such that
(a1 + 1) is prime
(a2 + 1) is prime
having a table of prime numbers from 1 to 1e6 it's a milliseconds task
It is possible to solve this problem without doing any factoring. All you need is a sieve.
Instead of a traditional Sieve of Eratosthenes that consists of bits (representing either prime or composite) arrange your sieve so each element of the array is a pointer to an initially-null list of factors. Then visit each element of the array, as you would with the Sieve of Eratosthenes. If the element is a non-null list, it is composite, so skip it. Otherwise, for each element and for each of its powers less than the limit, add the element to each multiple of the power. At the end of this process you will have a list of prime factors of the number. That wasn't very clear, so let me give an example for the numbers up to 20. Here's the array, initially empty:
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
Now we sieve by 2, adding 2 to each of its multiples:
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
2 2 2 2 2 2 2 2 2 2
Since we also sieve by powers, we add 2 to each multiple of 4:
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
2 2 2 2 2 2 2 2 2 2
2 2 2 2 2
And likewise, by each multiple of 8 and 16:
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
2 2 2 2 2 2 2 2 2 2
2 2 2 2 2
2 2
2
Now we're finished with 2, so we go to the next number, 3. The entry for 3 is null, so we sieve by 3 and its power 9:
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
2 2 2 2 2 2 2 2 2 2
2 2 2 2 2
2 2
2
3 3 3 3 3 3
3 3
Then we sieve by 5, 7, 11, 13, 17 and 19:
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
2 2 2 2 2 2 2 2 2 2
2 2 2 2 2
2 2
2
3 3 3 3 3 3
3 3
5 5 5 5
7 7
11
13
17
19
Now we have a list of all the prime factors of all the numbers less than the limit, computed by sieving rather than factoring. It's easy then to calculate the number of divisors by scanning the lists; count the number of occurrences of each factor in the list, add 1 to each total, and multiply the results. For instance, 12 has 2 factors of 2 and 1 factor of 3, so take (2+1) * (1+1) = 3 * 2 = 6, and indeed 12 has 6 factors: 1, 2, 3, 4, 6 and 12.
The final step is to check if the number of divisors has exactly two factors. That's easy: just look at the list of prime divisors and count them.
Thus, you have solved the problem without doing any factoring. That ought to be very fast, just a little bit slower than a traditional Sieve of Eratosthenes and very much faster than factoring each number to compute the number of divisors.
The only potential problem is space consumption for the lists of prime factors. But you shouldn't worry too much about that; the largest list will have only 19 factors (since the smallest factor is 2, and 2^20 is greater than your limit), and 78498 of the lists will have only a single factor (the primes less than a million).
Even though the above mentioned problem doesn't require calculating number of divisors, It still can be solved by calculating d(N) (divisors of N) within the time limit (0.07s).
The idea is to pretty simple. Keep track of smallest prime factor f(N) of every number. This can be done by standard prime sieve. Now, for every number i keep dividing it by f(i) and increment the count till i = 1. You now have set of prime counts for each number i.
int d[MAX], f[MAX];
void sieve() {
for (int i = 2; i < MAX; i++) {
if (!f[i]) {
f[i] = i;
for (int j = i * 2; j < MAX; j += i) {
if (!f[j]) f[j] = i;
}
}
d[i] = 1;
}
for (int i = 1; i < MAX; i++) {
int k = i;
while (k != 1) {
int s = 0, fk = f[k];
while (k % fk == 0) {
k /= fk; s++;
}
d[i] *= (s + 1);
}
}
}
Once, d(N) is figured out, rest of the problem becomes much simpler. Keeping a smallest prime factor of every number also helps to solve lots of other problems.

Counting the repeated values of a variable by id

I am at my very first steps with SAS, and I incurred into the following problem which I am not able to solve.
Suppose my dataset is:
data dat;
input id score gender;
cards;
1 10 1
1 10 1
1 9 1
1 9 1
1 9 1
1 8 1
2 9 2
2 8 2
2 9 2
2 9 2
2 10 2
;
run;
What I need to do is to count the number of times the score variable takes values 8, 9 and 10 by id. Then to create the newly variables count8, count9 and count10 such that I can get the following output:
id gender count8 count9 count10
1 1 1 3 2
2 2 1 3 1
How would you suggest ot proceed? Any help would be greatly appreciated.
Lots of ways to do that. Here's a simple one data step approach.
data want;
set dat;
by id;
if first.id then do;
count8=0;
count9=0;
count10=0;
end;
select(score);
when(8) count8+1;
when(9) count9+1;
when(10) count10+1;
otherwise;
end;
if last.id then output;
keep id count8 count9 count10;
run;
SELECT...WHEN is a shortening of a bunch of IF statements, basically (like CASE..WHEN in other languages).
Gender should be dropped, by the way, unless it's always the same by ID (or unless you intend to count by it.)
A more flexible approach than this is to use a PROC FREQ (or PROC MEANS or ...) and transpose it:
proc freq data=dat noprint;
tables id*score/out=want_pre;
run;
proc transpose data=want_pre out=want prefix=count;
by id;
id score;
var count;
run;
If you really only want 8,9,10 and want to drop records less than 8, do so in the data=dat part of PROC FREQ:
proc freq data=dat(where=(score ge 8)) noprint;

dynamic pivoting

I am hoping to get some help on a view which needs to be pivoted, I am not sure though.
View is in following format:
CASE CASE_ORDER MANAGER MONTHLY_CASES FISCAL_CASES
case_1 1 John 15 84
case_1 1 Jeff 10 80
case_2 2 John 20 90
case_2 2 Jeff 13 65
case_3 3 John 7 72
case_3 3 Jeff 17 70
My final chart should look like the following:
CASE CASE_ORDER JOHN_CURR_MONTH JOHN_FY JOHN_CURR_MONTH JOHN_FY
case_1 1 15 84 10 80
case_2 2 20 90 13 65
case_3 3 7 72 17 70
My problem is that managers can change and so does the number of managers from month to month,
so I can't hard code their names (ie. 'mgr1' and 'mgr2') and use DECODE. It has to be dynamic...
Thanks for your suggestion, I figured it out. In fact there is a similar answer in tom kyte's blog (http://www.oracle.com/technetwork/issue-archive/2012/12-jul/o42asktom-1653097.html) which I modified for my purpose. Here it is:
CREATE OR REPLACE PROCEDURE dynamic_pivot_proc ( p_cursor IN OUT SYS_REFCURSOR )
AS
l_query LONG := 'SELECT case_order, case';
BEGIN
FOR x IN (SELECT DISTINCT manager FROM test_table ORDER BY 1 )
LOOP
l_query := l_query ||
REPLACE( q'|, MAX(DECODE(manager,'$X$',monthly_total)) $X$_current_month|',
'$X$', dbms_assert.simple_sql_name(x.manager) ) ||
REPLACE( q'|, MAX(DECODE(manager,'$X$',fiscal_total)) $X$_fy|',
'$X$', dbms_assert.simple_sql_name(x.manager) );
END LOOP;
l_query := l_query || ' FROM test_table
GROUP BY case_order, case
ORDER BY case_order ';
OPEN p_cursor FOR l_query;
END;
SQL> variable x refcursor;
SQL> exec dynamic_pivot_proc( :x );
SQL> print x
CASE CASE_ORDER JEFF_CURRENT_MONTH JEFF_FY JOHN_CURRENT_MONTH JOHN_FY
1 case_1 10 80 15 84
2 case_2 13 65 20 90
3 case_3 17 70 7 72
Now the thing is instead of printing the result I want to store it in a view. How do I achieve that? I tried to modify the line
l_query LONG := 'SELECT case_order, case';
with
l_query LONG := 'CREATE OR REPLACE VIEW SELECT case_order, case';
Needless to say that it did not work because CREATE OR REPLACE is a DDL statement, so some how I have to use EXECUTE IMMEDIATE.
Any suggestion? Thanks in advance.

Resources