postgresql, convert column into header without rowid - postgresql

I am trying to query a column into header and sum it.
I saw some example using crosstab but i can't figure out how to make it work without rowid
Is there other workaround to make this works?
My Table
currency| amount
RMB | 12
IDR | 30
RMB | 22
USD | 58
IDR | 30
Expected query
RMB_sum | IDR_sum | USD_sum
34 | 60 | 58

As you've stated you know in advance all the values in currency at the time of writing this query, you can simply use conditional aggregates. As you're on PostgreSQL 9.1 you can only accomplish this by mixing sum() with a case statement:
select
sum(case when currency = 'RMB' then amount else 0 end) as RMB_sum,
sum(case when currency = 'IDR' then amount else 0 end) as IDR_sum,
sum(case when currency = 'USD' then amount else 0 end) as USD_sum
from
__transactions
(Note the above uses implicit grouping - everything in my select statement is an aggregate function so there is no need to explicitly group the query)
If you were using PostgreSQL 9.4+ you could simplify the above with the filter directive:
select
sum(amount) filter(where currency = 'RMB') as RMB_sum,
sum(amount) filter(where currency = 'IDR') as IDR_sum,
sum(amount) filter(where currency = 'USD') as USD_sum
from
__transactions

Related

How to subtract a seperate count from one grouping

I have a postgres query like this
select application.status as status, count(*) as "current_month" from application
where to_char(application.created, 'mon') = to_char('now'::timestamp - '1 month'::interval, 'mon')
and date_part('year',application.created) = date_part('year', CURRENT_DATE)
and application.job_status != 'expired'
group by application.status
it returns the table below that has the number of applications grouped by status for the current month. However I want to subtract a total count of a seperate but related query from the internal review number only. I want to count the number of rows with type = abc within the same table and for the same date range and then subtract that amount from the internal review number (Type is a seperate field). Current_month_desired is how it should look.
status
current_month
current_month_desired
fail
22
22
internal_review
95
22
pass
146
146
UNTESTED: but maybe...
The intent here is to use an analytic and case expression to conditionally sum. This way, the subtraction is not needed in the first place as you are only "counting" the values needed.
SELECT application.status as status
, sum(case when type = 'abc'
and application.status ='internal_review' then 0
else 1 end) over (partition by application.status)) as
"current_month"
FROM application
WHERE to_char(application.created, 'mon') = to_char('now'::timestamp - '1 month'::interval, 'mon')
and date_part('year',application.created) = date_part('year', CURRENT_DATE)
and application.job_status != 'expired'
GROUP BY application.status

How to create a pivot table in Postgresql using case when?

I want to create a pivot table using postgresql. I could accomplish this using SQLite, and I thought the logic would be similar, but it doesn't seem to be the case.
Here's the sample table:
create table df(
campaign varchar(50),
date date not null,
revenue integer not null
);
insert into df(campaign,date,revenue) values('A','2019-01-01',10000);
insert into df(campaign,date,revenue) values('B','2019-01-02',7000);
insert into df(campaign,date,revenue) values('A','2018-01-01',5000);
insert into df(campaign,date,revenue) values('B','2018-01-01',3500);
here's my sqlite code to transform the tidy data into pivot table:
select
sum(case when strftime('%Y', date) = '2019' then revenue else 0 end) as '2019',
sum(case when strftime('%Y', date) = '2018' then revenue else 0 end) as '2018',
campaign
from df
group by campaign
the result would be like this:
2018 2019 campaign
5000 10000 A
3500 7000 B
I tried making the similar code using postgres, I will just use the year 2019:
select
sum(case when extract('year' from date) = '2019' then revenue else 0 end) as '2019',
campaign
from df
group by campaign
somehow the code doesn't work, I don't understand what's wrong.
Query Error: error: syntax error at or near "'2019'"
what do I miss here?
db-fiddle link:
https://www.db-fiddle.com/f/f1WjMAAxwSPRvB8BrxECN7/0
The function strftime() is used to extract various parts of a date in SQLite, but is not supported by Postgresql.
Use date_part():
select campaign,
sum(case when date_part('year', date) = '2019' then revenue else 0 end) as "2019",
sum(case when date_part('year', date) = '2018' then revenue else 0 end) as "2018"
from df
group by campaign
Or use Postgresql's FILTER clause:
select campaign,
sum(revenue) filter (where date_part('year', date) = '2019') as "2019",
sum(revenue) filter (where date_part('year', date) = '2018') as "2018"
from df
group by campaign
Also, don't use single quotes for table/column names.
SQLite allows it but Postgresql does not.
It accepts only double quotes which is the SQL standard.
See the demo.

Postgres aggregate sum conditional on row comparison

So, I have data that looks something like this
User_Object | filesize | created_date | deleted_date
row 1 | 40 | May 10 | Aug 20
row 2 | 10 | June 3 | Null
row 3 | 20 | Nov 8 | Null
I'm building statistics to record user data usage to graph based on time based datapoints. However, I'm having difficulty developing a query to take the sum for each row of all queries before it, but only for the rows that existed at the time of that row's creation. Before taking this step to incorporate deleted values, I had a simple naive query like this:
SELECT User_Object.id, User_Object.created, SUM(filesize) OVER (ORDER BY User_Object.created) AS sum_data_used
FROM User_Object
JOIN user ON User_Object.user_id = user.id
WHERE user.id = $1
However, I want to alter this somehow so that there's a conditional for the the window function to only get the sum of any row created before this User Object when that row doesn't have a deleted date also before this User Object.
This incorrect syntax illustrates what I want to do:
SELECT User_Object.id, User_Object.created,
SUM(CASE WHEN NOT window_function_row.deleted
OR window_function_row.deleted > User_Object.created
THEN filesize ELSE 0)
OVER (ORDER BY User_Object.created) AS sum_data_used
FROM User_Object
JOIN user ON User_Object.user_id = user.id
WHERE user.id = $1
When this function runs on the data that I have, it should output something like
id | created | sum_data_used|
1 | May 10 | 40
2 | June 3 | 50
3 | Nov 8 | 30
Something along these lines may work for you:
SELECT a.user_id
,MIN(a.created_date) AS created_date
,SUM(b.filesize) AS sum_data_used
FROM user_object a
JOIN user_object b ON (b.user_id <= a.user_id
AND COALESCE(b.deleted_date, a.created_date) >= a.created_date)
GROUP BY a.user_id
ORDER BY a.user_id
For each row, self-join, match id lower or equal, and with date overlap. It will be expensive because each row needs to look through the entire table to calculate the files size result. There is no cumulative operation taking place here. But I'm not sure there is a way that.
Example table definition:
create table user_object(user_id int, filesize int, created_date date, deleted_date date);
Data:
1;40;2016-05-10;2016-08-29
2;10;2016-06-03;<NULL>
3;20;2016-11-08;<NULL>
Result:
1;2016-05-10;40
2;2016-06-03;50
3;2016-11-08;30

Default value in select query for null values in postgres

I have a table with sales Id, product code and amount. Some places product code is null. I want to show Missing instead of null. Below is my table.
salesId prodTypeCode amount
1 123 150
2 123 200
3 234 3000
4 234 400
5 234 500
6 123 200
7 111 40
8 111 500
9 1000
10 123 100
I want to display the total amount for every prodTypeCode with the option of If the prodTypeCode is null then Missing should be displayed.
select (CASE WHEN prodTypeCode IS NULL THEN
'Missing'
ELSE
prodTypeCode
END) as ProductCode, SUM(amount) From sales group by prodTypeCode
Above query giving error. Please suggest me to overcome this issue. I ahve created a SQLFIDDLE
The problem is a mismatch of datatypes; 'Missing' is text, but the product type code is numeric.
Cast the product type code to text so the two values are compatible:
select (CASE WHEN prodTypeCode IS NULL THEN
'Missing'
ELSE
prodTypeCode::varchar(40)
END) as ProductCode, SUM(amount) From sales group by prodTypeCode
See SQLFiddle.
Or, simpler:
select coalesce(prodTypeCode::varchar(40), 'Missing') ProductCode, SUM(amount)
from sales
group by prodTypeCode
See SQLFiddle.
Perhaps you have a type mismatch:
select coalesce(cast(prodTypeCode as varchar(255)), 'Missing') as ProductCode,
SUM(amount)
From sales s
group by prodTypeCode;
I prefer coalesce() to the case, simply because it is shorter.
I tried all 2 answers in my case and both did not work. I hope this snippet can help if both do not work for someone else:
SELECT
COALESCE(NULLIF(prodTypeCode,''), 'Missing') AS ProductCode,
SUM(amount)
From sales s
group by prodTypeCode;

Obtaining a date-bound running total on postgresql

I have a database query running on Postgresql 9.3 that looks like this in order to obtain a running balance of accounting entries:
select *,(sum(amount) over(partition
by
ae.account_id
order by
ae.date_posted,
ae.account_id
)) as formula0_1_
from
account_entry as ae
-- where ae.date_posted> '2014-01-01'
order by account_id desc, date_posted asc
expected output without the where clause would be:
id | date | amount | running balance
1 2014-01-01 10 10
2 2014-01-02 10 20
what I'm getting with the where clause:
id | date | amount | running balance
2 2014-01-02 10 10
How can I make this this query return me the same correct results if I try filtering by a date range (the bit commented above)?
You need to select and calculate your running balances first over all the data, and then put a WHERE clause in an outer SELECT.
SELECT
*
FROM
(SELECT
*,
SUM(amount) OVER (
PARTITION BY
ae.account_id
ORDER BY
ae.date_posted,
ae.account_id
) AS formula0_1_
FROM
account_entry AS ae) AS total
WHERE
total.date_posted > '2014-01-01'
ORDER BY
account_id DESC,
date_posted ASC;