oracle Converting ISBN10 to ISBN13 - oracle

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;

Related

Not a singlenot a single-group group function ORA-00937: Oracle

What can I do at the highest level to change this error
ORA-00937: not a single-group group function
00937. 00000 - "not a single-group group function"
*Cause:
*Action:
Error at Line: 3 Column: 5
select
year,
Net_TWRR_PERIOD,
round(((CASE WHEN MOD(SUM(CASE WHEN
( Net_TWRR_PERIOD ) <0 then 1 else 0 end ), 2 )=1 THEN -1 ELSE 1 END * EXP(SUM(LN(ABS(Net_TWRR_PERIOD)))))-1)*100,2)
from (select
year,
round(((CASE WHEN MOD(SUM(CASE WHEN (Net_TWRR ) <0 then 1 else 0 end ), 2 )=1 THEN -1 ELSE 1 END * EXP(SUM(LN(ABS(Net_TWRR)))))-1)*100,2) as Net_TWRR_PERIOD
from
(select ( net_rate_of_return / 100 + 1) as Net_TWRR,
year
from eom
WHERE id = '2'
and start_date < '09-September-2022'
) group by year order by year)
you are using the SUM functiion and the GROUP BY is missing in the outermost SQL.
create table eom(year number(4), start_date date, net_rate_of_return number (10,4), id number(4))
SELECT year,
Net_TWRR_PERIOD,
ROUND (
( ( CASE
WHEN MOD (
SUM (
CASE
WHEN (Net_TWRR_PERIOD) < 0 THEN 1
ELSE 0
END),
2) = 1
THEN
-1
ELSE
1
END
* EXP (SUM (LN (ABS (Net_TWRR_PERIOD)))))
- 1)
* 100,
2)
FROM ( SELECT year,
ROUND (
( ( CASE
WHEN MOD (
SUM (
CASE
WHEN (Net_TWRR) < 0 THEN 1
ELSE 0
END),
2) = 1
THEN
-1
ELSE
1
END
* EXP (SUM (LN (ABS (Net_TWRR)))))
- 1)
* 100,
2)
AS Net_TWRR_PERIOD
FROM (SELECT (net_rate_of_return / 100 + 1) AS Net_TWRR, year
FROM eom
WHERE id = '2' AND start_date < '09-September-2022')
GROUP BY year
ORDER BY year, Net_TWRR_PERIOD)
GROUP BY year, Net_TWRR_PERIOD
You have the outer query:
select year,
Net_TWRR_PERIOD,
round(
(
CASE
WHEN MOD(SUM(CASE WHEN Net_TWRR_PERIOD < 0 then 1 else 0 end ), 2)=1
THEN -1
ELSE 1
END
* EXP(SUM(LN(ABS(Net_TWRR_PERIOD))))
-1
) * 100,
2
)
from ( ... )
Which has a mix of aggregated columns and non-aggregated columns and you do not have a GROUP BY clause (in that outer query). You need to make sure all columns are either aggregated or contained in a GROUP BY.
So, change the outer query to:
select year,
Net_TWRR_PERIOD,
round(
(
CASE
WHEN MOD(SUM(CASE WHEN Net_TWRR_PERIOD < 0 then 1 else 0 end ), 2)=1
THEN -1
ELSE 1
END
* EXP(SUM(LN(ABS(Net_TWRR_PERIOD))))
-1
) * 100,
2
)
from ( ... )
GROUP BY year, Net_TWRR_PERIOD

pl sql payment calculation

I need help with PVA calculation in PL/SQL .I have formula:
Annuity = r * PVA Ordinary / [1 – (1 + r)-n]
Where:
PVA Ordinary = Present value of an ordinary annuity
r = Effective interest rate
n = Number of periods.
enter image description here
You can use:
DECLARE
principal NUMBER := 9000;
r NUMBER := 0.015;
n NUMBER := 5;
start_dt DATE := DATE '2022-07-14';
payment NUMBER := r * principal / (1 - POWER(1 + r, -n));
amt NUMBER := principal;
interest NUMBER;
pmt_dt DATE;
BEGIN
FOR i IN 1 .. n LOOP
pmt_dt := ADD_MONTHS(start_dt, i);
pmt_dt := pmt_dt + CASE pmt_dt - TRUNC(pmt_dt, 'IW')
WHEN 5 THEN 2 -- Saturday
WHEN 6 THEN 1 -- Sunday
ELSE 0 -- Weekday
END;
interest := amt * r;
amt := amt - payment + interest;
DBMS_OUTPUT.PUT_LINE(
TO_CHAR(i, 'fm0')
|| ', ' || TO_CHAR(pmt_dt, 'YYYY-MM-DD (DY)')
|| ', ' || TO_CHAR(payment, '9990.00')
|| ', ' || TO_CHAR(interest, '990.00')
|| ', ' || TO_CHAR(payment - interest, '9990.00')
|| ', ' || TO_CHAR(amt, '9990.00')
);
END LOOP;
END;
/
Which outputs:
1, 2022-08-15 (MON), 1881.80, 135.00, 1746.80, 7253.20
2, 2022-09-14 (WED), 1881.80, 108.80, 1773.01, 5480.19
3, 2022-10-14 (FRI), 1881.80, 82.20, 1799.60, 3680.59
4, 2022-11-14 (MON), 1881.80, 55.21, 1826.60, 1853.99
5, 2022-12-14 (WED), 1881.80, 27.81, 1853.99, -0.00
Or, in SQL using a MODEL clause:
WITH data (id, start_date, principal, rate, period ) AS (
SELECT 1, DATE '2022-07-14', 9000, 0.015, 5 FROM DUAL
)
SELECT pmt_dt + CASE pmt_dt - TRUNC(pmt_dt, 'IW')
WHEN 5 THEN 2
WHEN 6 THEN 1
ELSE 0
END AS pmt_dt,
ROUND(payment, 2) AS payment,
ROUND(interest, 2) AS interest,
ROUND(payment - interest, 2) AS reduction,
ROUND(balance, 2) AS balance
FROM data
MODEL
PARTITION BY (id)
DIMENSION BY (1 AS key)
MEASURES (
start_date,
principal,
rate,
period,
DATE '1900-01-01' AS pmt_dt,
rate * principal / (1 - POWER(1 + rate, -period)) AS payment,
0 AS interest,
0 AS balance
)
RULES SEQUENTIAL ORDER ITERATE (100) UNTIL (balance[ITERATION_NUMBER+1] <= 0) (
payment[ITERATION_NUMBER+1] = payment[1],
pmt_dt[ITERATION_NUMBER+1] = ADD_MONTHS(start_date[1], ITERATION_NUMBER+1),
interest[ITERATION_NUMBER + 1]
= COALESCE(balance[ITERATION_NUMBER],principal[1]) * rate[1],
balance[ITERATION_NUMBER+1]
= COALESCE(balance[ITERATION_NUMBER],principal[1])
- payment[1] + interest[ITERATION_NUMBER+1]
)
ORDER BY id, key
Which outputs:
PMT_DT
PAYMENT
INTEREST
REDUCTION
BALANCE
15-AUG-22
1881.8
135
1746.8
7253.2
14-SEP-22
1881.8
108.8
1773.01
5480.19
14-OCT-22
1881.8
82.2
1799.6
3680.59
14-NOV-22
1881.8
55.21
1826.6
1853.99
14-DEC-22
1881.8
27.81
1853.99
0
db<>fiddle here

Check Digit for Swiss Pay Slips in PLSQL

I have a problem with Luhn Algorithm for Pay Slip in Swiss. I found an algorithm in C, Phyton, or Java Script but I don't know how to implement this algorithm on Oracle.
http://dnando.github.io/blog/2014/09/23/check-digit-computation-swiss-pay-slips/
http://www.hosang.ch/modulo10.aspx
This page shows how to algorithm looks like.
public static int modulo10(string nummer)
{
// 'nummer' darf nur Ziffern zwischen 0 und 9 enthalten!
int[] tabelle = { 0, 9, 4, 6, 8, 2, 7, 1, 3, 5 };
int uebertrag = 0;
foreach (char ziffer in nummer)
uebertrag = tabelle[(uebertrag + ziffer - '0') % 10];
return (10 - uebertrag) % 10;
}
Do You know how to implement a table in PLSQL?
Thanks in advance for your help with this algorithm.
You can use:
CREATE FUNCTION luhn_modulo(
value IN VARCHAR2
) RETURN NUMBER DETERMINISTIC
IS
offsets CONSTANT CHAR(10) := '0946827135';
output PLS_INTEGER := 0;
BEGIN
FOR i IN 1 .. LENGTH( value ) LOOP
output := SUBSTR( offsets, MOD(output + SUBSTR( value, i, 1 ), 10) + 1, 1 );
END LOOP;
RETURN MOD(10 - output, 10);
END;
/
Then:
BEGIN
DBMS_OUTPUT.PUT_LINE( luhn_modulo( '1' ) );
DBMS_OUTPUT.PUT_LINE( luhn_modulo( '2' ) );
DBMS_OUTPUT.PUT_LINE( luhn_modulo( '3' ) );
DBMS_OUTPUT.PUT_LINE( luhn_modulo( '4' ) );
DBMS_OUTPUT.PUT_LINE( luhn_modulo( '1234' ) );
DBMS_OUTPUT.PUT_LINE( luhn_modulo( '000' ) );
END;
/
Outputs:
1
6
4
2
7
0
db<>fiddle here

split into rows and columns Oracle

I am creating a function that returns a table type object based on the split of the chain, the query is the following:
WITH COLUMNA AS (
SELECT ROWNUM COL_ID, REGEXP_SUBSTR ('A,B,C:D,E,F:','[^:]+',1,LEVEL) COL FROM DUAL
CONNECT BY REGEXP_SUBSTR ('A,B,C:D,E,F:','[^:]+',1,LEVEL) IS NOT NULL
ORDER BY COL_ID
)
SELECT * FROM (SELECT COL_ID, ROWNUM FIL_ID, SUBSTR(COL, INSTR(COL, ',', 1, LVL) + 1, INSTR(COL, ',', 1, LVL + 1) - INSTR(COL, ',', 1, LVL) - 1) NAME
FROM
( SELECT ',' || COL || ',' AS COL, COL_ID FROM COLUMNA ),
( SELECT LEVEL AS LVL FROM DUAL CONNECT BY LEVEL <= 100 )
WHERE LVL <= LENGTH(COL) - LENGTH(REPLACE(COL, ',')) - 1
ORDER BY COL_ID, NAME
) FILA
The result is as follows:
COL_ID FIL_ID NAME
1 1 A
1 2 B
1 3 C
2 4 D
2 5 E
2 6 F
And I Need To Get The Following Result
COL_ID VAL1 VAL2 VAL3 VALN
1 A B C X
2 D E F Y
I hope your valuable help!!!
You need to have a fixed number of columns in your object:
CREATE TYPE values_obj AS OBJECT(
COL_id INTEGER,
VAL1 VARCHAR2(10),
VAL2 VARCHAR2(10),
VAL3 VARCHAR2(10),
VAL4 VARCHAR2(10),
VAL5 VARCHAR2(10)
)
/
CREATE TYPE values_tab AS TABLE OF values_obj
/
CREATE OR REPLACE FUNCTION split_values(
in_list VARCHAR2
) RETURN values_tab
IS
vals VALUES_TAB;
BEGIN
SELECT values_obj(
LEVEL,
REGEXP_SUBSTR( in_list, '([^:,]*),?([^:,]*),?([^:,]*),?([^:,]*),?([^:,]*).*?(:|$)', 1, LEVEL, NULL, 1 ),
REGEXP_SUBSTR( in_list, '([^:,]*),?([^:,]*),?([^:,]*),?([^:,]*),?([^:,]*).*?(:|$)', 1, LEVEL, NULL, 2 ),
REGEXP_SUBSTR( in_list, '([^:,]*),?([^:,]*),?([^:,]*),?([^:,]*),?([^:,]*).*?(:|$)', 1, LEVEL, NULL, 3 ),
REGEXP_SUBSTR( in_list, '([^:,]*),?([^:,]*),?([^:,]*),?([^:,]*),?([^:,]*).*?(:|$)', 1, LEVEL, NULL, 4 ),
REGEXP_SUBSTR( in_list, '([^:,]*),?([^:,]*),?([^:,]*),?([^:,]*),?([^:,]*).*?(:|$)', 1, LEVEL, NULL, 5 )
)
BULK COLLECT INTO vals
FROM DUAL
CONNECT BY LEVEL < REGEXP_COUNT( in_list, '([^:,]*),?([^:,]*),?([^:,]*),?([^:,]*),?([^:,]*).*?(:|$)' );
RETURN vals;
END;
/
Then you can do:
SELECT *
FROM TABLE( split_values( 'A,B,C:D,E,F,G:H,I,J,K,L,M::N' ) );
Which outputs:
COL_ID VAL1 VAL2 VAL3 VAL4 VAL5
------ ---- ---- ---- ---- ----
1 A B C - -
2 D E F G -
3 H I J K L
4 - - - - -
5 N - - - -

Oracle 11g Sql convert date from blob field

I have a problem in converting a date value stored in a blob field in Oracle 11g sql command. When i execute the sql:
select dump(HIGH_VALUE) from all_tab_columns where COLUMN_NAME='TARIH'
i receive the following result;
Typ=23 Len=7: 120,116,3,6,1,1,1
I know that these numbers represent a date (not datetime), but i don't know how to extract the date from this result.
Thanks in advance,
Alper
Oracle stores dates in tables as 7-bytes
byte 1 - century + 100
byte 2 - (year MOD 100 ) + 100
byte 3 - month
byte 4 - day
byte 5 - hour + 1
byte 6 - minute + 1
byte 7 - seconds+ 1
So 120,116,3,6,1,1,1 converts to:
byte 1 - century = 120 - 100 = 20
byte 2 - year = 116 - 100 = 16
byte 3 - month = 3
byte 4 - day = 6
byte 5 - hour = 1 - 1 = 0
byte 6 - minute = 1 - 1 = 0
byte 7 - seconds = 1 - 1 = 0
So 2016-03-06T00:00:00
Oracle Setup:
CREATE TABLE file_upload ( file_blob BLOB );
INSERT INTO file_upload VALUES (
utl_raw.cast_to_raw(
CHR(120) || CHR(116) || CHR(3) || CHR(6) || CHR(1) || CHR(1) || CHR(1)
)
);
Query:
SELECT DUMP( DBMS_LOB.SUBSTR( file_blob, 7, 1 ) ) AS dmp,
TO_DATE(
TO_CHAR(
( ASCII( SUBSTR( chars, 1, 1 ) ) - 100 ) * 100
+ ASCII( SUBSTR( chars, 2, 1 ) ) - 100,
'0000'
)
|| TO_CHAR( ASCII( SUBSTR( chars, 3, 1 ) ), '00' )
|| TO_CHAR( ASCII( SUBSTR( chars, 4, 1 ) ), '00' )
|| TO_CHAR( ASCII( SUBSTR( chars, 5, 1 ) ) - 1, '00' )
|| TO_CHAR( ASCII( SUBSTR( chars, 6, 1 ) ) - 1, '00' )
|| TO_CHAR( ASCII( SUBSTR( chars, 7, 1 ) ) - 1, '00' ),
'YYYYMMDDHH24MISS'
) AS converted_date
FROM (
SELECT file_blob,
UTL_RAW.CAST_TO_VARCHAR2(DBMS_LOB.SUBSTR( file_blob, 7, 1 ) ) AS chars
FROM file_upload
);
Output:
DMP CONVERTED_DATE
------------------------------- -------------------
Typ=23 Len=7: 120,116,3,6,1,1,1 2016-03-06 00:00:00

Resources