i need to convert rows into columns using postgrsql
need resulSet as follows
monthname 2017 year(amount) 2018 year(amount)
Jan 10 250
feb 20 350
mar 40 100
below is my Query using crosstab function
SELECT *
FROM crosstab(
$$select
SUM(standard_loan_account_balance) as TOTAL_AMOUNT
,extract (year from mgc.transaction_date) as monthname
,extract(MONTH from mgc.transaction_date) as monthnumber
from bankfair.tbl_das_monthly_growth_chart mgc
where mgc.transaction_date
between (select (SELECT APP.app_today_date FROM bankfair.tbl_cmn_application_date app)+'-12 month'::interval)
and (SELECT APP.app_today_date FROM bankfair.tbl_cmn_application_date app) group by monthnumber,monthname
order by 1,2
$$
) as ct ("TOTAL_AMOUNT" numeric,"monthnumber" double precision,"monthname" double precision)
i didnt get expected output
I would start with a simple non-crosstab version of the query. This should do what you want:
select to_char(mgc.transaction_date, 'Month') as monthname,
sum(case when to_char(mgc.transaction_date, 'YYYY') = '2017'
then standard_loan_account_balance else 0
end) as slab_2017,
sum(case when to_char(mgc.transaction_date, 'YYYY') = '2018'
then standard_loan_account_balance else 0
end) as slab_2018
from bankfair.tbl_das_monthly_growth_chart mgc
group by monthname, extract(month from mgc.transaction_date)
order by monh(mgc.transaction_date);
You can then convert this to a crosstab version, if you need to.
In your desired resultset, the columns are
monthname 2017 year(amount) 2018 year(amount)
But in the AS clause that qualifies the result of the crosstab() call, you have put this, which seems to correspond to your non-pivoted columns:
as ct ("TOTAL_AMOUNT" numeric,"monthnumber" double precision,"monthname" double precision)
That's wrong, because the AS clause should designate exactly the pivoted-output columns. It seems that you did not understand this aspect of crosstab(), as you put the columns of the non-pivoted output.
The columns in the query passed as the first argument of crosstab also don't match. The first column needs to be the month's name, the second column the year, and the third column the amount. Finally, you need to limit the years to the ones that are hardcoded in your AS clause.
Related
I need to create 12 months report, which counts values per months. I have made a separate temp table using WITH for each months which counts parts for each aircraft. It takes data from the PARTS table. My table for January looks like this:
type
qty
month
Airbus
248
1
Boeing
120
1
Emb
14
1
Then I count amount of aicrafts each type per months using AC table, here's table for January:
type
qty
month
Airbus
23
1
Boeing
10
1
Emb
5
1
Since I need to find a division of Qty to Count, I implement division Qty / count. So I joined table 1 and table 2 using month column. And combined table for January looks like this:
type
qty
count
div
month
Airbus
248
23
10.7
1
Boeing
120
10
12
1
Emb
14
5
2.8
1
I create temp table for each month and the combine them with UNION ALL. But I am afraid it could lead to DB slowdown. I think I need to rewrite and optimize my script. Any ideas how I could implement that?
Also the data in tables is dynamic and can change. So I need to look only for the last 12 months.
In my script I will have to manually add more months, which is not applicaple.
Is there a way that could possibly solve the problem of optimization and take into account only last 12 months?
Rather than having a sub-query factoring (WITH) clause for each month and trying to use UNION ALL to join them, you can include the month in the GROUP BY clause when you are counting the quantities in the ac and part table.
Since you only provided the intermediate output from the WITH clauses, I've had to try to reverse engineer your original tables to give a query something like this:
SELECT COALESCE(ac.type, p.type) AS type,
COALESCE(ac.month, p.month) AS month,
COALESCE(ac.qty, 0) AS qty,
COALESCE(p.qty, 0) AS count,
CASE p.qty WHEN 0 THEN NULL ELSE ac.qty / p.qty END AS div
FROM (
SELECT type,
TRUNC(date_column, 'MM') AS month,
COUNT(*) AS qty
FROM ac
WHERE date_column >= ADD_MONTHS(SYSDATE, -12)
GROUP BY
type,
TRUNC(date_column, 'MM')
) ac
FULL OUTER JOIN
(
SELECT type,
TRUNC(date_column, 'MM') AS month,
COUNT(*) AS qty
FROM parts
WHERE date_column >= ADD_MONTHS(SYSDATE, -12)
GROUP BY
type,
TRUNC(date_column, 'MM')
) p
ON (
ac.type = p.type
AND ac.month = p.month
)
My table is like
summa_som date_operation
----- --------------
100 11/03/2005
500 13/07/2008
900 12/11/2015
Expected result
I want to calculate income for each month there should 3 columns:
income, month and year:
Income Month Year
------ -------- ----
10000 February 2015
15000 December 2015
I have tried this but, I do not understand well how sub-queries work. This code should give an idea of what I want:
select max(summa_som * 0.01) income
from t_operation
where to_char(date_operation,'MM') = 11
and to_char(date_operation,'YYYY') = 2015
( select max(summa_som * 0.01) income_for_dec2015 from t_operation
where to_char(date_operation,'MM') = 12
and to_char(date_operation,'YYYY') = 2015 )
You can use filter and aggregation to achieve this:
select to_char(date_operation, 'Month') as mn,
to_char(date_operation, 'YYYY') as yr,
max(summa_som) * 0.01 as income
from t_operation
where date_operation >= date '2015-11-01'
and date_operation < date '2016-01-01'
group by to_char(date_operation, 'Month'),
to_char(date_operation, 'YYYY')
If you want result of all the months and years for which the data is present, you can remove the filter condition.
You have some specific formatting concerns with date and something going on with the income by multiplying it by 0.01. When you have formatting concerns like this, I prefer to do the aggregation in an inner query based on the data types involved, keeping numbers as numbers and dates, even it modified for grouping as dates. Then, wrap the statement with an outer query that handles type conversions or formatting.
select (income*0.01) as income_value,
to_char(income_mn,'Month') as income_month,
to_char(income_mn,'YYYY') as income_year
from (
select trunc(date_operation,'MON') as income_mn,
max(summa_som) as income
from t_operation
where date_operation between to_date('11/2015','MM/YYYY')
and to_date('01/2016','MM/YYYY') - numtodsinterval(1, 'SECOND')
group by trunc(date_operation,'MON')
);
note: using between makes the both the upper and lower bound values inclusive. I've subtracted one second to the upper bound to only include the December values, but this is not really necessary unless you only want 2015 values.
My Goal is to load a monthly-daily tabular presentation of sales data with sum total and other average computation at the bottom,
I have one data result set with one column that is named as 'Day' which corresponds to the days of the month, with automatic datatype of int.
select datepart(day, a.date ) as 'Day'
On my second result set, is the loading of the sum at the bottom, it happens that the word 'Sum' is aligned to the column of Day, and I used Union All TO COMBINE the result set together, expected result set is something to this like
day sales
1 10
2 20
3 30
4 10
5 20
6 30
.
.
.
31 10
Sum 130
What I did is to convert the day value, originally in int to varchar datatype. this is to successfully join columns and it did, the new conflict is the sorting of the number
select * from #SalesDetailed
UNION ALL
select * from #SalesSum
order by location, day
Assuming your union query returns the correct results, just messes up the order, you can use case with isnumeric in the order by clause to manipulate your sort:
SELECT *
FROM
(
SELECT *
FROM #SalesDetailed
UNION ALL
SELECT *
FROM #SalesSum
) u
ORDER BY location,
ISNUMERIC(day) DESC,
CASE WHEN ISNUMERIC(day) = 1 THEN cast(day as int) end
The isnumeric will return 1 when day is a number and 0 when it's not.
Try this
select Day, Sum(Col) as Sales
from #SalesDetailed
Group by Day With Rollup
Edit (Working Sample) :
select
CASE WHEN Day IS NULL THEN 'SUM' ELSE STR(Day) END as Days,
Sum(Sales) from
(
Select 1 as Day , 10 as Sales UNION ALL
Select 2 as Day , 20 as Sales
) A
Group by Day With Rollup
EDIT 2:
select CASE WHEN Day IS NULL THEN 'SUM' ELSE STR(Day) END as Days,
Sum(Sales) as Sales
from #SalesDetailed
Group by Day With Rollup
I'm new in SQL. I was practicing and came on this. How would I retrieve the total number of sales during the first, second, third and fourth quarter separately. I know I'm not providing anything to work from, i would just like to understand the logic and the functions that can be used to do it.
You would use SUM() to give you a total aggregate of the dollar amount of sales, and COUNT(*) for the total count of sales. As far as breaking it down further (such as by quarter), you need to provide more information as far as how your data is structured.
You're not specifying the RDBMS you're using, nor any table structure, so I can only provide some general advice.
First, you need to get both the year and the quarter for each record in your table. As far as I know, every RDBMS has functions that can help you extract the year and the month of a date. Assuming a column named dt that holds the date, you can do something like this:
select year(dt) as y, month(dt) as month, sales
from your_table
That's just a piece of the solution. You need to get the quarter; again, as far as I know, there are functions that can help you. In MS Access you have Iif(), and in other RDBMS (like MySQL) you may have IF() and/ or CASE ... END, so you can get what you need with something like this:
select year(dt) as y
, (case
when month(dt) <= 3 then 1
when month(dt) <= 6 then 2
when month(dt) <= 9 then 3
else 4
end) as q
, sales
from your_table
(As an exercise, do the same thing using If()).
Finally, you can aggregate the data with GROUP BY and the appropriate aggregate functions:
select year(dt) as y
, (case
when month(dt) <= 3 then 1
when month(dt) <= 6 then 2
when month(dt) <= 9 then 3
else 4
end) as q
, sum(sales) as sum_sales
, count(sales) as count_sales
from your_table
group by y, q
You should be able to use something like this to simplify. The DATEPART() function can be found on Microsoft's website here.
SELECT SUM(Sales) AS Quarter1Sales
FROM YourTable
WHERE DATEPART(qq, DateSold) = 1
Note: you will change the 1 to a 2, then 3, then 4 for each quarter. Also change sales to the correct column name and YourTable to your table name.
I have a Postgres table that I'm trying to analyze based on some date columns.
I'm basically trying to count the number of rows in my table that fulfill this requirement, and then group them by month and year. Instead of my query looking like this:
SELECT * FROM $TABLE WHERE date1::date <= '2012-05-31'
and date2::date > '2012-05-31';
it should be able to display this for the months available in my data so that I don't have to change the months manually every time I add new data, and so I can get everything with one query.
In the case above I'd like it to group the sum of rows which fit the criteria into the year 2012 and month 05. Similarly, if my WHERE clause looked like this:
date1::date <= '2012-06-31' and date2::date > '2012-06-31'
I'd like it to group this sum into the year 2012 and month 06.
This isn't entirely clear to me:
I'd like it to group the sum of rows
I'll interpret it this way: you want to list all rows "per month" matching the criteria:
WITH x AS (
SELECT date_trunc('month', min(date1)) AS start
,date_trunc('month', max(date2)) + interval '1 month' AS stop
FROM tbl
)
SELECT to_char(y.mon, 'YYYY-MM') AS mon, t.*
FROM (
SELECT generate_series(x.start, x.stop, '1 month') AS mon
FROM x
) y
LEFT JOIN tbl t ON t.date1::date <= y.mon
AND t.date2::date > y.mon -- why the explicit cast to date?
ORDER BY y.mon, t.date1, t.date2;
Assuming date2 >= date1.
Compute lower and upper border of time period and truncate to month (adding 1 to upper border to include the last row, too.
Use generate_series() to create the set of months in question
LEFT JOIN rows from your table with the declared criteria and sort by month.
You could also GROUP BY at this stage to calculate aggregates ..
Here is the reasoning. First, create a list of all possible dates. Then get the cumulative number of date1 up to a given date. Then get the cumulative number of date2 after the date and subtract the results. The following query does this using correlated subqueries (not my favorite construct, but handy in this case):
select thedate,
(select count(*) from t where date1::date <= d.thedate) -
(select count(*) from t where date2::date > d.thedate)
from (select distinct thedate
from ((select date1::date as thedate from t) union all
(select date2::date as thedate from t)
) d
) d
This is assuming that date2 occurs after date1. My model is start and stop dates of customers. If this isn't the case, the query might not work.
It sounds like you could benefit from the DATEPART T-SQL method. If I understand you correctly, you could do something like this:
SELECT DATEPART(year, date1) Year, DATEPART(month, date1) Month, SUM(value_col)
FROM $Table
-- WHERE CLAUSE ?
GROUP BY DATEPART(year, date1),
DATEPART(month, date1)