PostgreSQL - Comparing value based on condition (<, >, =) which written on a column? - postgresql

I have this sample database:
Table 1:
Type Condition_weight Price
A >50 1000
A >10 & <50 500
A <10 100
As I remember, I can do a comparison on the Condition_weight without doing too much on query.
My expectation query is something like this:
select Price from Table_1
where Type = 'A'
and {my_input_this_is a number} satisfy Condition_weight
I read it somewhere about this solution but cant find it again.

You can create a function that returns true - you will have to do the logic to extract min and max and compare value.
pseudo code...
CREATE FUNCTION conditionWeightIsSatisfed(number weight)
BEGIN
set #minValue = 0;
set #MaxValue = 1000;
..... do your conversion of the text values.....
select weight >= #minValue and weight <= #maxValue
END

Related

Looking to reduce postgres queries to one query

I want to reduce the number of queries I'm making to my postgres database.
Currently I have this
select * from token_balances where asset_id = '36f813e4-403a-4246-a405-8efc0cbde76a' AND tick < yesterday ORDER BY tick DESC LIMIT 1;
select * from token_balances where asset_id = '36f813e4-403a-4246-a405-8efc0cbde76a' AND tick < last_week ORDER BY tick DESC LIMIT 1;
Is there some way I can make this into one query. Or is there even any need to reduce it?
Thanks
You can simply use the query with the greater date of tick. The first query already includes the results coming from the second one.
Just use the first query and you are good to go.
select * from token_balances where asset_id = '36f813e4-403a-4246-a405-8efc0cbde76a' AND tick < '2022-05-26T14:13:42.914Z' ORDER BY tick DESC LIMIT 1;
Rather than use yesterday (which is a valid reference) and last_week (which is not valid) just convert to date arithmetic. You can use simple date subtraction:
select *
from token_balances
where asset_id = '36f813e4-403a-4246-a405-8efc0cbde76a'
and tick < current_date - :num_days
order by tick desc
limit 1;

How should I query an integer where there are decimals in the data?

SELECT * FROM table1 WHERE spent>= '1000'
This query still bring out numbers such as 598.99 and 230.909. My question is why is it doing this when I asked to search over or equal to 1000. Is there anyway to query so it only shows equal and more than 1000?
This happens because your '1000' is a text value. The other value is (or is converted to) text, too, so you end up with byte-per-byte comparison.
598.99 is greater then 1000 because 5... is greater then 1....
Cast to numeric types to do a proper comparison:
SELECT * FROM table1 WHERE spent::numeric >= '1000'::numeric
Or simply:
SELECT * FROM table1 WHERE spent::numeric >= 1000
You must compare numbers to get numeric comparison.
Use
WHERE CAST(spent AS numeric) >= 1000

Retrieving multiple values from a large jsonb field faster (postgresql 9.4)

tl;dr
Using PSQL 9.4, is there a way to retrieve multiple values from a jsonb field, such as you would with the imaginary function:
jsonb_extract_path(x, ARRAY['a_dictionary_key', 'a_second_dictionary_key', 'a_third_dictionary_key'])
With the hope of speeding up the otherwise almost linear time required to select multiple values (1 value = 300ms, 2 values = 450ms, 3 values = 600ms)
Background
I have the following jsonb table:
CREATE TABLE "public"."analysis" (
"date" date NOT NULL,
"name" character varying (10) NOT NULL,
"country" character (3) NOT NULL,
"x" jsonb,
PRIMARY KEY(date,name)
);
With roughly 100 000 rows where each rows has a jsonb dictionary with 90+ keys and corresponding values. I'm trying to write an SQL query to select a few (< 10) key+values in a fairly quick way (< 500 ms)
Index and querying: 190ms
I started by adding an index:
CREATE INDEX ON analysis USING GIN (x);
This makes querying based on values in the "x" dictionary fast, such as this:
SELECT date, name, country FROM analysis where date > '2014-01-01' and date < '2014-05-01' and cast(x#>> '{a_dictionary_key}' as float) > 100;
This takes ~190 ms (acceptable for us)
Retrieving dictionary values
However, once I start adding keys to return in the SELECT part, execution time rises almost linear:
1 value: 300ms
select jsonb_extract_path(x, 'a_dictionary_key') from analysis where date > '2014-01-01' and date < '2014-05-01' and cast(x#>> '{a_dictionary_key}' as float) > 100;
Takes 366ms (+175ms)
select x#>'{a_dictionary_key}' as gear_down_altitude from analysis where date > '2014-01-01' and date < '2014-05-01' and cast(x#>> '{a_dictionary_key}' as float) > 100 ;
Takes 300ms (+110ms)
3 values: 600ms
select jsonb_extract_path(x, 'a_dictionary_key'), jsonb_extract_path(x, 'a_second_dictionary_key'), jsonb_extract_path(x, 'a_third_dictionary_key') from analysis where date > '2014-01-01' and date < '2014-05-01' and cast(x#>> '{a_dictionary_key}' as float) > 100;
Takes 600ms (+410, or +100 for each value selected)
select x#>'{a_dictionary_key}' as a_dictionary_key, x#>'{a_second_dictionary_key}' as a_second_dictionary_key, x#>'{a_third_dictionary_key}' as a_third_dictionary_key from analysis where date > '2014-01-01' and date < '2014-05-01' and cast(x#>> '{a_dictionary_key}' as float) > 100 ;
Takes 600ms (+410, or +100 for each value selected)
Retrieving more values faster
Is there a way to retrieve multiple values from a jsonb field, such as you would with the imaginary function:
jsonb_extract_path(x, ARRAY['a_dictionary_key', 'a_second_dictionary_key', 'a_third_dictionary_key'])
Which could possibly speed up these lookups. It can return them either as columns or as an list/array or even a json object.
Retrieving an array using PL/Python
Just for the heck of it I made a custom function using PL/Python, but that was much slower (5s+), possibly due to json.loads:
CREATE OR REPLACE FUNCTION retrieve_objects(data jsonb, k VARCHAR[])
RETURNS TEXT[] AS $$
if not data:
return []
import simplejson as json
j = json.loads(data)
l = []
for i in k:
l.append(j[i])
return l
$$ LANGUAGE plpython2u;
# Usage:
# select retrieve_objects(x, ARRAY['a_dictionary_key', 'a_second_dictionary_key', 'a_third_dictionary_key']) from analysis where date > '2014-01-01' and date < '2014-05-01'
Update 2015-05-21
I re-implemented the table using hstore with GIN index and the performance is almost identical to using jsonb, i.e not helpfull in my case.
You're using the #> operator, which looks like it performs a path search. Have you tried a normal -> lookup? Like:
select json_column->'json_field1'
, json_column->'json_field2'
It would be interesting to see what happened if you used a temporary table. Like:
create temporary table tmp_doclist (doc jsonb)
;
insert tmp_doclist
(doc)
select x
from analysis
where ... your conditions here ...
;
select doc->'col1'
, doc->'col2'
, doc->'col3'
from tmp_doclist
;
This is hard to test without the data.
Create a custom type
create type my_query_result_type (
a_dictionary_key float,
a_second_dictionary_key float
)
And your query
select (json_populate_record(null::my_query_result_type,j::json)).* from analysis;
You should be able to use a temporary table instead of type which will be created at, runtime making your query dynamic.
But first check it out if this helps form the performance point of view.

Postgresql upper limit of a calculated field

is there a way to set an upper limit to a calculation (calculated field) which is already in a CASE clause? I'm calculating percentages and, obviously, don't want the highest value exceed '100'.
If it wasn't in a CASE clause already, I'd create something like 'case when calculation > 100.0 then 100 else calculation end as needed_percent' but I can't do it now..
Thanks for any suggestions.
I think using least function will be the best option.
select least((case when ...), 100) from ...
There is a way to set an upper limit on a calculated field by creating an outer query. Check out my example below. The inner query will be the query that you have currently. Then just create an outer query on it and use a WHERE clause to limit it to <= 1.
SELECT
z.id,
z.name,
z.percent
FROM(
SELECT
id,
name,
CASE WHEN id = 2 THEN sales/SUM(sales) ELSE NULL END AS percent
FROM
users_table
) AS z
WHERE z.percent <= 1

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.