This function is a workaround ... nothing with better performance?
CREATE or REPLACE FUNCTION varbit_to_int(v varbit) RETURNS int AS $f$
SELECT CASE bit_length(v)
WHEN 1 THEN v::bit(1)::int
WHEN 2 THEN v::bit(2)::int
WHEN 3 THEN v::bit(3)::int
...
WHEN 30 THEN v::bit(30)::int
WHEN 31 THEN v::bit(31)::int
WHEN 32 THEN v::bit(32)::int
ELSE NULL::int
END
$f$ LANGUAGE SQL IMMUTABLE;
Same problem for bigint:
CREATE or replace FUNCTION varbit_to_bigint(p varbit) RETURNS bigint AS $f$
SELECT CASE bit_length($1)
WHEN 1 THEN $1::bit(1)::bigint
WHEN 2 THEN $1::bit(2)::bigint
WHEN 3 THEN $1::bit(3)::bigint
...
WHEN 62 THEN $1::bit(62)::bigint
WHEN 63 THEN $1::bit(63)::bigint
WHEN 64 THEN $1::bit(64)::bigint
ELSE NULL::bigint
END
$f$ LANGUAGE SQL IMMUTABLE STRICT;
Using many times in loops seems CPU-wasteful, only to avoid "cannot cast type bit varying to integer" error. Maybe an external C-language library do this and other useful castings.
NOTICE select b'101'::bit(64)::bigint != b'101'::bigint;
I tested a couple of variants (for bigint only) with built-in functionality and this variant with OVERLAY() turned out fastest in my local tests on Postgres 11:
CREATE OR REPLACE FUNCTION varbit2bigint2(b varbit)
RETURNS bigint AS
$func$
SELECT OVERLAY(bit(64) '0' PLACING b FROM 65 - bit_length(b))::bigint
$func$ LANGUAGE SQL IMMUTABLE;
Other candidates:
Note the different conversion of empty bitstrings ('') to 0 vs. NULL. Adapt to your needs!
Your function:
CREATE OR REPLACE FUNCTION varbit2bigint1(b varbit)
RETURNS bigint AS
$func$
SELECT CASE bit_length($1)
WHEN 1 THEN $1::bit(1)::bigint
WHEN 2 THEN $1::bit(2)::bigint
WHEN 3 THEN $1::bit(3)::bigint
WHEN 4 THEN $1::bit(4)::bigint
WHEN 5 THEN $1::bit(5)::bigint
WHEN 6 THEN $1::bit(6)::bigint
WHEN 7 THEN $1::bit(7)::bigint
WHEN 8 THEN $1::bit(8)::bigint
WHEN 9 THEN $1::bit(9)::bigint
WHEN 10 THEN $1::bit(10)::bigint
WHEN 11 THEN $1::bit(11)::bigint
WHEN 12 THEN $1::bit(12)::bigint
WHEN 13 THEN $1::bit(13)::bigint
WHEN 14 THEN $1::bit(14)::bigint
WHEN 15 THEN $1::bit(15)::bigint
WHEN 16 THEN $1::bit(16)::bigint
WHEN 17 THEN $1::bit(17)::bigint
WHEN 18 THEN $1::bit(18)::bigint
WHEN 19 THEN $1::bit(19)::bigint
WHEN 20 THEN $1::bit(20)::bigint
WHEN 21 THEN $1::bit(21)::bigint
WHEN 22 THEN $1::bit(22)::bigint
WHEN 23 THEN $1::bit(23)::bigint
WHEN 24 THEN $1::bit(24)::bigint
WHEN 25 THEN $1::bit(25)::bigint
WHEN 26 THEN $1::bit(26)::bigint
WHEN 27 THEN $1::bit(27)::bigint
WHEN 28 THEN $1::bit(28)::bigint
WHEN 29 THEN $1::bit(29)::bigint
WHEN 30 THEN $1::bit(30)::bigint
WHEN 31 THEN $1::bit(31)::bigint
WHEN 32 THEN $1::bit(32)::bigint
WHEN 33 THEN $1::bit(33)::bigint
WHEN 34 THEN $1::bit(34)::bigint
WHEN 35 THEN $1::bit(35)::bigint
WHEN 36 THEN $1::bit(36)::bigint
WHEN 37 THEN $1::bit(37)::bigint
WHEN 38 THEN $1::bit(38)::bigint
WHEN 39 THEN $1::bit(39)::bigint
WHEN 40 THEN $1::bit(40)::bigint
WHEN 41 THEN $1::bit(41)::bigint
WHEN 42 THEN $1::bit(42)::bigint
WHEN 43 THEN $1::bit(43)::bigint
WHEN 44 THEN $1::bit(44)::bigint
WHEN 45 THEN $1::bit(45)::bigint
WHEN 46 THEN $1::bit(46)::bigint
WHEN 47 THEN $1::bit(47)::bigint
WHEN 48 THEN $1::bit(48)::bigint
WHEN 49 THEN $1::bit(49)::bigint
WHEN 50 THEN $1::bit(50)::bigint
WHEN 51 THEN $1::bit(51)::bigint
WHEN 52 THEN $1::bit(52)::bigint
WHEN 53 THEN $1::bit(53)::bigint
WHEN 54 THEN $1::bit(54)::bigint
WHEN 55 THEN $1::bit(55)::bigint
WHEN 56 THEN $1::bit(56)::bigint
WHEN 57 THEN $1::bit(57)::bigint
WHEN 58 THEN $1::bit(58)::bigint
WHEN 59 THEN $1::bit(59)::bigint
WHEN 60 THEN $1::bit(60)::bigint
WHEN 61 THEN $1::bit(61)::bigint
WHEN 62 THEN $1::bit(62)::bigint
WHEN 63 THEN $1::bit(63)::bigint
WHEN 64 THEN $1::bit(64)::bigint
ELSE NULL::bigint
END
$func$ LANGUAGE SQL IMMUTABLE; -- no STRICT modifier
Left-padding the text representation with '0':
CREATE OR REPLACE FUNCTION pg_temp.varbit2bigint3(b varbit)
RETURNS bigint AS
$func$
SELECT lpad(b::text, 64, '0')::bit(64)::bigint
$func$ LANGUAGE SQL IMMUTABLE;
Bit-shifting before the cast:
CREATE OR REPLACE FUNCTION varbit2bigint4(b varbit)
RETURNS bigint AS
$func$
SELECT (bit(64) '0' || b << bit_length(b))::bit(64)::bigint
$func$ LANGUAGE SQL IMMUTABLE;
db<>fiddle here
Related:
Postgresql Convert bit varying to integer
Your feedback
It is not worst, it is faster!
EXPLAIN ANALYZE select
varbit_to_bigint(osm_id::bit(64)::varbit)
from planet_osm_point limit 10000 ;
-- Planning time: 0.697 ms
-- Execution time: 1133.571 ms
EXPLAIN ANALYZE select
lpad(osm_id::bit(64)::varbit::text, 32, '0')::bit(64)::bigint
from planet_osm_point limit 10000;
-- Planning time: 0.105 ms
-- Execution time: 26.429 ms
You show a STRICT modifier with the bigint variant of the function in the question (not sure why it differs from the integer variant). If that represents the function you actually tested, I expect most of the observed performance difference is due to that added STRICT modifier preventing function inlining. Quoting the Postgres Wiki:
if the function is declared STRICT, then the planner must be able to
prove that the body expression necessarily returns NULL if any
parameter is null. At present, this condition is only satisfied if:
every parameter is referenced at least once, and all functions,
operators and other constructs used in the body are themselves STRICT.
That seems to hurt your function badly - while my winner seems unaffected, and the other two variants are even ~ 10 % faster. Same fiddle with STRICT functions:
db<>fiddle here
Related:
Function executes faster without STRICT modifier?
I suggest you re-test with and without STRICT modifier to see for yourself.
Related
I am new to PL/SQL and I can't figure out what is the problem in the following function, as I get the error ORA-00904: par_cantitate invalid identifier. Please, please could you help me. Thank you!
CREATE OR REPLACE FUNCTION vanzari_med(par_cantitate
ProduseVandute.Cantitate%TYPE)
RETURN NUMBER IS
c ProduseVandute.Cantitate%TYPE;
s Medicament.Stoc%TYPE;
NO_SALES EXCEPTION;
BEGIN
SELECT Stoc INTO s
FROM ProduseVandute pv, Medicament m
WHERE pv.Cantitate=m.Stoc and Cantitate=par_cantitate;
IF s<>15
THEN RAISE NO_SALES;
ELSE
SELECT Cantitate INTO c
FROM ProduseVandute pv, Medicament m,(SELECT * FROM Vanzari GROUP
BY ID_Vanzari)v
WHERE Cantitate=par_cantitate and pv.Cantitate=m.Stoc and
pv.ID_Vanzari=v.ID_Vanzari;
END IF;
RETURN c;
END vanzari_med
Are you sure? If tables you used contain at least columns from this function, then it compiles. Though, as you didn't handle exception you raised, it might not work properly, but - it compiles.
Sample tables:
SQL> CREATE TABLE ProduseVandute
2 (
3 cantitate NUMBER,
4 id_vanzari NUMBER
5 );
Table created.
SQL> CREATE TABLE medicament
2 (
3 stoc NUMBER
4 );
Table created.
SQL> CREATE TABLE vanzari
2 (
3 id_vanzari NUMBER
4 );
Table created.
Your function, unmodified (just added terminators at the end):
SQL> CREATE OR REPLACE FUNCTION vanzari_med(par_cantitate
2 ProduseVandute.Cantitate%TYPE)
3 RETURN NUMBER IS
4 c ProduseVandute.Cantitate%TYPE;
5 s Medicament.Stoc%TYPE;
6 NO_SALES EXCEPTION;
7 BEGIN
8 SELECT Stoc INTO s
9 FROM ProduseVandute pv, Medicament m
10 WHERE pv.Cantitate=m.Stoc and Cantitate=par_cantitate;
11 IF s<>15
12 THEN RAISE NO_SALES;
13 ELSE
14 SELECT Cantitate INTO c
15 FROM ProduseVandute pv, Medicament m,(SELECT * FROM Vanzari GROUP
16 BY ID_Vanzari)v
17 WHERE Cantitate=par_cantitate and pv.Cantitate=m.Stoc and
18 pv.ID_Vanzari=v.ID_Vanzari;
19 END IF;
20 RETURN c;
21 END vanzari_med;
22 /
Function created.
SQL>
In Postgresql, I need to take an ASCII string like CNR39XL and turn it into a number like 21317392311 (ASCII values - 65, but digits left as is). This is to take some logic in our software and push it down into the databse level. I’m partway there with this:
ourdb=> SELECT CASE
ourdb-> WHEN ASCII(c) < 65 THEN c::integer
ourdb-> ELSE ASCII(c)-65
ourdb-> END
ourdb-> FROM regexp_split_to_table('CNR39XL','') s(c);
case
------
2
13
17
3
9
23
11
(7 rows)
However, I can't figure out how to take that final set of numbers and convert it into the string. What am I looking for?
Use string_agg
SELECT CAST(
string_agg(
CAST (CASE ... END AS text),
''
)
AS numeric)
FROM ...
How do I get the ASCII value of a string as an int in PostgreSQL?
For example: the string S06.6X9A
Currently, I am using an ASCII function, but it returns only the first character of a given string.
Use string_to_array('S06.6X9A', null) to split the string into a text[] of individual characters. Then unnest to turn that text[] into a table. Then use that in a from clause and run ascii() over each row.
select ascii(char)
from (
select unnest( string_to_array('S06.6X9A', null) )
) as chars(char);
ascii
-------
83
48
54
46
54
88
57
65
Simpler than Schwern's answer is:
SELECT ascii( unnest( string_to_array('S06.6X9A', NULL) ) )
CREATE or replace FUNCTION billtesting() RETURNS trigger AS
$$
BEGIN
if (destination_number'^(?:[0-7] ?){6,14}[0-9]$' ~ digits and destination_number !~ '15487498')
then
insert into isp_cdr(destination_number,caller_id,duration,billsec)
values (destination_number,caller_id,duration,billsec);
UPDATE isp_cdr
SET nibble_total_billed = billsec * user_rate;
end if;
RETURN NEW;
END
$$
LANGUAGE plpgsql;
CREATE TRIGGER bill_testing_update
BEFORE UPDATE
ON isp_cdr
FOR EACH ROW
EXECUTE PROCEDURE billtesting();
This is the query for testing
insert into isp_cdr(destination_number,caller_id,duration,billsec)
values ('012687512123','123125641','43','35');
This is the sample lcr table information
digits user_rate
1 0.02
23 0.07
652 0.12
1123 0.28
87521 0.15
123161 0.54
9641231 1.20
65491641 0.89
this is the sample isp_cdr table information
destination_number caller_id duration billsec nibble_total_billed
123561231 1315142 67 58 0
with this trigger procedure i want to match the digits with destination_number with and use the user_rate to do the calculation
user_rate * billsec = nibble_total_billed
but after the insert the testing query the calculation does working it din't show any result at nibble_total_billed what i think is my pattern matching part has some mistake i need to match the digits with the destination_number.
friends i had written a code to upload a data in newemp table using UTL code i given below but i get the error
1 declare
2 EMPNO NUMBER(4);
3 ENAME VARCHAR2(10);
4 JOB VARCHAR2(10);
5 MGR NUMBER(4);
6 HIREDATE DATE;
7 SAL NUMBER(7,2);
8 COMM NUMBER(7,2);
9 DEPTNO NUMBER(2);
10 line varchar2(100);
11 namesfile UTL_FILE.FILE_TYPE;
12 begin
13 namesfile :=UTL_FILE.FOPEN('DIPRJDIR','empdata.txt','R');
14 loop
15 UTL_FILE.GET_LINE(namesfile,EMPNO,4);
16 dbms_output.put_line('EMPNO :' || EMPNO);
17 UTL_FILE.GET_LINE(namesfile,ENAME,10);
18 dbms_output.put_line('ENAME :' || ENAME);
19 UTL_FILE.GET_LINE(namesfile,JOB,9);
20 dbms_output.put_line('JOB :' || JOB);
21 UTL_FILE.GET_LINE(namesfile,MGR,4);
22 dbms_output.put_line('MGR :' || MGR);
23 UTL_FILE.GET_LINE(namesfile,HIREDATE,5);
24 dbms_output.put_line('HIREDATE :' || HIREDATE);
25 UTL_FILE.GET_LINE(namesfile,SAL,9);
26 dbms_output.put_line('SAL :' || SAL);
27 UTL_FILE.GET_LINE(namesfile,COMM,9);
28 dbms_output.put_line('COMM :' || COMM);
29 UTL_FILE.GET_LINE(namesfile,DEPTNO,2);
30 dbms_output.put_line('DEPTNO :' || DEPTNO);
31 insert into newemp values(EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,COMM,DEPTNO);
32 end loop;
33 utl_file.fclose(namesfile);
34* end;
SQL> /
EMPNO :7839
ENAME :KING
JOB :PRESIDENT
MGR :0
ERROR
declare
*
ERROR at line 1:
ORA-01843: not a valid month
ORA-06512: at line 23
and my data is in below given format
7839KING PRESIDENT 000017-nov-1981 005000.00 000000.0010 so Please help me
That's because the UTL_FILE.GET_LINE procedure, gets a VARCHAR2 as a second parameter.
If you put something else than a varchar2 oracle tries to implicitly cast it to the right type.
This worked quite well for you with numbers, but with a Date datatype, oracle will use the NLS_DATE_FORMAT which might not meet the format of string in the file.
You can fix it by using a VARCHAR2 in the UTL_FILE.GET_LINE call and then use to_date function to convert it to a date datatype.
But, there is a better way to do this job-
You can use an external table to read the file and then insert-select from it.