I can't seem to use DBMSRAND.
GRANT EXECUTE ON DBMS_RANDOM TO *schema*;
ORA-01031: insufficient priveleges
Not sure why as I'm using the DBadmin schema to grant, but does anyone have a work around for random number generation in oracle sql?
If you're really desperate you could roll your own, eg here's an LG one
SQL> create or replace
2 package global is
3 a number := to_number(to_char(systimestamp,'FF'));
4 end;
5 /
Package created.
SQL>
SQL> create or replace
2 function rand(r in number) return number is
3
4 mill constant number:=100000000;
5 ten_thou constant number:=10000;
6 lg_num constant number:=31415821;
7
8 v number;
9 w number;
10 x number;
11 y number;
12 z number;
13
14 begin
15 w:=trunc(global.a/ten_thou);
16 x:=mod(global.a,ten_thou);
17 y:=trunc(lg_num/ten_thou);
18 z:=mod(lg_num,ten_thou);
19 v := mod((mod(x*y+w*z,ten_thou)*ten_thou+x*z),mill);
20 global.a:=mod(v+1,mill);
21 return(trunc((trunc(global.a/ten_thou)*r)/ten_thou));
22 end;
23 /
Function created.
SQL>
SQL> select rand(100) from dual;
RAND(100)
----------
21
SQL> select rand(100) from dual;
RAND(100)
----------
72
SQL> select rand(100) from dual;
RAND(100)
----------
1
SQL> select rand(100) from dual;
RAND(100)
----------
43
but random number generation is a science so you should be aware of the all of the limitations and drawbacks of rolling your own.
https://en.wikipedia.org/wiki/Linear_congruential_generator
So your best bet is to get appropriate access to the appropriate packages.
Related
as scott, write a policy function to implement the following policy: Users can access id and score without any restriction, but they can only access their own name (using the masking behavior). sysdba should be able to access any data without restrictions.
Table name: RATING
ID NAME SCORE
---------- ---------------------------------------- ----------
1 SYS 4
2 RHWTT 5
3 LEO 4
4 MOD3_ADMIN 5
5 VPD674 4
6 SCOTT 5
7 HR 4
8 OE 5
9 PM 4
10 IX 5
11 SH 4
12 BI 5
13 IXSNEAKY 4
14 DVF 5
command:
SCOTT > CREATE FUNCTION SEC_FUNCTION ( p_schema IN VARCHAR2, p_object IN VARCHAR2)
RETURN VARCHAR2
AS
BEGIN
RETURN 'NAME = SYS_CONTEXT (''USERENV'',''SESSION_USER'')';
END;
/
Function created.
SCOTT > EXECUTE DBMS_RLS.ADD_POLICY (
OBJECT_SCHEMA => 'SCOTT',
OBJECT_NAME => 'RATING',
POLICY_NAME => 'SEC_POLICY',
FUNCTION_SCHEMA => 'SCOTT',
POLICY_FUNCTION => 'SEC_FUCTION',
SEC_RELEVANT_COLS => 'NAME',
SEC_RELEVANT_COLS_OPT => DBMS_RLS.ALL_ROWS);
PL/SQL procedure successfully completed.
ERROR:
select * from rating;
ORA-28110: policy function or package SCOTT.SEC_FUCTION has error
It works if you remove the typo 'SEC_FUCTION':
BEGIN
DBMS_RLS.ADD_POLICY (
OBJECT_SCHEMA => 'SCOTT',
OBJECT_NAME => 'RATING',
POLICY_NAME => 'SEC_POLICY',
FUNCTION_SCHEMA => 'SCOTT',
POLICY_FUNCTION => 'SEC_FUNCTION',
SEC_RELEVANT_COLS => 'NAME',
SEC_RELEVANT_COLS_OPT => DBMS_RLS.ALL_ROWS);
END;
/
Then you get the desired result:
SELECT * FROM rating;
ID NAME SCORE
1 null 4
2 null 5
3 null 4
4 null 5
5 null 4
6 SCOTT 5
7 null 4
8 null 5
9 null 4
10 null 5
11 null 4
12 null 5
13 null 4
14 null 5
I had generate 1 round numbers:
set serveroutput on
declare
i number(9);
x number(9) := 0;
begin
for i in 0..7 loop
DBMS_OUTPUT.PUT_LINE(x);
x:=x+1;
end loop;
end;
Result is: 0, 1, 2, 3....7
My next round should generate numbers: 10, 11, 12....17
Total output should looks as below:
0 1 2 3 4 5 6 7
10 11 12 13 14 15 16 17
20 21 22 23 24 25 26 27
...
80 81 82 83 84 85 86 87
How can I jump 3 between each round? I will increase my counter till 81.
set serveroutput on
declare
i number(9);
y number(9);
x number(9) := 0;
z number(9) := 0;
begin
for y in 0..8 loop
for i in 0..7 loop
DBMS_OUTPUT.PUT_LINE(x);
x := z+i+1;
end loop;
z := z + 10;
x := z;
end loop;
end;
Looks like you want to output 9 times 7 numbers. That is one loop running 9 times and inside a loop that runs 7 times, I'd say. E.g.
begin
for i in 0..8 loop
for j in 0..7 loop
dbms_output.put(i * 10 + j);
dbms_output.put(' ');
end loop;
dbms_output.put_line('');
end loop;
end;
How about such a WHILE loop?
SQL> set serveroutput on
SQL> declare
2 i number := 0;
3 begin
4 while i < 30
5 loop
6 dbms_output.put_line (i);
7
8 i := i + case when substr (to_char (i), -1) >= 7 then 3 else 1 end;
9 end loop;
10 end;
11 /
0
1
2
3
4
5
6
7
10
11
12
13
14
15
16
17
20
21
22
23
24
25
26
27
PL/SQL procedure successfully completed.
SQL>
The Logic is: if i mod 10 = 7 then increment by 3 otherwise increment by one. Thus,
set serveroutput on
declare
i number(9);
x number(9) := 0;
begin
for i in 0..100 loop
DBMS_OUTPUT.PUT_LINE(x);
IF mod(i,10) = 7 THEN
x := x +3;
ELSE
x:=x+1;
END IF;
end loop;
end;
I have a query below, I want it to sort the data by id, but it doesn't sort at all.
Select distinct ec.category,ec.id
from print ec
order by ec.id asc
What could be the reason?
this is the output :
Looking at your data, the column data type is a varchar, aka 'text'.
If it is text, it sorts like text, according to the place the character occurs in the character set used.
So each column is ordered on the first character, then the second, etc. So 2 comes after 11.
Either make the column a numeric data type, like number, or use to_number in the sorting:
select distinct ec.category,ec.id
from print ec
order by to_number(ec.id)
The difference lies in the way varchar and number are sorted. in your case, since you have used varchar data type to store number, the sorting is done for the ASCII values.
NUMBERS when sorted as STRING
SQL> WITH DATA AS(
2 SELECT LEVEL rn FROM dual CONNECT BY LEVEL < = 11
3 )
4 SELECT rn, ascii(rn) FROM DATA
5 order by ascii(rn)
6 /
RN ASCII(RN)
---------- ----------
1 49
11 49
10 49
2 50
3 51
4 52
5 53
6 54
7 55
8 56
9 57
11 rows selected.
SQL>
As you can see, the sorting is based on the ASCII values.
NUMBER when sorted as a NUMBER itself
SQL> WITH DATA AS(
2 SELECT LEVEL rn FROM dual CONNECT BY LEVEL < = 11
3 )
4 SELECT rn, ascii(rn) FROM DATA
5 ORDER BY rn
6 /
RN ASCII(RN)
---------- ----------
1 49
2 50
3 51
4 52
5 53
6 54
7 55
8 56
9 57
10 49
11 49
11 rows selected.
SQL>
How to fix the issue?
Change the data type to NUMBER. As a workaround, you could use to_number.
Using to_number -
SQL> WITH DATA AS(
2 SELECT to_char(LEVEL) rn FROM dual CONNECT BY LEVEL < = 11
3 )
4 SELECT rn, ascii(rn) FROM DATA
5 ORDER BY to_number(rn)
6 /
RN ASCII(RN)
--- ----------
1 49
2 50
3 51
4 52
5 53
6 54
7 55
8 56
9 57
10 49
11 49
11 rows selected.
SQL>
Make sure the type of your "id" column is int. (integer = number)
Right now it is probably text, char or varchar(for text, strings).
You can't sort numbers alphabetically or strings/text chronologically like you are trying now.
when you sort a string datatype that has in values it produce result as
1
10
11
2
21 etc...
Hence
Change your Id datatype to int/bigint
You can only just cast/convert the datatype in a query
Select distinct ec.category,ec.id
from print ec
order by cast(ec.id as int) asc
I need to create a stored procedure in SQL Server that accepts the following two parameters:
A select statement returning 1 column.
A number of columns.
The stored procedure would then run the select statement and return the result of the select statement with the values of the single column split into the given amount of columns per row.
Here are some examples:
exec stored_proc ‘select id from table where id between 1 and 20’, 5
The result of the select would be:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
The result of the stored procedure call would be:
1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16 17 18 19 20
Or the call could be:
exec stored_proc ‘select id from table where id between 1 and 20’, 10
Giving the result of:
1 2 3 4 5 6 7 8 9 10
11 12 13 14 15 16 17 18 19 20
Though I'm not sure you should be doing this in SQL, it can be done.
I think the way to do it would be do create a cursor and use it's iterations to build a dynamic SQL statement.
During each iteration, add each piece of data as a new column (field) and when you reach the number of columns add something like Union Select
Given the following table
create table tmp_test as
select mod(level, 5) as n
from dual
connect by level <= 10
;
and this function
create or replace function test_deterministic (Pn in number
) return number deterministic is
begin
dbms_output.put_line(Pn);
dbms_lock.sleep(1);
return Pn;
end;
It executes 6 times, taking 6 seconds:
SQL> select test_deterministic(n) from tmp_test;
TEST_DETERMINISTIC(N)
---------------------
1
2
3
4
0
1
2
3
4
0
10 rows selected.
1
2
3
4
0
1
Elapsed: 00:00:06.02
I would have expected this to execute 5 times. If I run this SELECT statement in SQL Developer or PL/SQL Developer it only executes 5 times. Equally, if I run this in Pl/SQL it executes 5 times:
SQL> begin
2 for i in ( select test_deterministic(n) from tmp_test ) loop
3 null;
4 end loop;
5 end;
6 /
1
2
3
4
0
Elapsed: 00:00:05.01
Why is this function executed 6 times when called in SQL from SQL*Plus? I expected it to execute 5 times instead.
I'm on version 11.2.0.3.5 and the SQL*Plus client is release 11.2.0.1.0 (64bit).
Blame SQL*Plus, Ben. Your function works, in this situation, correctly. The extra value(1) you see is there because of arraysize value, and, mostly because of the way how SQL*Plus fetches rows. It first fetches first row and only then it starts to use arraysize for subsequent fetches. Every new fetch is a new database call, which forces your deterministic function to be evaluated. Try to set the arraysize to 1 or 2(same effect) and execute your select statement. The first row returns, and then, arraysize comes to play and each subsequent fetch will return couple of rows:
Arraysize is set to 1(two in fact)
SQL> set arraysize 1;
SQL> select test_deterministic(n) from tmp_test;
TEST_DETERMINISTIC(N)
---------------------
1
2
3
4
0
1
2
3
4
0
10 rows selected.
1
2
3
4
0
1
2
3
4
0
Elapsed: 00:00:10.10
The same query with much larger arraysize:
SQL> set arraysize 50;
SQL> select test_deterministic(n) from tmp_test;
TEST_DETERMINISTIC(N)
---------------------
1
2
3
4
0
1
2
3
4
0
10 rows selected.
1
2
3
4
0
1
Elapsed: 00:00:06.06
SQL> spool off;
Any other client, whether it's SQL Developer or PL/SQL Developer lacks such behavior and gives correct output.