PostgreSQL: Constant for smallint maximum value? - postgresql

Does PostgreSQL have a constant (like MAXFLOAT in Objective-C) for the maximum value that a smallint can be?
I know the PostgreSQL numeric types documentation says it's 32767, but I'd rather use a constant than hard coding a value that could change.
I'm using this number to prevent an error when incrementing a smallint, e.g.:
UPDATE populations
SET count = count + 1
WHERE city_id = 3
AND count < 32767;

Create it:
create function MAX_SMALLINT() returns smallint immutable language sql as '
select 32767::smallint;
';
Use it:
UPDATE populations
SET count = count + 1
WHERE city_id = 3
AND count < MAX_SMALLINT();

In extension of Neil's suggestion, you could use this:
create function MAX_SMALLINT() returns smallint immutable language sql as '
select ((1 << ((pg_column_size(1::smallint) << 3) - 1)) - 1)::smallint;
';
But honestly, I don't believe smallint will ever be anything else than 2 bytes in Postgres.

Related

Check if character varying is between range of numbers

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;

How can I query whether a value lies before or after a range?

I have a Postgres range and a value, and want to be able to determine if the value lies before, within, or after the range.
Determining if the value lies within the range is trivial:
SELECT '[1,10]'::int4range #> 3; -- t
But looking at range functions and operators, the #> operator is the only one I see that doesn't require both operands to be ranges, so determining whether the value lies before or after the range is not as straightforward.
I'm currently constructing a trivial range to represent my value (inclusive endpoints, both equal to the value), and then using << (strictly left of) and >> (strictly right of):
SELECT '[1,10]'::int4range << '[11,11]'::int4range; -- t
SELECT '[1,10]'::int4range >> '[-3,-3]'::int4range; -- t
This works, but having to construct this trivial range representing a single discrete value just so I can use the << and >> operators feels a bit kludge-y to me. Is there some built-in function or operator I'm overlooking that would allow me to do these queries using the value directly?
I considered and rejected an approach based on using lower(range) > value and upper(range) < value, as that doesn't account for the inclusivity/exclusivity of the range's bounds.
I'm using Postgres 9.6.5, but it doesn't look like anything has changed in this regard in Postgres 10.
I [...] rejected an approach based on using lower(range) > value and upper(range) < value, as that doesn't account for the inclusivity/exclusivity of the range's bounds.
I am not sure what you mean with that. lower() and upper() do account for inclusive/exclusive: lower('(1,10]'::int4range); returns 2 and lower('[1,10]'::int4range); returns 1
It seems to me creating an operator for this would be quite easy:
Create two functions to compare an int to an int4range:
create function int_smaller_than_range(p_value int, p_check int4range)
returns boolean
as
$$
select p_value < lower(p_check);
$$
language sql;
create function int_greater_than_range(p_value int, p_check int4range)
returns boolean
as
$$
select p_value > upper(p_check);
$$
language sql;
Then create the operators:
create operator < (
procedure = int_smaller_than_range,
leftarg = int,
rightarg = int4range,
negator = >
);
create operator > (
procedure = int_greater_than_range,
leftarg = int,
rightarg = int4range,
negator = <
);
This can now be used like this:
select 4 > int4range(5,10); -> false
select 4 < int4range(4,10,'[]'); -> false
select 4 < int4range(4,10,'(]'); -> true
select 5 > int4range(4,10,'[]'); -> false
select 11 > int4range(4,10,'[]'); -> false
select 11 > int4range(4,10,'[)'); -> true

T-SQL Join on foreign key that has leading zero

I need to link various tables that each have a common key (a serial number in this case). In some tables the key has a leading zero e.g. '037443' and on others it doesn't e.g. '37443'. In both cases the serial refers to the same product. To confound things serial 'numbers' are not always just numeric e.g. may be "BDO1234", in these cases there is never a leading zero.
I'd prefer to use the WHERE statement (WHERE a.key = b.key) but could use joins if required. Is there any way to do this?
I'm still learning so please keep it simple if possible. Many thanks.
Based on the accepted answer in this link, I've written a small tsql sample to show you what I meant by 'the right direction':
Create the test table:
CREATE TABLE tblTempTest
(
keyCol varchar(20)
)
GO
Populate it:
INSERT INTO tblTempTest VALUES
('1234'), ('01234'), ('10234'), ('0k234'), ('k2304'), ('00034')
Select values:
SELECT keyCol,
SUBSTRING(keyCol, PATINDEX('%[^0]%', keyCol + '.'), LEN(keyCol)) As trimmed
FROM tblTempTest
Results:
keyCol trimmed
-------------------- --------------------
1234 1234
01234 1234
10234 10234
0k234 k234
k2304 k2304
00034 34
Cleanup:
DROP TABLE tblTempTest
Note that the values are alpha-numeric, and only leading zeroes are trimmed.
One possible drawback is that if there is a 0 after a white space it will not be trimmed, but that's an easy fix - just add ltrim:
SUBSTRING(LTRIM(keyCol), PATINDEX('%[^0]%', LTRIM(keyCol + '.')), LEN(keyCol)) As trimmed
You need to create a function
CREATE FUNCTION CompareSerialNumbers(#SerialA varchar(max), #SerialB varchar(max))
RETURNS bit
AS
BEGIN
DECLARE #ReturnValue AS bit
IF (ISNUMERIC(#SerialA) = 1 AND ISNUMERIC(#SerialB) = 1)
SELECT #ReturnValue =
CASE
WHEN CAST(#SerialA AS int) = CAST(#SerialB AS int) THEN 1
ELSE 0
END
ELSE
SELECT #ReturnValue =
CASE
WHEN #SerialA = #SerialB THEN 1
ELSE 0
END
RETURN #ReturnValue
END;
GO
If both are numeric then it compares them as integers otherwise it compares them as strings.

How do I convert a character to integer within a PostgreSQL (9.1) function?

I have this following code:
BEGIN
x := split_part(text, ',', 1);
UPDATE albumphoto SET order = 1 WHERE idtable = 1 AND idx = x;
END
But my column table named idx is a numeric type, and the split_part returns a character type to the variable x. I've tried using CAST, but I don't know how to use it properly.
Any ideas?
Like this:
UPDATE albumphoto SET order = 1 WHERE idtable = 1 AND idx = CAST (x AS INTEGER);
(Use appropriate numeric type instead of INTEGER).
Or simpler:
UPDATE albumphoto
SET "order" = 1
WHERE idtable = 1
AND idx = split_part(text, ',', 1)::int -- cast to actual type (if not a string type)
AND "order" IS DISTINCT FROM 1;
expression::type is the simple (non-SQL-standard) Postgres way to cast. Details in the manual in the chapter Type Casts.
More about data types in PostgreSQL.
The last predicate I added is useful if "order" could already be 1, in which case the update wouldn't change anything - at full cost. Rather do nothing instead. Related (see last paragraph):
How do I (or can I) SELECT DISTINCT on multiple columns?
And you don't need a variable.

How to get min/max of two integers in Postgres/SQL?

How do I find the maximum (or minimum) of two integers in Postgres/SQL? One of the integers is not a column value.
I will give an example scenario:
I would like to subtract an integer from a column (in all rows), but the result should not be less than zero. So, to begin with, I have:
UPDATE my_table
SET my_column = my_column - 10;
But this can make some of the values negative. What I would like (in pseudo code) is:
UPDATE my_table
SET my_column = MAXIMUM(my_column - 10, 0);
Have a look at GREATEST and LEAST.
UPDATE my_table
SET my_column = GREATEST(my_column - 10, 0);
You want the inline sql case:
set my_column = case when my_column - 10 > 0 then my_column - 10 else 0 end
max() is an aggregate function and gets the maximum of a row of a result set.
Edit: oops, didn't know about greatest and least in postgres. Use that instead.