PostgreSQL - Aliases column and HAVING - postgresql

SELECT
CASE WHEN SUM(X.Count)*3600 is null THEN '0'
ELSE
SUM(X.Count)*3600
END AS PJZ,
X.Mass
FROM X
WHERE X.Mass > 2000
HAVING ((X.Mass / PJZ * 100) - 100) >= 10;
Getting: ERROR: Column »pjz« doesn't exists.
How can I do something like this?

You can't use aliases in a having, and have to duplicate the statement in the having cluause. Since you only want to check for null, you could do this:
SELECT coalesce(SUM(X.Count)*3600, 0) AS PJZ, X.Mass
FROM X
WHERE X.Mass > 2000
HAVING ((X.Mass / coalesce(SUM(X.Count)*3600, 0) * 100) - 100) >= 10;

Other option is to surround query by WITH statement - for example:
WITH x as (
SELECT coalesce(SUM(X.Count)*3600, 0) AS PJZ, X.Mass
FROM X
WHERE X.Mass > 2000
)
SELECT * from X WHERE PJZ >=10
It is far better then code duplication in my opinion

Wrap it into a derived table:
SELECT CASE
WHEN PJZ = 0 THEN 100
ELSE PJZ
END as PJZ,
mass
FROM (
SELECT CASE
WHEN SUM(X.Count)*3600 is null THEN '0'
ELSE SUM(X.Count)*3600
END AS PJZ,
X.Mass
FROM X
WHERE X.Mass > 2000
GROUP BY X.mass
) t
WHERE PJZ = 0
OR ((X.Mass / PJZ * 100) - 100) >= 10;
(Note that I added the missing group by as otherwise the query would not be valid)

Related

Is there a smarter method to create series with different intervalls for count within a query?

I want to create different intervalls:
0 to 10 steps 1
10 to 100 steps 10
100 to 1.000 steps 100
1.000 to 10.000 steps 1.000
to query a table for count the items.
with "series" as (
(SELECT generate_series(0, 10, 1) AS r_from)
union
(select generate_series(10, 90, 10) as r_from)
union
(select generate_series(100, 900, 100) as r_from)
union
(select generate_series(1000, 9000, 1000) as r_from)
order by r_from
)
, "range" as ( select r_from
, case
when r_from < 10 then r_from + 1
when r_from < 100 then r_from + 10
when r_from < 1000 then r_from + 100
else r_from + 1000
end as r_to
from series)
select r_from, r_to,(SELECT count(*) FROM "my_table" WHERE "my_value" BETWEEN r_from AND r_to) as "Anz."
FROM "range";
I think generate_series is the right way, there is another way, we can use simple math to calculate the numbers.
SELECT 0 as r_from,1 as r_to
UNION ALL
SELECT power(10, steps ) * v ,
power(10, steps ) * v + power(10, steps )
FROM generate_series(1, 9, 1) v
CROSS JOIN generate_series(0, 3, 1) steps
so that might as below
with "range" as
(
SELECT 0 as r_from,1 as r_to
UNION ALL
SELECT power(10, steps) * v ,
power(10, steps) * v + power(10, steps)
FROM generate_series(1, 9, 1) v
CROSS JOIN generate_series(0, 3, 1) steps
)
select r_from, r_to,(SELECT count(*) FROM "my_table" WHERE "my_value" BETWEEN r_from AND r_to) as "Anz."
FROM "range";
sqlifddle
Rather than generate_series you could create defined integer range types (int4range), then test whether your value is included within the range (see Range/Multirange Functions and Operators. So
with ranges (range_set) as
( values ( int4range(0,10,'[)') )
, ( int4range(10,100,'[)') )
, ( int4range(100,1000,'[)') )
, ( int4range(1000,10000,'[)') )
) --select * from ranges;
select lower(range_set) range_start
, upper(range_set) - 1 range_end
, count(my_value) cnt
from ranges r
left join my_table mt
on (mt.my_value <# r.range_set)
group by r.range_set
order by lower(r.range_set);
Note the 3rd parameter in creating the ranges.
Creating a CTE as above is good if your ranges are static, however if dynamic ranges are required you can put the ranges into a table. Changes ranges then becomes a matter to managing the table. Not simple but does not require code updates. The query then reduces to just the Main part of the above:
select lower(range_set) range_start
, upper(range_set) - 1 range_end
, count(my_value) cnt
from range_tab r
left join my_table mt
on (mt.my_value <# r.range_set)
group by r.range_set
order by lower(r.range_set);
See demo for both here.

Duplicate Category Name Error 42710 in Crosstab query Postgres

I am having a tough time getting my crosstab query to execute. I keep getting "Duplicate Category Name Error 42710" when I run the following query.
SELECT
*
FROM
crosstab (
$$
Select
date_year,
city_size,
sum(Total_Rev)
From
(
SELECT
EXTRACT(YEAR FROM SOLD.DATE) Date_Year,
CASE WHEN CITY.POPULATION < 3700000 THEN 'SMALL' WHEN CITY.POPULATION >= 3700000
AND CITY.POPULATION < 6700000 THEN 'MEDIUM' WHEN CITY.POPULATION >= 6700000
AND CITY.POPULATION < 9000000 THEN 'LARGE' WHEN CITY.POPULATION >= 9000000 THEN 'X_LARGE' END City_Size,
CASE WHEN DISCOUNT_ON.DISCOUNT_PERCENTAGE IS NOT NULL THEN (
(DISCOUNT_ON.DISCOUNT_PERCENTAGE / 100) * PRODUCT.RETAIL_PRICE
) * SOLD.QUANTITY WHEN DISCOUNT_ON.DISCOUNT_PERCENTAGE IS NULL THEN PRODUCT.RETAIL_PRICE * SOLD.QUANTITY END Total_Rev
FROM
SOLD,
STORE,
CITY,
DISCOUNT_ON,
PRODUCT
WHERE
CITY.CITY_NAME = STORE.CITY_NAME
AND SOLD.PID = PRODUCT.PID
AND SOLD.PID = DISCOUNT_ON.PID
AND SOLD.DATE = DISCOUNT_ON.DATE
AND STORE.STORE_NUMBER = SOLD.STORE_NUMBER
) tbl1
group by
date_year,city_size
order by
date_year,city_size $$,
$$
SELECT
CASE WHEN CITY.POPULATION < 3700000 THEN 'SMALL' WHEN CITY.POPULATION >= 6700000
AND CITY.POPULATION < 9000000 THEN 'LARGE' END City_Size
FROM
CITY $$
) AS FINAL_RESULT(date_year Numeric,"SMALL" REAL,"MEDIUM" REAL,"LARGE" REAL,"X_LARGE" REAL );
When I run the subquery I do get results:
[Image of Results from Subquery]
Thank You
Using filtered aggregation is typically easier to deal with than using the crosstab() function:
select date_year,
sum(Total_Rev) filter (where city_size = 'SMALL') as small,
sum(Total_Rev) filter (where city_size = 'MEDIUM') as medium,
sum(Total_Rev) filter (where city_size = 'LARGE') as large,
sum(Total_Rev) filter (where city_size = 'X_LARGE') as x_large
from (
SELECT EXTRACT(YEAR FROM SOLD.DATE) Date_Year,
CASE
WHEN city.population < 3700000 THEN 'SMALL'
WHEN city.population >= 3700000 AND city.population < 6700000 then 'MEDIUM'
WHEN city.population >= 6700000 AND city.population < 9000000 then 'LARGE'
WHEN city.population >= 9000000 THEN 'X_LARGE'
END city_size,
CASE
WHEN DISCOUNT_ON.DISCOUNT_PERCENTAGE IS NOT NULL
THEN ((DISCOUNT_ON.DISCOUNT_PERCENTAGE / 100) * PRODUCT.RETAIL_PRICE) * SOLD.QUANTITY
WHEN DISCOUNT_ON.DISCOUNT_PERCENTAGE IS NULL
THEN PRODUCT.RETAIL_PRICE * SOLD.QUANTITY
END total_rev
FROM sold
JOIN store ON store.store_number = sold.store_number
JOIN city ON city.city_name = store.city_name
JOIN discount_on ON sold.pid = discount_on.pid AND sold.date = discount_on.date
JOIN product ON sold.pid = product.pid
) tbl1
group by date_year,city_size
order by date_year,city_size

Combining Two Queries and Calculating Result

I am attempting to calculate the difference between query 1:
select case
when cnt >= 1 AND cnt <= 2000 then cnt * 6
when cnt >= 2001 AND cnt <= 4000 then ((cnt - 2000) * 5) + 12000
when cnt >= 4001 AND cnt <= 6000 then ((cnt - 4000) * 4) + 22000
when cnt >= 6001 AND cnt <= 8000 then ((cnt - 6000) * 3) + 30000
when cnt >= 8001 then ((cnt - 8000) * 2) + 36000
else 1
end "Customer Investment"
from (
select COUNT(*) as cnt
from "mv_fundraiser_report"
where thank_you_delivered = true
[[AND {{NonProfit}}]]
[[AND {{StartDate}}]]
) t
And Query 2:
SELECT ((cast(A.TNUM as float)/cast(A.TDENOM as float))-(cast(A.FNUM as float)/cast(A.FDENOM as float)))*cast(A.TDENOM as float) AS "Heck"
FROM (SELECT
(SELECT SUM("public"."mv_fundraiser_report"."total_raised")
FROM "public"."mv_fundraiser_report"
WHERE ("public"."mv_fundraiser_report"."opt-in" = FALSE
AND NOT "public"."mv_fundraiser_report"."campaign_id" = 704943916598630
AND NOT "public"."mv_fundraiser_report"."campaign_id" = 193572775319413
AND NOT(first_name IS NULL
AND total_raised > 1000
AND fundraiser_type = 'Generic Fundraiser')
AND [[{{NonProfit}}]]
AND [[{{DateRange}}]])) AS FNUM,
(SELECT count(*) AS "count"
FROM "public"."mv_fundraiser_report"
WHERE ("public"."mv_fundraiser_report"."opt-in" = FALSE
AND NOT "public"."mv_fundraiser_report"."campaign_id" = 704943916598630
AND NOT "public"."mv_fundraiser_report"."campaign_id" = 193572775319413
AND NOT(first_name IS NULL
AND total_raised > 1000
AND fundraiser_type = 'Generic Fundraiser')
AND [[{{NonProfit}}]]
AND [[{{DateRange}}]])) AS FDENOM,
(SELECT SUM("public"."mv_fundraiser_report"."total_raised")
FROM "public"."mv_fundraiser_report"
WHERE ("public"."mv_fundraiser_report"."opt-in" = TRUE
AND NOT "public"."mv_fundraiser_report"."campaign_id" = 704943916598630
AND NOT "public"."mv_fundraiser_report"."campaign_id" = 193572775319413
AND NOT(first_name IS NULL
AND total_raised > 1000
AND fundraiser_type = 'Generic Fundraiser')
AND [[{{NonProfit}}]]
AND [[{{DateRange}}]])) AS TNUM,
(SELECT count(*) AS "count"
FROM "public"."mv_fundraiser_report"
WHERE ("public"."mv_fundraiser_report"."opt-in" = TRUE
AND NOT "public"."mv_fundraiser_report"."campaign_id" = 704943916598630
AND NOT "public"."mv_fundraiser_report"."campaign_id" = 193572775319413
AND NOT(first_name IS NULL
AND total_raised > 1000
AND fundraiser_type = 'Generic Fundraiser')
AND [[{{NonProfit}}]]
AND [[{{DateRange}}]])) AS TDENOM) A
Both of the queries work by themselves and return a single number, but I am struggling heavily with the syntax. I'm totally unsure of how I can make use of (window?) functions in order to achieve my end result. Any help is appreciated, thanks!
You can place each query into common table expressions, then subtract one from the other.
For example (reworked query 2 for simplification):
WITH one AS (
SELECT
CASE
WHEN cnt >= 1 AND cnt <= 2000 THEN cnt * 6
WHEN cnt >= 2001 AND cnt <= 4000 THEN ((cnt - 2000) * 5) + 12000
WHEN cnt >= 4001 AND cnt <= 6000 THEN ((cnt - 4000) * 4) + 22000
WHEN cnt >= 6001 AND cnt <= 8000 THEN ((cnt - 6000) * 3) + 30000
WHEN cnt >= 8001 THEN ((cnt - 8000) * 2) + 36000
ELSE 1
END "Customer Investment"
FROM (
SELECT COUNT(*) as cnt
FROM "mv_fundraiser_report"
WHERE thank_you_delivered = true
[[AND {{NonProfit}}]]
[[AND {{StartDate}}]]
) t
),
two AS (
SELECT (
(
SUM(public.mv_fundraiser_report.total_raised)
FILTER (WHERE public.mv_fundraiser_report.opt-in = FALSE)
)::float AS FNUM /
(
count(*)
FILTER (WHERE public.mv_fundraiser_report.opt-in = FALSE)
)::float AS FDENOM
) -
(
(
SUM(public.mv_fundraiser_report.total_raised)
FILTER (WHERE public.mv_fundraiser_report.opt-in = TRUE)
)::float AS TNUM /
(
count(*)
FILTER (WHERE public.mv_fundraiser_report.opt-in = TRUE)
)::float AS TDENOM
) AS "Heck"
FROM public.mv_fundraiser_report
WHERE NOT public.mv_fundraiser_report.campaign_id = 704943916598630
AND NOT public.mv_fundraiser_report.campaign_id = 193572775319413
AND NOT (first_name IS NULL
AND total_raised > 1000
AND fundraiser_type = 'Generic Fundraiser')
AND [[{{NonProfit}}]]
AND [[{{DateRange}}]])
)
SELECT one."Customer Investment" - two."Heck" AS difference
FROM one, two;
I don't have your data or schema to test this against, so this untested.

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';

Using aggregate functions on alias?

I want to make a query where I am computing the difference between two columns. Something like:
SELECT a,
b,
a - b as "diff"
FROM ...
Now I would like to calculate the stddev of the "diff" column using postgresql built-in stddev aggregate function. How can I achieve this?
Thanks.
EDIT:
The actual query is this:
SELECT tr.date_start,
tr.date_end,
(((CASE when(tourney_summary.val_curr_conv != 0) THEN tourney_summary.val_curr_conv * (tr.amt_won + tr.cnt_bounty * tourney_summary.amt_bounty) ELSE 0.0 END))) AS "amt_won_curr_conv",
(((CASE when(tourney_summary.val_curr_conv != 0) THEN tourney_summary.val_curr_conv * (tourney_summary.amt_buyin + tourney_summary.amt_fee + tourney_summary.amt_rebuy * tr.cnt_rebuy + tourney_summary.amt_addon * tr.cnt_addon + tourney_summary.amt_bounty) ELSE 0.0 END))) AS "amt_buyin_ttl_curr_conv",
((((CASE when(tourney_summary.val_curr_conv != 0) THEN tourney_summary.val_curr_conv * (tr.amt_won + tr.cnt_bounty * tourney_summary.amt_bounty) ELSE 0.0 END))) - (((CASE when(tourney_summary.val_curr_conv != 0) THEN tourney_summary.val_curr_conv * (tourney_summary.amt_buyin + tourney_summary.amt_fee + tourney_summary.amt_rebuy * tr.cnt_rebuy + tourney_summary.amt_addon * tr.cnt_addon + tourney_summary.amt_bounty) ELSE 0.0 END)))) as net_amt_won,
stddev((((CASE when(tourney_summary.val_curr_conv != 0) THEN tourney_summary.val_curr_conv * (tr.amt_won + tr.cnt_bounty * tourney_summary.amt_bounty) ELSE 0.0 END))) - (((CASE when(tourney_summary.val_curr_conv != 0) THEN tourney_summary.val_curr_conv * (tourney_summary.amt_buyin + tourney_summary.amt_fee + tourney_summary.amt_rebuy * tr.cnt_rebuy + tourney_summary.amt_addon * tr.cnt_addon + tourney_summary.amt_bounty) ELSE 0.0 END)))) as diff_std_dev
FROM tourney_summary,
tourney_results tr
WHERE
tr.id_player=1
AND tourney_summary.id_tourney = tr.id_tourney
AND ((tourney_summary.id_gametype = 1)
AND (((((((tourney_summary.id_table_type IN
(SELECT lttt.id_table_type
FROM tourney_table_type lttt
WHERE lttt.val_seats = 2))))))
AND (((((tourney_summary.id_table_type IN
(SELECT lttt.id_table_type
FROM tourney_table_type lttt
WHERE position('S' IN lttt.val_speed) > 0))
OR (tourney_summary.id_table_type IN
(SELECT lttt.id_table_type
FROM tourney_table_type lttt
WHERE position('H' IN lttt.val_speed) > 0))))))))
AND ((tourney_summary.date_start >= '2013/08/15 23:00:00')))
GROUP BY tr.date_start,
tr.date_end,
tourney_summary.val_curr_conv,
tr.amt_won,
tr.cnt_bounty,
tourney_summary.amt_bounty,
tourney_summary.amt_buyin,
tourney_summary.amt_fee,
tourney_summary.amt_rebuy,
tr.cnt_rebuy,
tourney_summary.amt_addon,
tr.cnt_addon
ORDER BY tr.date_end DESC;
The "a" and "b" expressions (the ones with CASE) are big. And I don't know how to avoid the copy/paste. In any case using stddev on the a-b expression returns a blank column. What am I doing wrong?
Thanks.
You pretty much answer it yourself. Calculate the standard deviation of the difference:
SELECT a,
b,
a - b as "diff",
stddev(a - b) AS "diff_stddev"
FROM ...
If a - b is a computationally expensive operation or is in fact a much more complex expression in reality, you can wrap it in a subquery:
SELECT a, b, "diff", stddev("diff") AS diff_stddev
FROM (
SELECT a, b, a - b
FROM ...
) x (a, b, "diff")
x is just a throw-away alias for the subquery table.
it's also possible to do this with cte
with cte1 as (
select a, b, a - b as diff
from ...
)
select
a, b, diff, stddev(diff) as diff_stddev
from cte1