How to generate a random char in oracle - oracle

I have a column in my table in which I can insert M or F, according to the user's gender (Male or Female).
I need to generate this column randomly.
This column type is VARCHAR2(1).
I think I should use something like :
TRUNC(dbms_random.value(X,X))
But I dont know how.

Since dbms_random.value returns a value >= 0 and < 1, I'd just use something like this assuming you want an equal probability of getting an M or an F. If you want to produce a skewed distribution, adjust the 0.5 value to whatever you'd like
case when dbms_random.value < 0.5
then 'M'
else 'F'
end

Related

Store big number in Oracle -- Please give an example that can store 9e125

The Oracle doc says one can store a number up to 9.99...9 x 10125 with up to 38 significant digits: https://docs.oracle.com/cd/B28359_01/server.111/b28318/datatype.htm#i16209.
I tried this:
create table bigtest (t number(38,2));
insert into bigtest values (5e40);
But I got
[Error] Execution (8: 29): ORA-01438: value larger than specified precision allowed for this column
It is supposed to be able to store 9.99e125, right? Could any one give an example on how to store 9.99e125?
See DBfiddle here (Oracle 18c).
create table T1 (
anumber number
) ;
insert into t1 ( anumber ) values ( 9.99e125 ) ;
select * from t1 ;
ANUMBER
999000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
One way is to use the number data type without precision and scale specified.
You can specify precision and scale for very large (and also for very small) numbers though. Just keep in mind that negative scale means "that many zeros at the end of an integer" - the total number of digits can be up to precision + absolute value of scale.
In the example below, note that 38 + 84 = 122. The scale must be between -84 and 127, which means that if you do use precision and scale, you can only store numbers < 1e123 - a smaller range than for the full number data type, but still storing very large numbers
create table tbl(x number(38,-84));
insert into tbl values (3.493e121);
select x from tbl;
X
----------
3.4930E+121

INDEX ON Date Range and a Uniqe Number VFP

I need to change a select statement in VFP to do a simple task (select all records in a date range and by a Employee Number). I have tried everything I can think of. I know I can do it in SQL select but I just want to use the table I have and not a cursor.
I'm trying something like this
INDEX ON Date >= ThisForm.DateFrom+Date + Date=< ThisForm.DateTo+ALLTRIM(empid) TAG MyOrder
I know how INDEX ON works but my format is wrong.
Maybe you meant a filtered index. However you don't need to do such a thing. You could simply have an index combining employee number (I suppose it is an integer) and the date. Then you can use a simple for and while scoping clauses or range or alike (you really didn't explain what you would do) - and even an SQL might be the easier way depending on what you would do.
ie: (using date as a column name is a bad idea, but that is another matter)
INDEX ON padl(empId, 10, '1') + dtoc(Date,1) TAG MyOrder
Having such an index you could scan all the records for a given employee in a date range like this:
local lnEmployee, lcStart, lcEnd
lnEmployee = 1 && whatever the employee number is
lcStart = padl(m.lnEmployee, 10, '1')+dtoc(ThisForm.DateFrom,1)
lcEnd = padl(m.lnEmployee, 10, '1')+dtoc(ThisForm.DateTo,1)
scan for padl(empId, 10, '1') + dtoc(Date,1) >= m.lcStart and ;
padl(empId, 10, '1') + dtoc(Date,1) <= m.lcEnd
* whatever
endscan
This would do the same:
local lnEmployee, lcStart, lcEnd
lnEmployee = 1 && whatever the employee number is
lcStart = padl(m.lnEmployee, 10, '1')+dtoc(ThisForm.DateFrom,1)
lcEnd = padl(m.lnEmployee, 10, '1')+dtoc(ThisForm.DateTo,1)
set order to tag myOrder
set range to m.lcStart, m.lcEnd
scan
* whatever
endscan
PS: Actually with indexes on empid and date, a simple scan for ... would do too. ie:
scan for empId = m.lnEmpoyee and ;
Date >= ThisForm.DateFrom and ;
Date <= ThisForm.DateTo
*...
endscan
Welp don't really like filter but if any one has a better idea let me know!
Set Filter To Date >= ThisForm.DateFrom .And. Date =< ThisForm.DateTo .And. EmpID = AllTrim(ThisForm.Combo1.Value)

Oracle 01481. 00000 - "invalid number format model"

This is my query i have used to get the value as money. Nut when concat value getting above exception. The query is
select to_char(b.balance,'9999.'||d.number_of_decimal_places) from balance b, decimal d
Am stuck with this problem.
If you have numeric number_of_decimal_places values like 1, 2, 3 etc. then you are constructing a format model like, for example, '9999.2' instead of '9999.99'.
You can convert that integer value to the format model with rpad or lpad:
select to_char(b.balance,'9999.'||rpad('9', d.number_of_decimal_places, '9'))
from balance b, decimal d
Or for trailing zeros:
select to_char(b.balance,'9999.'||rpad('0', d.number_of_decimal_places, '0'))
from balance b, decimal d
If you have string number_of_decimal_places values like '9', '99', '999' etc. then the concatenation you have will work unless you have an invalid value in one of the rows, which would be any character other than a 9 or a 0.
That includes spaces, which you could have in a varchar2 or char field. Either way you could remove those with trim:
select to_char(b.balance,'9999.'||trim(d.number_of_decimal_places))
from balance b, decimal d
But if you have any other characters then you will need to identify and correct the data in those rows; and even with spaces it would be better to fix the data if it's a varchar2 column.
It would be better to use new-style joins; I haven't changed these examples because it isn't clear if you are doing a cartesian product or have just omitted the join conditions.
If number_of_decimals returns a value like 2 then:
SELECT TO_CHAR( b.balance, RPAD( '9999.', 5 + d.number_of_decimals, '9' ) )
FROM balance b
CROSS JOIN
decimal d
For whatever reason, the concatenation of '9999.'||d.number_of_decimal_places is generating an invalid mask. We can only guess at the actual table values, presence of spaces, or whatever else may be causing issues with what it is doing.
So your solution is to run:
select '9999.'||d.number_of_decimal_places from decimal d
See what the actual format mask is that you are generating, and adjust as necessary.

Select from a loop in Oracle

In oracle 11g, I want to execute a query like that :
In this case, I didn't allowed use Function or Procedure.
I tried to Google it, but i couldn't find a good solution. Almost show me the way to use Function or Store Procedure.
Table X with columns (A,B,C)
With a row in table X i want to select :
Count = B - A;
for(i=0;i<Count;i++)
{
C++;
D = C * A;
}
Expect result : table Y with columns (A,B,C,D)
You are thinking like a 3GL developer. Java (or whatever) only has arrays, so everything is an iteration. But SQL is a set-oriented language: we don't need loops to work on sets of data. Oracle SQL has built-in aggregation functions which allow us to compute values from sets of records.
For instance, this query calculates total remuneration (salary plus commission), number of employees and average salary:
select sum(sal + nvl(comm,0)) as total_renum
, count(*) as total_emps
, avg(sal) as average_salary
from emp
/
Oracle has a comprehensive range of such functions, some of them are really powerful. Find out more. Be sure to check out analytic functions too.
Hmmm, so you subsequently posted a cryptic snippet of code. It's still not clear exactly what you want, but this might produce the outcome for your tab;e Y:
select a
, b
, c
, 0 + ((c+level) * a) as d
from x
connect by level <= (b-a)
/
For each row in table X it will generate (b-a) rows, with a derived value of d. I have assumed a start of 0 for d.

How can I limit the numbers of results being grouped in my Group By in Oracle?

I've got a table of a parameters, values, and times at which those values were recorded.
I've got a procedure which takes in a time, and needs to get the average result of each parameters value in the window of time that is -15/+5 seconds around that time frame. On top of that, I want to make sure that I take the no more than 15 records before the passed in time, and no more than 5 records after it.
For example, maybe I'm recording values of some parameters every second. If I passed in the time 21:30:30, I'd want to get the values between 21:30:15 and 21:30:35. But if I was recording every half second, I'd actually have more parameters that fit in that time frame than I want, and that's where my need to limit my results comes in.
I've read this question and this article which seem pretty related to what I'm trying to do, but unfortunately I'm dealing with Oracle and not MySQL, so I can't use "limit".
I've currently got something that looks like this:
std_values as
(
select
V.ParameterId,
V.NumericValue,
from
ValuesTable V
where
V.ValueSource = pValueSource
and V.Time >= pSummaryTime - 15/86400
and V.Time <= pSummaryTime + 5/86400
)
select
ParameterId,
avg(NumericValue) as NumericValue
from
std_values
group by
ParameterId
pValueSource is just something that lets me filter down which value types I'm looking at, and pSummaryTime is the input time that I'm basing my time frame around. The goal here is to get the 15 records before pSummaryTime that falls within that window, and the 5 after that falls within that window, and use those for the average. Currently I'm not limiting the number of "before" and "after" results though, so I'm ending up with the average of everything that falls into that time window. And without something like "limit", I'm not sure how to do this in Oracle.
Sounds like you want a moving window aggregate function. This is part of the Analytical functions feature of Oracle.
It's not my strong suit, and since you didn't include sample tables/data to build a test case, I'll just point you to the Oracle documentation, here:
http://docs.oracle.com/cd/B14117_01/server.101/b10736/analysis.htm#i1006709
You probably want something like:
AVG(NumericValue) over (order by pSummaryTime RANGE BETWEEN 15 PRECEDING AND 5 FOLLOWING)
but, like I said, not my strong suit, and totally untested, but, I hope it gets the idea across.
Hope that helps.
Thanks to Mark Bobak's answer getting me on the right track, I ended up with this solution.
with
values_before as
(
select
V.ParameterId,
V.NumericValue,
row_number() over (Partition by V.ParameterId order by V.Time desc) as RowNumber
from
ValuesTable V
where
V.ValueSource = pValueSource
and V.Time >= pSummaryTime - 15/86400
and V.Time <= pSummaryTime
),
values_after as
(
select
V.ParameterId,
V.NumericValue,
row_number() over (Partition by V.ParameterId order by V.Time desc) as RowNumber
from
ValuesTable V
where
V.ValueSource = pValueSource
and V.Time <= pSummaryTime + 5/86400
and V.Time > pSummaryTime
),
values_all as
(
select * from values_before where RowNumber <= 15
union all
select * from values_after where RowNumber <= 5
)
select ParameterId, avg(NumericValue) from values_all group by ParameterId
No doubt there's a better way to do this, but it at least seems to be giving the correct result. The key was using an analytical function to set the row number and order for the 15 before and 5 after, and then filtering my results down to just those.

Resources