I am getting the following error:
Warning: compiled but with compilation errors
when I try to create the following function:
Create or replace function Time_Gap (v_date1, v_date2, return varchar2 )
is
difrence_In_Hours varchar2;
difrence_In_minutes varchar2;
difrence_In_seconds varchar2;
begin
difrence_In_Hours := floor(((v_date2-v_date1)*24*60*60)/3600);
difrence_In_minutes := floor((((v_date2-v_date1)*24*60*60) -
floor(((v_date2-v_date1)*24*60*60)/3600)*3600)/60);
difrence_In_seconds := round((((v_date2-v_date1)*24*60*60) -
floor(((v_date2-v_date1)*24*60*60)/3600)*3600 -
(floor((((v_date2-v_date1)*24*60*60) -
floor(((v_date2-v_date1)*24*60*60)/3600)*3600)/60)*60) ));
return difrence_In_Hours || '' HRS '' || difrence_In_minutes || '' MINS '' || difrence_In_seconds
|| '' SECS '';
end ;
/
what am I doing wrong???
Thanks,
a few syntax errors, return type should be outside of input parameters and define type of input:
create or replace function time_gap (v_date1 in date, v_date2 in date)
return varchar2 is
difrence_in_hours varchar2(500);
difrence_in_minutes varchar2(500);
difrence_in_seconds varchar2(500);
begin
difrence_in_hours := floor ( ( (v_date2 - v_date1) * 24 * 60 * 60) / 3600);
difrence_in_minutes :=
floor ( ( ( (v_date2 - v_date1) * 24 * 60 * 60) - floor ( ( (v_date2 - v_date1) * 24 * 60 * 60) / 3600) * 3600) / 60);
difrence_in_seconds :=
round (
( ( (v_date2 - v_date1) * 24 * 60 * 60)
- floor ( ( (v_date2 - v_date1) * 24 * 60 * 60) / 3600) * 3600
- ( floor (
( ( (v_date2 - v_date1) * 24 * 60 * 60) - floor ( ( (v_date2 - v_date1) * 24 * 60 * 60) / 3600) * 3600)
/ 60)
* 60)));
return difrence_in_hours || ' HRS ' || difrence_in_minutes || ' MINS ' || difrence_in_seconds || ' SECS';
end;
/
EDIT:
You have to give us more of the error message. It's not saying anything. This works fine for me:
declare
function time_gap (v_date1 in date, v_date2 in date)
return varchar2 is
difrence_in_hours number;
difrence_in_minutes number;
difrence_in_seconds number;
begin
difrence_in_hours := floor ( ( (v_date2 - v_date1) * 24 * 60 * 60) / 3600);
difrence_in_minutes :=
floor ( ( ( (v_date2 - v_date1) * 24 * 60 * 60) - floor ( ( (v_date2 - v_date1) * 24 * 60 * 60) / 3600) * 3600) / 60);
difrence_in_seconds :=
round (
( ( (v_date2 - v_date1) * 24 * 60 * 60)
- floor ( ( (v_date2 - v_date1) * 24 * 60 * 60) / 3600) * 3600
- ( floor (
( ( (v_date2 - v_date1) * 24 * 60 * 60)
- floor ( ( (v_date2 - v_date1) * 24 * 60 * 60) / 3600) * 3600)
/ 60)
* 60)));
return trim (to_char (difrence_in_hours)) || ' HRS ' || trim (to_char (difrence_in_minutes)) || ' MINS ' || trim (
to_char (
difrence_in_seconds)) || ' SECS';
end;
begin
dbms_output.put_line (time_gap (sysdate, sysdate - 2));
end;
==>
PL/SQL block executed
-48 HRS 0 MINS 0 SECS
Have you created it without checking the syntax? Always check the syntax before doing stuff like that.
As per https://docs.oracle.com/cd/B12037_01/server.101/b10759/create_function.gif just in the first line:
you lack one of [IN][OUT][IN OUT] at every argument
you lack datatypes for every argument
return statement should be outside the parentheses.
At that point I stopped reading.
Related
I have a function below that returns a random INTERVAL between a range of hours, which appears to be working fine but is currently limited to hours only.
I would would like to expand this functionality to also support returning a random INTERVAL for days, minutes by passing in a literal (ie 'DAY', 'MINUTE' or 'SECOND')
For example if I call random_interval (1,4, 'DAY') I would get something like this +000000002 11:24:43.000000000 or if i call random_interval (20,40, 'MINUTE') I would get something like
+000000000 00:24:44.000000000
Thanks in advance to all who answer and for your time and expertise.
CREATE OR REPLACE FUNCTION random_interval(
p_min_hours IN NUMBER,
p_max_hours IN NUMBER
) RETURN INTERVAL DAY TO SECOND
IS
BEGIN
RETURN floor(dbms_random.value(p_min_hours, p_max_hours)) * interval '1' hour
+ floor(dbms_random.value(0, 60)) * interval '1' minute
+ floor(dbms_random.value(0, 60)) * interval '1' second;
END random_interval;
/
SELECT random_interval(1, 10) as random_val FROM dual CONNECT BY level <= 10 order by 1
RANDOM_VAL
+000000000 01:04:03.000000000
+000000000 03:14:52.000000000
+000000000 04:39:42.000000000
+000000000 05:00:39.000000000
+000000000 05:03:28.000000000
+000000000 07:03:19.000000000
+000000000 07:06:13.000000000
+000000000 08:50:55.000000000
+000000000 09:10:02.000000000
+000000000 09:26:44.000000000
Try giving this a shot instead
CREATE OR REPLACE FUNCTION random_interval(
p_min IN NUMBER,
p_max IN NUMBER,
p_period VARCHAR2
) RETURN INTERVAL DAY TO SECOND
IS
BEGIN
IF p_period = 'HOUR' THEN
RETURN floor(dbms_random.value(p_min, p_max)) * interval '1' hour
+ floor(dbms_random.value(0, 60)) * interval '1' minute
+ floor(dbms_random.value(0, 60)) * interval '1' second;
ELSE IF p_period = 'DAY' THEN
RETURN floor(dbms_random.value(p_min, p_max)) * interval '1' day
+ floor(dbms_random.value(0, 24)) * interval '1' hour
+ floor(dbms_random.value(0, 60)) * interval '1' minute
+ floor(dbms_random.value(0, 60)) * interval '1' second;
ELSE IF p_period = 'MINUTE' THEN
RETURN floor(dbms_random.value(p_min, p_max)) * interval '1' minute
+ floor(dbms_random.value(0, 60)) * interval '1' second;
ELSE IF p_period = 'SECOND' THEN
RETURN floor(dbms_random.value(p_min, p_max)) * interval '1' second;
ELSE
RETURN NULL;
END IF;
END random_interval;
/
SELECT random_interval(1, 10, 'DAY') as random_val FROM dual CONNECT BY level <= 10 order by 1
RANDOM_VAL
+000000003 02:46:09.000000000
+000000004 19:19:56.000000000
+000000002 11:24:43.000000000
+000000002 16:20:44.000000000
+000000001 22:24:30.000000000
+000000002 15:14:38.000000000
+000000003 00:48:03.000000000
+000000003 18:08:13.000000000
+000000002 01:05:34.000000000
+000000002 08:12:19.000000000
You don't need a user-defined function as you can use the built-in functions DBMS_RANDOM.VALUE(lower_bound, upper_bound) and NUMTODSINTERVAL(amount, duration):
SELECT NUMTODSINTERVAL(
DBMS_RANDOM.VALUE(1, 3),
'MINUTE'
)
FROM DUAL;
Which will generate a random interval greater than or equal to 1 minute and less than 3 minutes (with a random about of seconds).
If you did want to wrap it into a function then:
CREATE FUNCTION random_interval(
p_min IN NUMBER,
p_max IN NUMBER,
p_duration IN VARCHAR2
) RETURN INTERVAL DAY TO SECOND
IS
BEGIN
RETURN NUMTODSINTERVAL(DBMS_RANDOM.VALUE(p_min, p_max), p_duration);
END;
/
If you want the seconds to be an integer then:
CREATE OR REPLACE FUNCTION random_interval(
p_min IN NUMBER,
p_max IN NUMBER,
p_duration IN VARCHAR2
) RETURN INTERVAL DAY TO SECOND
IS
v_interval INTERVAL DAY TO SECOND := NUMTODSINTERVAL(DBMS_RANDOM.VALUE(p_min, p_max), p_duration);
BEGIN
RETURN ( EXTRACT(DAY FROM v_interval) * 24 * 60 * 60
+ EXTRACT(HOUR FROM v_interval) * 60 * 60
+ EXTRACT(MINUTE FROM v_interval) * 60
+ FLOOR(EXTRACT(SECOND FROM v_interval))
) * INTERVAL '1' SECOND;
END;
/
fiddle
I have this code but its not give appropriate output in kilometers:
CREATE OR REPLACE FUNCTION distance (Lat1 IN NUMBER,
Lon1 IN NUMBER,
Lat2 IN NUMBER,
Lon2 IN NUMBER,
Radius IN NUMBER DEFAULT 3963)
RETURN NUMBER
IS
-- Convert degrees to radians
DegToRad NUMBER := 57.29577951;
BEGIN
RETURN ( NVL (Radius, 0)
* ACOS (
( SIN (NVL (Lat1, 0) / DegToRad)
* SIN (NVL (Lat2, 0) / DegToRad))
+ ( COS (NVL (Lat1, 0) / DegToRad)
* COS (NVL (Lat2, 0) / DegToRad)
* COS (
NVL (Lon2, 0) / DegToRad - NVL (Lon1, 0) / DegToRad))));
END;
You can use:
CREATE FUNCTION haversine_distance(
lat1 IN NUMBER,
long1 IN NUMBER,
lat2 IN NUMBER,
long2 IN NUMBER
) RETURN NUMBER DETERMINISTIC
IS
PI CONSTANT NUMBER := ASIN(1) * 2;
R CONSTANT NUMBER := 6371; -- Approx. radius of the earth in km
PHI1 CONSTANT NUMBER := lat1 * PI / 180;
PHI2 CONSTANT NUMBER := lat2 * PI / 180;
DELTA_PHI CONSTANT NUMBER := (lat2 - lat1) * PI / 180;
DELTA_LAMBDA CONSTANT NUMBER := (long2 - long1) * PI / 180;
a NUMBER;
c NUMBER;
BEGIN
a := SIN(delta_phi/2) * SIN(delta_phi/2) + COS(phi1) * COS(phi2) *
SIN(delta_lambda/2) * SIN(delta_lambda/2);
c := 2 * ATAN2(SQRT(a), SQRT(1-a));
RETURN R * c; -- in kilometres
END;
/
Then you can use:
SELECT haversine_distance(
lat1 => 33.6844, long1 => 73.0479, lat2 => 34.1688, long2 => 73.2215
) AS distance
FROM DUAL;
Which outputs:
DISTANCE
56.1938360038059453699709826045549961225
Or, you can use SDO_GEOM:
SELECT sdo_geom.sdo_distance(
sdo_geometry(2001, 4326, sdo_point_type(73.0479, 33.6844, null), null, null),
sdo_geometry(2001, 4326, sdo_point_type(73.2215, 34.1688, null), null, null),
0.005,
'unit=km'
) AS distance
FROM DUAL
Which outputs:
DISTANCE
56.0766116774312
db<>fiddle here
I am subtracting DATE_IN (16-NOV-20 06:02 PM) from DATE_OUT (17-NOV-20 03:23 PM). I expect the answer to be 21.21 (hours.minutes), but it gives me 21.34 which is the correct hours but not the correct minutes. What am I doing wrong?
function TIME_CALCULATIONFormula return NUMBER is
begin
RETURN ROUND((((:DATE_OUT - :DATE_IN)*24)*60)/60,2);
end;
The value is correct as 0.34 hours = 20.4 minutes.
If you want to return hours as the units and minutes as the decimal then you could use:
function TIME_CALCULATIONFormula return NUMBER
is
diff INTERVAL DAY TO SECOND := (:DATE_OUT - :DATE_IN) DAY TO SECOND;
begin
RETURN EXTRACT( DAY FROM diff ) * 24
+ EXTRACT( HOUR FROM diff )
+ EXTRACT( MINUTE FROM diff ) / 100;
end;
or
function TIME_CALCULATIONFormula return NUMBER
is
diff INTERVAL DAY TO SECOND := CAST(:DATE_OUT AS TIMESTAMP)
- CAST(:DATE_IN AS TIMESTAMP);
begin
RETURN EXTRACT( DAY FROM diff ) * 24
+ EXTRACT( HOUR FROM diff )
+ EXTRACT( MINUTE FROM diff ) / 100;
end;
or
function TIME_CALCULATIONFormula return NUMBER
is
begin
RETURN TRUNC( ( :date_out - :date_in ) * 24 ) +
MOD( ( :date_out - :date_in ) * 24 * 60, 60 ) / 100;
end;
or, if you are not going to have time differences over 24 hours and want trailing zeroes on the numbers:
function TIME_CALCULATIONFormula return VARCHAR2
is
begin
RETURN TO_CHAR( DATE '1900-01-01' + ( :date_out - :date_in ), 'HH24.MI' );
end;
db<>fiddle here
The result you're getting is not "hours.minutes" - it's a decimal representation of the hours. 0.34 hour is 20 minutes and 24 seconds, which seems to be the result you're gunning for, up to the rounding error of the two decimal places you're forcing.
i have a query with this code
return $query
->select([
'lineofpro AS pabrik_line',
'date AS tanggal',
DB::raw('ROUND((100*(COUNT( CASE WHEN `tabTravel Card`.qcresu = "Pass" THEN 1 ELSE NULL END ))/count(`tabTravel Card`.lineofpro))) AS lar_percentage'),
DB::raw("ROUND( IFNULL(SUM(`tabTravel Card`.finga),0) / ( count(`tabTravel Card`.lineofpro) * 200) * 1000000) AS finger_a"),
DB::raw("ROUND( IFNULL(SUM(`tabTravel Card`.fingb),0) / ( count(`tabTravel Card`.lineofpro) * 200) * 1000000) AS finger_b"),
DB::raw("ROUND( IFNULL(SUM(`tabTravel Card`.fingc),0) / ( count(`tabTravel Card`.lineofpro) * 200) * 1000000) AS finger_c"),
DB::raw("ROUND( IFNULL(SUM(`tabTravel Card`.fingd),0) / ( count(`tabTravel Card`.lineofpro) * 200) * 1000000) AS finger_d"),
DB::raw("ROUND( IFNULL(SUM(`tabTravel Card`.finge),0) / ( count(`tabTravel Card`.lineofpro) * 200) * 1000000) AS finger_e"),
DB::raw("ROUND( IFNULL(SUM(`tabTravel Card`.crotchf),0) / ( count(`tabTravel Card`.lineofpro) * 200) * 1000000) AS crotch_f"),
DB::raw("ROUND( IFNULL(SUM(`tabTravel Card`.crotchg),0) / ( count(`tabTravel Card`.lineofpro) * 200) * 1000000) AS crotch_g"),
DB::raw("ROUND( IFNULL(SUM(`tabTravel Card`.crotchh),0) / ( count(`tabTravel Card`.lineofpro) * 200) * 1000000) AS crotch_h"),
DB::raw("ROUND( IFNULL(SUM(`tabTravel Card`.crotchi),0) / ( count(`tabTravel Card`.lineofpro) * 200) * 1000000) AS crotch_i"),
DB::raw("ROUND( IFNULL(SUM(`tabTravel Card`.palmj),0) / ( count(`tabTravel Card`.lineofpro) * 200) * 1000000) AS palm_j"),
DB::raw("ROUND( IFNULL(SUM(`tabTravel Card`.cuffk),0) / ( count(`tabTravel Card`.lineofpro) * 200) * 1000000) AS cuff_k"),
DB::raw("ROUND( IFNULL(SUM(`tabTravel Card`.totalpin),0) / ( count(`tabTravel Card`.lineofpro) * 200) * 1000000) AS total_pinhole"),
])
->where([
['date', '>=', $beginDate],
['date', '<=', $endDate],
$this->generatePlanCodeFilter($plan, $line),
])
->groupBy([
'lineofpro',
'date',
]);
is it possible to query this without DB::raw?
i try to create dynamic query
but using an array to return the select value with DB::raw will only return '' string
You can define your fields as accessors and move your calculation logic there.
Then you can build the query using Eloquent.
Ex:
public function getTotalPinholeAttribute()
{
// calculation logic here
}
You can read more about accessors here: https://laravel.com/docs/8.x/eloquent-mutators#defining-an-accessor
I need to convert ISBN from 10 digits to 13 digits. I tried but not getting correct result.This is the code that I have tried and its not giving correct result.
Create or replace FUNCTION F_ISBN_CONV_13 (ISBN_10 IN VARCHAR2) RETURN VARCHAR2
AS
V_ISBN_13 VARCHAR2(13);
BEGIN
SELECT
CASE WHEN LENGTH(ISBN_10) = 10 THEN
CASE WHEN SUBSTR(ISBN_10,1,3) = '801' THEN
'201' ||
SUBSTR(ISBN_10,1,9) ||
SUBSTR(
(
10 -
SUBSTR(
(
(
2 +
1 +
SUBSTR(ISBN_10,2,1) +
SUBSTR(ISBN_10,4,1) +
SUBSTR(ISBN_10,6,1) +
SUBSTR(ISBN_10,8,1)
) +
(
(
0 +
SUBSTR(ISBN_10,1,1) +
SUBSTR(ISBN_10,3,1) +
SUBSTR(ISBN_10,5,1) +
SUBSTR(ISBN_10,7,1) +
SUBSTR(ISBN_10,9,1)
)
) * 3
)
,-1
,1
)
)
,-1
,1
)
ELSE
'978' ||
SUBSTR(ISBN_10,1,9) ||
SUBSTR(
(
10-
SUBSTR(
(
(
9 +
8 +
SUBSTR(ISBN_10,2,1) +
SUBSTR(ISBN_10,4,1) +
SUBSTR(ISBN_10,6,1) +
SUBSTR(ISBN_10,8,1)
) +
(
(
7 +
SUBSTR(ISBN_10,1,1) +
SUBSTR(ISBN_10,3,1) +
SUBSTR(ISBN_10,5,1) +
SUBSTR(ISBN_10,7,1) +
SUBSTR(ISBN_10,9,1)
)
) * 3
)
,1
,1
)
)
,-1
,1
)
END
ELSE
ISBN_10
END INTO V_ISBN_13
FROM
DUAL;
RETURN V_ISBN_13;
EXCEPTION
--< code >
END F_ISBN_CONV_13;
I think the following code should be self-explanatory. For details see the Wikipedia links.
-- https://en.wikipedia.org/wiki/International_Standard_Book_Number#ISBN-10_to_ISBN-13_conversion
create or replace function isbn10_to_13(p_isbn10 in varchar2)
return varchar2 is
v_isbn10_digits constant varchar2(9) := substr(replace(p_isbn10, '-', ''), 1, 9);
v_isbn13 varchar2(17) := '978-' || substr(p_isbn10, 1, length(p_isbn10) - 1);
v_checkdigit number;
begin
-- https://en.wikipedia.org/wiki/International_Standard_Book_Number#ISBN-13_check_digit_calculation
v_checkdigit :=
(1 * 9)
+ (3 * 7)
+ (1 * 8)
+ (3 * to_number(substr(v_isbn10_digits, 1, 1)))
+ (1 * to_number(substr(v_isbn10_digits, 2, 1)))
+ (3 * to_number(substr(v_isbn10_digits, 3, 1)))
+ (1 * to_number(substr(v_isbn10_digits, 4, 1)))
+ (3 * to_number(substr(v_isbn10_digits, 5, 1)))
+ (1 * to_number(substr(v_isbn10_digits, 6, 1)))
+ (3 * to_number(substr(v_isbn10_digits, 7, 1)))
+ (1 * to_number(substr(v_isbn10_digits, 8, 1)))
+ (3 * to_number(substr(v_isbn10_digits, 9, 1)))
;
v_checkdigit := 10 - mod(v_checkdigit, 10);
if v_checkdigit = 10
then
v_checkdigit := 0;
end if;
return v_isbn13 || v_checkdigit;
end;
/
Example
col isbn10 for a13
col isbn13 for a17
with isbn10(i10) as (
select '0-30-640615-x' from dual union all
select '0-07-223065-7' from dual union all
select '0-596-51446-x' from dual
)
select isbn10.i10 as isbn10, isbn10_to_13(isbn10.i10) as isbn13
from isbn10;
ISBN10 ISBN13
------------- -----------------
0-30-640615-x 978-0-30-640615-7
0-07-223065-7 978-0-07-223065-9
0-596-51446-x 978-0-596-51446-4
I have got the correct result.
Create or replace FUNCTION F_ISBN_CONV_13 (ISBN_10 IN VARCHAR2) RETURN VARCHAR2
AS
V_ISBN_13 VARCHAR2(13);
BEGIN
SELECT
CASE WHEN LENGTH(ISBN_10) = 10 THEN
CASE WHEN SUBSTR(ISBN_10,1,3) = '801' THEN
'201' ||
SUBSTR(ISBN_10,1,9) ||
SUBSTR(
(
10 -
SUBSTR(
(
(
2 +
1 +
SUBSTR(ISBN_10,2,1) +
SUBSTR(ISBN_10,4,1) +
SUBSTR(ISBN_10,6,1) +
SUBSTR(ISBN_10,8,1)
) +
(
(
0 +
SUBSTR(ISBN_10,1,1) +
SUBSTR(ISBN_10,3,1) +
SUBSTR(ISBN_10,5,1) +
SUBSTR(ISBN_10,7,1) +
SUBSTR(ISBN_10,9,1)
)
) * 3
)
,-1
,1
)
)
,-1
,1
)
ELSE
'978' ||
SUBSTR(ISBN_10,1,9) ||
SUBSTR(
(
10-
SUBSTR(
(
(
9 +
8 +
SUBSTR(ISBN_10,2,1) +
SUBSTR(ISBN_10,4,1) +
SUBSTR(ISBN_10,6,1) +
SUBSTR(ISBN_10,8,1)
) +
(
(
7 +
SUBSTR(ISBN_10,1,1) +
SUBSTR(ISBN_10,3,1) +
SUBSTR(ISBN_10,5,1) +
SUBSTR(ISBN_10,7,1) +
SUBSTR(ISBN_10,9,1)
)
) * 3
)
,-1 -- Need to change here from '1' to '-1'
,1
)
)
,-1
,1
)
END
ELSE
ISBN_10
END INTO V_ISBN_13
FROM
DUAL;
RETURN V_ISBN_13;
EXCEPTION
--< code >
END F_ISBN_CONV_13;