Postgresql upper limit of a calculated field - postgresql

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

Related

comparison within in clause of postgresql

Is it possible to add condition within the in clause of postgresql
for example
select ... where (t1.subject,t2.weight) in ((1,2),(2,3))
I want to check whether subject is 1 but weight can be >= 2 not just 2 and so on. So that condition would logically look somewhat like
select ... where (t1.subject,t2.weight) in ((1,>2),(2,>3))
No, this is not possible. You need to write
…
WHERE t1.subject = 1 AND t2.weight > 2
OR t1.subject = 2 AND t2.weight > 3;
You can select value of object using subquery. Simple just select query subject which are having weight greater than >=2.
select ... where (t1.subject,t2.weight) in (select subject FROM ... where weight >=2 ,select subject FROM ... where weight >=3 );

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 to keep one record in specific column and make other record value 0 in group by clause in PostgreSQL?

I have a set of data like this
The Result should look Like this
My Query
SELECT max(pi.pi_serial) AS proforma_invoice_id,
max(mo.manufacturing_order_master_id) AS manufacturing_order_master_id,
max(pi.amount_in_local_currency) AS sales_value,
FROM proforma_invoice pi
JOIN schema_order_map som ON pi.pi_serial = som.pi_id
LEFT JOIN manufacturing_order_master mo ON som.mo_id = mo.manufacturing_order_master_id
WHERE to_date(pi.proforma_invoice_date, 'DD/MM/YYYY') BETWEEN to_date('01/03/2021', 'DD/MM/YYYY') AND to_date('19/04/2021', 'DD/MM/YYYY')
AND pi.pi_serial in (9221,
9299)
GROUP BY mo.manufacturing_order_master_id,
pi.pi_serial
ORDER BY pi.pi_serial
Option 1: Create a "Running Total" field in Crystal Reports to sum up only one "sales_value" per "proforma_invoice_id".
Option 2: Add a helper column to your Postgresql query like so:
case
when row_number()
over (partition by proforma_invoice_id
order by manufacturing_order_master_id)
= 1
then sales_value
else 0
end
as sales_value
I prepared this SQLFiddle with an example for you (and would of course like to encourage you to do the same for your next db query related question on SO, too :-)

What does filter mean?

select driverid, count(*)
from f1db.results
where position is null
group by driverid order by driverid
My thought: first find out all the records that position is null then do the aggregate function.
select driverid, count(*) filter (where position is null) as outs
from f1db.results
group by driverid order by driverid
First time, I meet with filter clause, not sure what does it mean?
Two code block results are different.
Already googled, seems don't have many tutorials about the FILTER. Kindly share some links.
A more helpful term to search for is aggregate filters, since FILTER (WHERE) is only used with aggregates.
Filter clauses are an additional filter that is applied only to that aggregate and nowhere else. That means it doesn't affect the rest of the columns!
You can use it to get a subset of the data, like for example, to get the percentage of cats in a pet shop:
SELECT shop_id,
CAST(COUNT(*) FILTER (WHERE species = 'cat')
AS DOUBLE PRECISION) / COUNT(*) as "percentage"
FROM animals
GROUP BY shop_id
For more information, see the docs
FILTER ehm... filters the records which should be aggregated - in your case, your aggregation counts all positions that are NULL.
E.g. you have 10 records:
SELECT COUNT(*)
would return 10.
If 2 of the records have position = NULL
than
SELECT COUNT(*) FILTER (WHERE position IS NULL)
would return 2.
What you want to achieve is to count all non-NULL records:
SELECT COUNT(*) FILTER (WHERE position IS NOT NULL)
In the example, it returns 8.

How to get a sum of all rows that meets condition in postgres

I am trying to return sums with their specific conditions.
SELECT
COUNT(*),
SUM("transactionTotal" WHERE "entryType"=sold) as soldtotal,
SUM(case when "entryType"=new then "transactionTotal" else 0 end) as newtotal
FROM "MoneyTransactions"
WHERE cast("createdAt" as date) BETWEEN '2020-10-08' AND '2020-10-09'
I am trying to sum up the rows with "entryType"=sold and "entryType"=new and return those values separately.
obviously both my logic are wrong.
can someone lend a hand.
You were on the right track to use conditional aggregation, but your syntax is slightly off. Try this version:
SELECT
COUNT(*) AS total_cnt,
SUM(transactionTotal) FILTER (WHERE entryType = 'sold') AS soldtotal,
SUM(transactionTotal) FILTER (WHERE entryType = 'new') AS newtotal
FROM MoneyTransactions
WHERE
createdAt::date BETWEEN '2020-10-08' AND '2020-10-09';
Note: Assuming your createdAt column already be date, then there is no point in casting it. If it is text, then yes you would need to convert it, but you might have to use TO_DATE depending on its format.