Perform Different Calculations Based on Count Outcome - postgresql

Today I am attempting to use the COUNT(*) function to return rows that match my criteria as seen below.
SELECT COUNT(*)
FROM "mv_fundraiser_report"
WHERE thank_you_delivered = true
What I am attempting to do is to perform a different calculation based on the number that is returned. For instance:
If COUNT > 2000
RETURN COUNT * 2
If COUNT < 2000
RETURN COUNT * 3
I'm looking for guidance as to how I could execute this task within PostgreSQL. Thanks!

Using condition expression CASE
select case
when cnt > 2000
then cnt * 2
else cnt * 3
end new_cnt
from (
select COUNT(*) as cnt
from "mv_fundraiser_report"
where thank_you_delivered = true
) t

Related

SQL query to collect rows above and below a certain match in Postgres

I have a postgres table A where I am looking to query n rows above and below a certain chosen value in a column.
I have already implemented two basic queries as shown below
SELECT * FROM A
WHERE a >= 100
AND a <= 100+n
and
SELECT * FROM A
WHERE a <= 100
AND a >= 100-n
Is there a way to merge these two queries into 1 instead of having to define two separate queries?
Naively just put both conditions in the where clause and link them with OR:
SELECT * FROM A
WHERE ( a >= 100 AND a <= 100+n )
OR (a <= 100 AND a >= 100-n )
but that will probably perform badly.
with a union,
SELECT * FROM A
WHERE a >= 100
AND a <= 100+n
UNION -- use UNION ALL here if you want two copies of all a=100 rows
SELECT * FROM A
WHERE a <= 100
AND a >= 100-n
Or by reasoning about ordered values and noting that the conditions specify ranges on a and noticing that they overlap.
SELECT * FROM A
WHERE a >= 100-n
AND a <= 100+n
this can also be rewritten as
SELECT * FROM A
WHERE a BETWEEN 100-n AND 100+n

Postgresql SUM calculated column

I am trying to create some sql to calculate the worth of a users inventory and have manage to get it to work up to the final step.
SELECT DISTINCT ON (pricing_cards.card_id)
(inventory_cards.nonfoil * pricing_cards.nonfoil) + (inventory_cards.foil * pricing_cards.foil) as x
FROM inventory_cards
INNER JOIN pricing_cards ON pricing_cards.card_id = inventory_cards.card_id
WHERE inventory_cards.user_id = 1
ORDER BY pricing_cards.card_id, pricing_cards.date DESC;
The code above bring back a single column that has the correct calculation for card. I now need to sum this column together but keep getting errors when I try to sum it.
Adding SUM((inventory_cards.nonfoil * pricing_cards.nonfoil) + (inventory_cards.foil * pricing_cards.foil)) throws the following error
ERROR: column "pricing_cards.card_id" must appear in the GROUP BY clause or be used in an aggregate function
LINE 6: ORDER BY pricing_cards.card_id, pricing_cards.date DESC;
Adding GROUP BY pricing_cards.card_id, pricing_cards.date seems to fix the errors but is returning the same column of calculated values.
so:
SELECT DISTINCT ON (pricing_cards.card_id)
SUM((inventory_cards.nonfoil * pricing_cards.nonfoil) + (inventory_cards.foil * pricing_cards.foil)) as x
FROM inventory_cards
INNER JOIN pricing_cards ON pricing_cards.card_id = inventory_cards.card_id
WHERE inventory_cards.user_id = 1
GROUP BY pricing_cards.card_id, pricing_cards.date
ORDER BY pricing_cards.card_id, pricing_cards.date DESC;
Returns:
x
0.71
29.92
25.67
171.20
0.32
0.26
I suggest you use a subquery to get the latest pricing data, then join and sum:
SELECT
SUM(inventory_cards.nonfoil * latest_pricing.nonfoil + inventory_cards.foil * latest_pricing.foil)
FROM inventory_cards
INNER JOIN (
SELECT DISTINCT ON (card_id)
card_id, nonfoil, foild
FROM pricing_cards
ORDER BY pricing_cards.card_id, pricing_cards.date DESC
) AS latest_pricing USING (card_id)
WHERE inventory_cards.user_id = 1
For alternatives in the subquery, see also Select first row in each GROUP BY group? and Optimize GROUP BY query to retrieve latest row per user.

Postgres optimize SELECT and indexes

I have written a query to summarise financial transactions so that I can go back to any date and find out what the financial position was at that time.
The data is multi company so the data holds information for all the group companies
Being financial information, some of the accounts reset themselves at each year end whilst others have a running balance.
The structure of the tables is:
nominal_account - Return 1 row for each account
nominal-transaction_lines - the full dataset
nominal_period_year - is a summary of transactions that is based on the nominal account, month/year and the financial company.
My SQL below works but takes over a minute to generate (the SQL below is based on today's date).
The query is broken into a few sections.
The first part of the case (na1.id=178) is a special account that is the summary of all income/expenditure records for that financial year.
The second part first looks for any records in the summary table until the last month end and then goes to the transaction table to find any records since the beginning of the current month. Added together that makes the balance.
At present the transaction table has around 25m records and the summary table 26000.
I am not asking for the query to be written for me, just some hints as to how to speed it up. So if anyone could suggest ways to optimize it or which indexes would help to speed it up, I would be very grateful.
SELECT id, nominal_code AS nom_code, description AS descr, COALESCE(management_type,0) AS type,
case
when na1.id = 178
then (SELECT coalesce( (SELECT sum(period_movement)
FROM nominal_period_year JOIN nominal_account on nominal_account.id = nominal_period_year.nominal_account
WHERE (period_key/10000000000) <= 201704 AND
financial_company = 1 AND
nominal_account.profit_or_balance = true)
+
(SELECT sum(period_movement) FROM nominal_period_year WHERE nominal_account = na1.id AND
(period_key/10000000000) <= 201803 AND nominal_period_year.financial_company = 1)
+
(SELECT GREATEST(0, sum(db_Amount)) - GREATEST(0, sum(cr_Amount))
FROM nominal_transaction_lines
WHERE transaction_date between '2018-04-01' AND '2018-04-27'
AND original_id = 0
AND reversed_by = 0
AND status = 'A'
AND financial_company = 1 AND status = 'A' AND nominal_account = na1.id)
,0.00) AS balance)
ELSE
(SELECT coalesce(
(SELECT sum(period_movement) FROM nominal_period_year WHERE nominal_account = na1.id AND
(case
when na1.profit_or_balance = true
then (period_key/10000000000) > 201704
ELSE period_key > 0
end)
AND (period_key/10000000000) <= 201803 AND nominal_period_year.financial_company = 1)
+
(SELECT GREATEST(0, sum(db_Amount)) - GREATEST(0, sum(cr_Amount))
FROM nominal_transaction_lines
WHERE transaction_date between '2018-04-01' AND '2018-04-27'
AND original_id = 0
AND reversed_by = 0
AND financial_company = 1
AND status = 'A'
AND nominal_account = na1.id)
,0) AS balance)
end
FROM nominal_account AS na1
order by nom_code;

Looping SQL query - PostgreSQL

I'm trying to get a query to loop through a set of pre-defined integers:
I've made the query very simple for this question.. This is pseudo code as well obviously!
my_id = 0
WHILE my_id < 10
SELECT * from table where id = :my_id`
my_id += 1
END
I know that for this query I could just do something like where id < 10.. But the actual query I'm performing is about 60 lines long, with quite a few window statements all referring to the variable in question.
It works, and gets me the results I want when I have the variable set to a single figure.. I just need to be able to re-run the query 10 times with different variables hopefully ending up with one single set of results.
So far I have this:
CREATE OR REPLACE FUNCTION stay_prices ( a_product_id int ) RETURNS TABLE (
pid int,
pp_price int
) AS $$
DECLARE
nights int;
nights_arr INT[] := ARRAY[1,2,3,4];
j int;
BEGIN
j := 1;
FOREACH nights IN ARRAY nights_arr LOOP
-- query here..
END LOOP;
RETURN;
END;
$$ LANGUAGE plpgsql;
But I'm getting this back:
ERROR: query has no destination for result data
HINT: If you want to discard the results of a SELECT, use PERFORM instead.
So do I need to get my query to SELECT ... INTO the returning table somehow? Or is there something else I can do?
EDIT: this is an example of the actual query I'm running:
\x auto
\set nights 7
WITH x AS (
SELECT
product_id, night,
LAG(night, (:nights - 1)) OVER (
PARTITION BY product_id
ORDER BY night
) AS night_start,
SUM(price_pp_gbp) OVER (
PARTITION BY product_id
ORDER BY night
ROWS BETWEEN (:nights - 1) PRECEDING
AND CURRENT ROW
) AS pp_price,
MIN(spaces_available) OVER (
PARTITION BY product_id
ORDER BY night
ROWS BETWEEN (:nights - 1) PRECEDING
AND CURRENT ROW
) AS min_spaces_available,
MIN(period_date_from) OVER (
PARTITION BY product_id
ORDER BY night
ROWS BETWEEN (:nights - 1) PRECEDING
AND CURRENT ROW
) AS min_period_date_from,
MAX(period_date_to) OVER (
PARTITION BY product_id
ORDER BY night
ROWS BETWEEN (:nights - 1) PRECEDING
AND CURRENT ROW
) AS max_period_date_to
FROM products_nightlypriceperiod pnpp
WHERE
spaces_available >= 1
AND min_group_size <= 1
AND night >= '2016-01-01'::date
AND night <= '2017-01-01'::date
)
SELECT
product_id as pid,
CASE WHEN x.pp_price > 0 THEN x.pp_price::int ELSE null END as pp_price,
night_start as from_date,
night as to_date,
(night-night_start)+1 as duration,
min_spaces_available as spaces
FROM x
WHERE
night_start = night - (:nights - 1)
AND min_period_date_from = night_start
AND max_period_date_to = night;
That will get me all the nights night periods available for all my products in 2016 along with the price for the period and the max number of spaces I could fill in that period.
I'd like to be able to run this query to get all the periods available between 2 and 30 days for all my products.
This is likely to produce a table with millions of rows. The plan is to re-create this table periodically to enable a very quick look up of what's available for a particular date. The products_nightlypriceperiod represents a night of availability of a product - e.g. Product X has 3 spaces left for Jan 1st 2016, and costs £100 for the night.
Why use a loop? You can do something like this (using your first query):
with params as (
select generate_series(1, 10) as id
)
select t.*
from params cross join
table t
where t.id = params.id;
You can modify params to have the values you really want. Then just use cross join and let the database "do the looping."

T-sql Percent calculation stuffed with WHERE clauses doesn't work

I have t-sql as follows:
SELECT (COUNT(Intakes.fk_ClientID) * 100) / (
SELECT count(*)
FROM INTAKES
WHERE Intakes.AdmissionDate >= #StartDate
)
FROM Intakes
WHERE Intakes.fk_ReleasedFromID = '1'
AND Intakes.AdmissionDate >= #StartDate;
I'm trying to get the percentage of clients who have releasedfromID = 1 out of a subset of clients who have a certain range of admission dates. But I get rows of 1's and 0's instead. Now, I can get the percentage if I take out the where clauses, it works:
SELECT (COUNT(Intakes.fk_ClientID) * 100) / (
SELECT count(*)
FROM INTAKES
)
FROM Intakes
WHERE Intakes.fk_ReleasedFromID = '1';
works fine. It selects ClientIDs where ReleasedFromID =1, multiplies it by 100 and divides by total rows in Intakes. But how do you run percentage with WHERE clauses as above?
After reading comment from #Anssssss
SELECT (COUNT(Intakes.fk_ClientID) * 100.0) / (
SELECT count(*)
FROM INTAKES
) 'percentage'
FROM Intakes
WHERE Intakes.fk_ReleasedFromID = '1';