I have a strange situation in the desing of my DB. I have the case that the type of value of a field can be a normal integer or a number between a range. I explain myself with a example:
the column age can be a number (18) or a range between (18-30). How I can represent this with postgresql?
Thx!
An integer range can represent both a single integer value and a range. The single value:
select int4range(18,18,'[]');
int4range
-----------
[18,19)
The ")" in the result above means exclusive.
The range:
select int4range(18,30,'[]');
int4range
-----------
[18,31)
There are a couple different ways to do this.
Store a VARCHAR
Store two values lower bound and upper bound
If there are only a select set of ranges you can create a lookup table for that set and store a foreign key to that lookup table.
You can make a bigger number, for example 18 x 1000 + 0 = 18000 for 18 and 18 x 1000 + 30 = 18030 for (18, 30).
When you retrieve it, you do first = round(number/1000) for the first number and second = number - first for the second number.
You can also store them as a point http://www.postgresql.org/docs/9.4/static/datatype-geometric.html#AEN6730.
Related
New to postgres and unsure how to accomplish the following. I have a table as follows:
create table if not exists my_table (
id int GENERATED BY DEFAULT AS IDENTITY primary key,
key int default 0
)
What I am trying to do is take an integer value (my_key) and if it's >= 0 and < 10 then add a leading zero (0) to it and insert it into my_table.key
I have tried to_char(my_key::integer,'09')::integer where my_key = 0 and it doesn't insert 00 within the key column.
Any help would be great.
Leading zeros don't change the value of an integer, so this is a question about formatting numbers.
If you want to display the id column with leading zeros, you could do that like this:
SELECT to_char(id, '00'), key
FROM "table";
The format 00 formats the number as a two-digit string with leasing zeros. If id is greater than 99, the number cannot be formatted like this, and you will get ##.
See the documentation for details about to_char and the available formats.
Is it possible to define a column that auto increments which is a 12 digit number on a schema level?
So the sequence would go 000000000000, 000000000001, ...
You can create a sequence specifying min,max,start values. Then assign that sequence as a default. You commented that your need is "EAN-13, but the first digit is a constant", from this I assume you actually need a 13 digit number beginning with a fixed digit. You can use that fixed digit as the leading value of the sequence. Something like ( assumes that constant first digit is 5):
create sequence barcode_seq
increment 1
minvalue 5000000000000
maxvalue 5999999999999
start 5000000000000;
While sequences tend to be used as table keys that is not a requirement. Use the above sequence as the default value wherever the barcode is assigned. See fiddle.
Are you looking for the serial datatype? That's an auto-incrementing integer. It ranges from 1 to 2147483647, which is a bit less than 12 digits. If you need something bigger, you can switch to bigserial, that goes up to 9223372036854775807.
create table mytable (
id serial,
val text
);
insert into mytable (val) values ('foo'), ('bar');
select * from mytable;
id | val
-: | :--
1 | foo
2 | bar
I hava data in my database and i need to select all data where 1 column number is between 1-100.
Im having problems, because i cant use - between 1 and 100; Because that column is character varying, not integer. But all data are numbers (i cant change it to integer).
Code;
dst_db1.eachRow("Select length_to_fault from diags where length_to_fault between 1 AND 100")
Error - operator does not exist: character varying >= integer
Since your column supposed to contain numeric values but is defined as text (or version of text) there will be times when it does not i.e. You need 2 validations: that the column actually contains numeric data and that it falls into your value restriction. So add the following predicates to your query.
and length_to_fault ~ '^\+?\d+(\.\d*)?$'
and length_to_fault::numeric <# ('[1.0,100.0]')::numrange;
The first builds a regexp that insures the column is a valid floating point value. The second insures the numeric value fall within the specified numeric range. See fiddle.
I understand you cannot change the database, but this looks like a good place for a check constraint esp. if n/a is the only non-numeric are allowed. You may want to talk with your DBA ans see about the following constraint.
alter table diags
add constraint length_to_fault_check
check ( lower(length_to_fault) = 'n/a'
or ( length_to_fault ~ '^\+?\d+(\.\d*)?$'
and length_to_fault::numeric <# ('[1.0,100.0]')::numrange
)
);
Then your query need only check that:
lower(lenth_to_fault) != 'n/a'
The below PostgreSQL query will work
SELECT length_to_fault FROM diags WHERE regexp_replace(length_to_fault, '[\s+]', '', 'g')::numeric BETWEEN 1 AND 100;
I want to list all domains, their datatypes, and size.
Background
I've managed to do the query, based on this SO answer.
The basic code takes all fields:
SELECT
*
FROM
rdb$fields
I found that I could get fields from rdb$fields:
filter fields from this request by RDB$FIELD_NAME
get field type code from RDB$FIELD_TYPE
get field length from RDB$FIELD_LENGTH
Reference:
https://firebirdsql.org/file/documentation/reference_manuals/fblangref25-en/html/fblangref-appx04-fields.html
Question
How to combine all this to list all domains, their datatypes, and size?
I want to get only domains created by users, not automatic ones.
The code:
select
t.RDB$FIELD_NAME Name,
case t.RDB$FIELD_TYPE
when 7 then 'SMALLINT'
when 8 then 'INTEGER'
when 10 then 'FLOAT'
when 12 then 'DATE'
when 13 then 'TIME'
when 14 then 'CHAR'
when 16 then 'BIGINT'
when 27 then 'DOUBLE PRECISION'
when 35 then 'TIMESTAMP'
when 37 then 'VARCHAR'
when 261 then 'BLOB'
end Type_Name,
t.RDB$CHARACTER_LENGTH Chr_Length
from RDB$FIELDS t
where coalesce( rdb$system_flag, 0) = 0
and not ( rdb$field_name starting with 'RDB$')
Also interesting, I could not find a system table with datatypes. Had to hardcode them from the reference.
Thanks for the help in comments:
#MarkRotteveel
RDB$TYPE contains types, but names them differently:
You can find all data types in the RDB$TYPE for RDB$FIELD_NAME =
'RDB$FIELD_TYPE' (although you will need to map some types as it lists
SMALLINT as SHORT, INTEGER as LONG, BIGINT as INT64 and VARCHAR as
VARYING)
Need to use field RDB$CHARACTER_LENGTH instead of RDB$FIELD_LENGTH.
Note that RDB$FIELD_LENGTH is the wrong column for char/varchar
columns as it is the length in bytes (which depends on the character
set), you need to use RDB$CHARACTER_LENGTH for the length in
characters, and for numerical fields, you'll more likely need
RDB$FIELD_PRECISION (+ RDB$FIELD_SCALE), you are also ignoring sub
type information.
I needed the length of varchars only but appears RDB$FIELD_LENGTH = RDB$CHARACTER_LENGTH, 1 byte = 1 char for 1 byte character set.
If you use a 1 byte character set [1 byte = 1 char], but for example, UTF-8 is
(max) 4 byte per character, so then the field_length = 4 x
character_length
#Arioch
The most reliable way to get user domains:
To an extent one may use select * from rdb$fields where coalesce(
rdb$system_flag, 0) = 0 and not ( rdb$field_name starting with 'RDB$')
however no one prohibits user from manually/explicitly creating column
named "RDB$1234567".
I have one column. In that column all rows are having 10 digits i.e. 1234567890. By using PostgreSQL update query, I need to update last 5 digits to 99999. i.e. 1234599999
Can any one provide me update query for above requirement.
Integer divide your number by 100,000 (i.e. discard the remainder), multiply it by 100,000 then add 99,999:
UPDATE table SET field = FLOOR(field / 100000) * 100000 + 99999;
UPDATE table name SET column name = column name:: int / 10000 * 10000 + 9999 WHERE column name!=''''
Here column name having varchar data type I converted to int as per my requirement.