I am trying to convert bit varyingto numeric (decimal value) in postgresql
select cast('10110101110110010100110001101010100111011011010000000101'::bit varying as numeric);
I have tried the cast function but getting below error.
ERROR: cannot cast type bit varying to numeric LINE 1: select
cast('10110101110110010100110001101010100111011011010...
^
********** Error **********
ERROR: cannot cast type bit varying to numeric SQL state: 42846
Character: 8
Output should be : 5429503678976. Any suggestions
EDIT: I do not know the max value so can not use
select b'10110101110110010100110001101010100111011011010000000101'::bigint
Thanks
You could break it up into individual bits, convert those to the appropriate numeric values and then sum them together:
with data(d) as (VALUES (B'10110101110110010100110001101010100111011011010000000101') )
select sum(parts) FROM (
SELECT get_bit(d, length(d) - n)::numeric * pow(2::numeric, n-1) as parts
from data, generate_series(1, length(d), 1) g(n)
) sub
;
sum
------------------------------------
51185893014090757.0000000000000000
(1 row)
Note that this gives me the same answer as select b'10110101110110010100110001101010100111011011010000000101'::bigint; but it should work for larger values as well, where bigint would be out of range.
You could turn it into a function:
CREATE OR REPLACE FUNCTION bitstring_to_numeric(l_bit_string varbit)
RETURNS numeric AS $$
SELECT sum(parts) FROM (
SELECT get_bit(l_bit_string,
length(l_bit_string) - n)::numeric * pow(2::numeric, n-1) as parts
from generate_series(1, length(l_bit_string), 1) g(n)
) sub
$$ LANGUAGE SQL
IMMUTABLE;
select bitstring_to_numeric(B'10110101110110010100110001101010100111011011010000000101');
bitstring_to_numeric
------------------------------------
51185893014090757.0000000000000000
Related
I want to use trunc() on a BigQuery "numeric" string column to truncate it from 3 decimals to 2 decimals.
This works when the string is specified inside trunc:
select
"yey" as x,
trunc(numeric '50.239', 2) as trunced
x trunced
1 yey 50.23
But if I want to run this on a table it doesn't work:
with t as(
select
"yey" as x,
'50.239' as y
)
select x, trunc(numeric y, 2) as y_trunc from t
And produces the error Syntax error: Expected ")" but got identifier "y" at [7:25]
Does anybody know what's going on?
numeric '50.239' is a numeric literal representation, not type casting. You should use cast(y as numeric) if you want to convert a string column to a numeric column.
with t as(
select
"yey" as x,
'50.239' as y
)
select x, trunc(cast(y as numeric), 2) as y_trunc from t
references
NUMERIC literals
You can construct NUMERIC literals using the NUMERIC keyword followed by a floating point value in quotes.
Examples:
SELECT NUMERIC '0';
SELECT NUMERIC '123456';
SELECT NUMERIC '-3.14';
I am trying to create a SQL UDF that takes a decimal number as an argument but if I pass in a number with a different number of decimal places I get an error, even if the number has a smaller number of digits to the right of the decimal point.
Here is a example of the problem:
CREATE OR REPLACE TABLE EXAMPLE (
COL1 NUMBER(7,5)
);
INSERT INTO EXAMPLE VALUES (9.12),(10.467),(11.6),(12.01234);
CREATE OR REPLACE FUNCTION LESS_THAN(DATA1 NUMBER(7,5))
RETURNS TABLE (COL1 NUMBER(7,5))
as
$$
SELECT COL1 FROM EXAMPLE WHERE COL1 < DATA1
$$
;
select * from table(LESS_THAN(11.3)); --Error: SQL compilation error: error line 1 at position 20 Invalid argument types for function 'LESS_THAN': (NUMBER(3,1))
select * from table(LESS_THAN(9.85)); --Error: SQL compilation error: error line 1 at position 20 Invalid argument types for function 'LESS_THAN': (NUMBER(3,2))
select * from table(LESS_THAN(10.85001)); --Works
I would like to be able to create a UDF that allows any decimal to be accepted.
Any thoughts?
Thanks
Try casting the inputs to NUMBER(7,5). For example:
select * from table(LESS_THAN('11.3'::NUMBER(7,5)));
select * from table(LESS_THAN( 11.3 ::NUMBER(7,5)));
You can use this system function to observe the types:
select 11.3 x, system$typeof(x); -- NUMBER(3,1)
select 11.30 x, system$typeof(x); -- NUMBER(3,1)
select 11.30 ::NUMBER(7,5) x, system$typeof(x); -- NUMBER(7,5)
select '11.30'::NUMBER(7,5) x, system$typeof(x); -- NUMBER(7,5)
I'm trying to get the max value from a text field. All but two of the values are numbers with a single decimal. However, two of the values have something like 8.2.10. How can I pull back just the integer value? The values can go higher than 9.n, so I need to convert this field into a number so that I can get the largest value returned. So all I want to get back is the 8 from the 8.2.1.
Select cast(VERSION as int) is bombing out because of those two values with a second . in them.
You may derive by using regexp_substr with \d pattern :
with tab as
(
select regexp_substr('8.2.1', '\d', 1, 1) from dual
union all
select regexp_substr('9.0.1', '\d', 1, 1) from dual
)
select * from tab;
For Oracle you must attend the value as string for retire only the part before the dot. Ex:
SELECT NVL( SUBSTR('8.2.1',0, INSTR('8.2.1','.')-1),'8.2.1') AS SR FROM DUAL;
Check than the value is repeated 3 times in the sentence, and if the value is zero or the value didn't have decimal part then it will return the value as was set.
I had to use T-SQL rather PL/SQL, but the idea is the same:
DECLARE #s VARCHAR(10);
SELECT #s='8.2.1';
SELECT CAST(LEFT(#s, CHARINDEX('.', #s) - 1) AS INT);
returns the integer 8 - note that it won't work if there are no dots because it takes the part of the string to the left of the first dot.
If my quick look at equivalent functions was correct, then in Oracle that would end up as:
SELECT CAST(SUBSTR(VERSION, 1, INSTR(VERSION, '.') - 1) AS INT)
I have a table software and columns in it as dev_cost, sell_cost. If dev_cost is 16000 and sell_cost is 7500, how do I find the quantity of software to be sold in order to recover the dev_cost?
I have queried as below:
select dev_cost / sell_cost from software ;
It is returning 2 as the answer. But we need to get 3, right?
What would be the query for that?
Your columns have integer types, and integer division truncates the result towards zero. To get an accurate result, you'll need to cast at least one of the values to float or decimal:
select cast(dev_cost as decimal) / sell_cost from software ;
or just:
select dev_cost::decimal / sell_cost from software ;
You can then round the result up to the nearest integer using the ceil() function:
select ceil(dev_cost::decimal / sell_cost) from software ;
(See demo on SQLFiddle.)
You can cast integer type to numeric and use ceil() function to get the desired output
The PostgreSQL ceil function returns the smallest integer value that
is greater than or equal to a number.
SELECT 16000::NUMERIC / 7500 col
,ceil(16000::NUMERIC / 7500)
Result:
col ceil
------------------ ----
2.1333333333333333 3
So your query should be
select ceil(dev_cost::numeric/sell_cost)
from software
You can also cast your variable to the desired type, then apply division:
SELECT (dev_cost::numeric/sell_cost::numeric);
You can round your value , and specify the number of digits after point:
SELECT TRUNC((dev_cost::numeric/sell_cost::numeric),2);
This query will round result to next integer
select round(dev_cost ::decimal / sell_cost + 0.5)
=# select row(0, 1) ;
row
-------
(0,1)
(1 row)
How to get 0 within the same query? I figured the below sort of working but is there any simple way?
=# select json_agg(row(0, 1))->0->'f1' ;
?column?
----------
0
(1 row)
No luck with array-like syntax [0].
Thanks!
Your row type is anonymous and therefore you cannot access its elements easily. What you can do is create a TYPE and then cast your anonymous row to that type and access the elements defined in the type:
CREATE TYPE my_row AS (
x integer,
y integer
);
SELECT (row(0,1)::my_row).x;
Like Craig Ringer commented in your question, you should avoid producing anonymous rows to begin with, if you can help it, and type whatever data you use in your data model and queries.
If you just want the first element from any row, convert the row to JSON and select f1...
SELECT row_to_json(row(0,1))->'f1'
Or, if you are always going to have two integers or a strict structure, you can create a temporary table (or type) and a function that selects the first column.
CREATE TABLE tmptable(f1 int, f2 int);
CREATE FUNCTION gettmpf1(tmptable) RETURNS int AS 'SELECT $1.f1' LANGUAGE SQL;
SELECT gettmpf1(ROW(0,1));
Resources:
https://www.postgresql.org/docs/9.2/static/functions-json.html
https://www.postgresql.org/docs/9.2/static/sql-expressions.html
The json solution is very elegant. Just for fun, this is a solution using regexp (much uglier):
WITH r AS (SELECT row('quotes, "commas",
and a line break".',null,null,'"fourth,field"')::text AS r)
--WITH r AS (SELECT row('',null,null,'')::text AS r)
--WITH r AS (SELECT row(0,1)::text AS r)
SELECT CASE WHEN r.r ~ '^\("",' THEN ''
WHEN r.r ~ '^\("' THEN regexp_replace(regexp_replace(regexp_replace(right(r.r, -2), '""', '\"', 'g'), '([^\\])",.*', '\1'), '\\"', '"', 'g')
ELSE (regexp_matches(right(r.r, -1), '^[^,]*'))[1] END
FROM r
When converting a row to text, PostgreSQL uses quoted CSV formatting. I couldn't find any tools for importing quoted CSV into an array, so the above is a crude text manipulation via mostly regular expressions. Maybe someone will find this useful!
With Postgresql 13+, you can just reference individual elements in the row with .fN notation. For your example:
select (row(0, 1)).f1; --> returns 0.
See https://www.postgresql.org/docs/13/sql-expressions.html#SQL-SYNTAX-ROW-CONSTRUCTORS