How to change row value into column header - sql

select
extract(year from datetimestamp ) Yr,extract(month from datetimestamp) Mn,
c.weekday_of_month wk, a.aircraft_type,count( a.aircraft_type) from fcm_bv.Flights b
join fcm_bv.Fleet a on b.aircraftid=a.tail
join SYS_CALENDAR.CALENDAR c
on cast(b.datetimestamp AS DATE FORMAT 'YYYY-MM-DD') = cast(c.calendar_date AS DATE FORMAT 'YYYY-MM-DD')
where cast(datetimestamp as date) >= '2011-09-01'
and cast(datetimestamp as date) <= '2011-09-30' order by wk
group by Yr,Mn,wk,a.fleet,a.aircraft_type
While Running above Query I am getting out put like this
Yr Mn wk AIRCRAFT_TYPE Count(AIRCRAFT_TYPE)
2011 9 1 B737-700 1744
2011 9 1 B737-800 131
2011 9 1 B737-800W 2711
2011 9 1 B737-8BK 180
2011 9 1 B737-700W 329
But I need output in below format
Yr Mn wk B737-700 B737-800 B737-800W B737-8BK B737-700W
2011 9 1 1744 131 2711 180 329
Could any one help me

In the past when I have needed to do this the pivot was against a discrete, managable volume of categories and the following SQL has served me well:
SELECT EXTRACT(YEAR FROM b.datetimestamp) AS Yr
, EXTRACT(MONTH FROM b.datetimestamp) AS Mn
, C.weekday_of_month
, COUNT(CASE WHEN a.aircraft_type = 'B737-700' THEN a.aircraft_type ELSE NULL END) AS B737-700
, COUNT(CASE WHEN a.aircraft_type = 'B737-800' THEN a.aircraft_type ELSE NULL END) AS B737-800
, /* Other Known Aircrafts */
, COUNT(CASE WHEN a.aircrat_type NOT IN ('<list of known aircraft types>') THEN a.aircraft_type ELSE NULL END) AS Uncategorized_Aircraft
FROM fcm_bv.Flights b
join fcm_bv.Fleet a on b.aircraftid=a.tail
join SYS_CALENDAR.CALENDAR c
on cast(b.datetimestamp AS DATE FORMAT 'YYYY-MM-DD') = cast(c.calendar_date AS DATE FORMAT 'YYYY-MM-DD')
WHERE cast(datetimestamp as date) >= '2011-09-01'
AND cast(datetimestamp as date) <= '2011-09-30' order by wk
GROUP BY Yr,Mn,wk,a.fleet
If you have to pivot against a constantly changing category it may be best to leave the pivoting to MS Excel or BI tool of choice.
Hope this helps.

Related

How to extract random dates targets/sales data from monthly targets

I have monthly targets defined for the different category of items for the complete year.
Example:
January Target for A Category - 15,000
January Target for R Category - 10,000
January Target for O Category - 5,000
Actual Sales for A Category January - 18,400
Actual Sales for R Category January - 8,500
Actual Sales for O Category January - 3,821
The SQL query to compare actual sales with target will be simple as follows:
SELECT TO_CHAR (Sales_Date, 'MM') Sales_Month,
Sales_Category,
SUM (Sales_Value) Sales_Val_Monthly,
Target_Month,
Target_Category,
Target_Value
FROM Sales_Data, Target_Data
WHERE TO_CHAR (Sales_Date, 'MM') = Target_Month
AND Sales_Category = Target_Category
GROUP BY TO_CHAR (Sales_Date, 'MM'),
Target_Month,
Target_Category,
Sales_Category,
Target_Value;
Now I have a requirement that user will input FROM_DATE and TILL_DATE in the report parameter and the starting/ending date can be random, it will not represent a complete month or week, the start date can be 12/01/2018 and end date can be 15/01/2018, i.e., data for 4 days. The result should calculate the actual data for 4 days, calculate the target for 4 days considering the fact that there will be 6 working days (Sunday is a holiday) and if the date range includes Sunday, it should not be considered.
Also, the number of days in a month should be considered and the date parameters may contain some days from one month and some days from another month or maybe more than one month.
Target_Table (Target_Data)
Target_Year Target_Month Target_Category Target_Value
2018 01 A 15000
2018 02 A 8500
2018 03 A 9500
2018 01 R 15000
2018 02 R 8500
2018 03 R 9500
2018 01 O 15000
2018 02 O 8500
2018 03 O 9500
Sales Table (Sales_Data)
Inv_Txn Inv_No Sales_Date Item_Code Sales_Category Qty Rate Sales_Value Inv_Locn Inv_SM_ID
A21 2018000001 02/01/2018 XXXX A 2 5.5 11 O001 XXXX
R32 2018000001 27/02/2018 XXXX R 3 9.5 28.5 O305 XXXX
O98 2018000001 12/03/2018 XXXX O 12 12.5 150 O901 XXXX
U76 2018000001 18/01/2018 XXXX A 98 5.5 539 O801 XXXX
B87 2018000001 19/02/2018 XXXX R 2 9.5 19 O005 XXXX
A21 2018000002 13/03/2018 XXXX R 45 9.5 427.5 O001 XXXX
B87 2018000002 14/03/2018 XXXX O 12 12.5 150 O005 XXXX
Desired Output (From Date: 27/02/2018 Till Date: 06/03/2018)
Target_Category Target_Value Sales_Value
A 87.52 21.88
A 96.25 24.06
A 74.25 18.56
R 100.25 25.06
R 800.2 200.05
R 25.1 6.28
O 75.5 18.88
O 98.1 24.53
O 25.5 6.38
The first step might be to see whether we can get the number of Sundays in a given month. As it turns out, we can - and we don't have to use any SQL tricks or PL/SQL:
SELECT EXTRACT( DAY FROM LAST_DAY(SYSDATE) ) AS month_day_cnt
, CEIL( ( LAST_DAY(TRUNC(SYSDATE, 'MONTH')) - NEXT_DAY(TRUNC(SYSDATE, 'MONTH')-1, 'SUN') + 1 ) / 7 ) AS sunday_cnt
FROM dual;
This will give us the number of days in a given month as well as the number of Sundays. All we need to do is subtract the latter number from the former to get the number of working days. We can work that into your initial query (by the way, I suggest using TRUNC() instead of TO_CHAR() since your users might want a date range that spans more than one calendar year):
SELECT TRUNC(s.Sales_Date, 'MONTH') AS Sales_Month
, EXTRACT( DAY FROM LAST_DAY( TRUNC(s.Sales_Date, 'MONTH') ) ) - CEIL( ( LAST_DAY(TRUNC(s.Sales_Date, 'MONTH')) - NEXT_DAY(TRUNC(s.Sales_Date, 'MONTH')-1, 'SUN') + 1 ) / 7 ) AS working_day_cnt
, s.Sales_Category, SUM(s.Sales_Value) AS Sales_Val_Monthly
, t.Target_Value -- Target_Month and Target_Category are superfluous
FROM Sales_Data s INNER JOIN Target_Data t
ON TO_CHAR(s.Sales_Date, 'MM') = t.Target_Month
AND TO_CHAR(s.Sales_Date, 'YYYY') = t.Target_Year
AND s.Sales_Category = t.Target_Category
GROUP BY TRUNC(s.Sales_Date, 'MONTH'), Sales_Category, Target_Value;
Now given a start date and an end date, we can generate the number of working days for all the months in between those dates as follows:
SELECT TRUNC(range_dt, 'MONTH'), COUNT(*) FROM (
SELECT start_dt + LEVEL - 1 AS range_dt
FROM dual
CONNECT BY start_dt + LEVEL - 1 < end_dt
) WHERE TO_CHAR(range_dt, 'DY') != 'SUN'
GROUP BY TRUNC(range_dt, 'MONTH');
where start_dt and end_dt are parameters supplied by the user. Putting this all together, we'll have something like the following:
WITH rd ( range_month, range_day_cnt ) AS (
SELECT TRUNC(range_dt, 'MONTH'), COUNT(*) FROM (
SELECT start_dt + LEVEL - 1 AS range_dt
FROM dual
CONNECT BY start_dt + LEVEL - 1 < end_dt
) WHERE TO_CHAR(range_dt, 'DY') != 'SUN'
GROUP BY TRUNC(range_dt, 'MONTH')
)
SELECT range_month, Sales_Category, Sales_Val_Monthly
, range_day_cnt, working_day_cnt, Target_Value
, Target_Value*range_day_cnt/working_day_cnt AS prorated_target_value
FROM (
SELECT r.range_month, r.range_day_cnt
, EXTRACT( DAY FROM LAST_DAY( TRUNC(s.Sales_Date, 'MONTH') ) ) - CEIL( ( LAST_DAY(TRUNC(s.Sales_Date, 'MONTH')) - NEXT_DAY(TRUNC(s.Sales_Date, 'MONTH')-1, 'SUN') + 1 ) / 7 ) AS working_day_cnt
, s.Sales_Category, SUM(s.Sales_Value) AS Sales_Val_Monthly
, t.Target_Value -- Target_Month and Target_Category are superfluous
FROM rd INNER JOIN Sales_Data s
ON rd.range_month = TRUNC(s.Sales_Date, 'MONTH')
INNER JOIN Target_Data t
ON TO_CHAR(s.Sales_Date, 'MM') = t.Target_Month
AND TO_CHAR(s.Sales_Date, 'YYYY') = t.Target_Year
AND s.Sales_Category = t.Target_Category
WHERE s.Sales_Date >= TRUNC(start_dt)
AND s.Sales_Date < TRUNC(end_dt+1)
GROUP BY r.range_month, r.range_day_cnt, s.Sales_Category, t.Target_Value
) ORDER BY range_month;
If you have a table of public holidays, then those will have to be factored in somewhere as well - both in the rd common table expression and from the calculation of working days. If the above doesn't give you a start on that then I can take a look again in a bit and see how the other holidays might be worked in.
You can calculate the number of working days between two dates using below query. I added a nonworking date via a table named: holiday_dates and created a series of dates from 12/01/2018 to 15/01. I remove those dates that are either Sunday or holiday. Please let me know if it works for you. Thanks.
create table holiday_dates(holiday_dte date, holiday_desc varchar(100));
insert into holiday_dates values(TO_DATE('13/01/2018','DD-MM-YYYY'), 'Not a Working Day');
With tmp as (
select count(*) as num_of_working_days
from ( select rownum as rn
from all_objects
where rownum <= to_date('15/01/2018','DD-MM-YYYY') - to_date('12/01/2018','DD-MM-YYYY')+1 )
where to_char( to_date('12/01/2018','DD-MM-YYYY')+rn-1, 'DY' ) not in ( 'SUN' )
and not exists ( select null from holiday_dates where holiday_dte = trunc(to_date('12/01/2018','DD-MM-YYYY') + rn - 1)))
SELECT TO_CHAR (Sales_Date, 'MM') Sales_Month,
Sales_Category,
SUM (Sales_Value) Sales_Val_Monthly,
Target_Month,
Target_Category,
Target_Value,
tmp.num_of_working_days
FROM Sales_Data, Target_Data, tmp
WHERE Sales_Date between to_date('12/01/2018','DD-MM-YYYY') and to_date('15/01/2018','DD-MM-YYYY')
AND Sales_Category = Target_Category
GROUP BY TO_CHAR (Sales_Date, 'MM'),
Target_Month,
Target_Category,
Sales_Category,
Target_Value;

db2 compare year and month side by side

I need to compare side by side the companies values by current year vs last year and current month with same month of the previous year.
I use this query to get the values
SELECT STORE, SUM(TOTAL) as VAL, DATE FROM MYTABLE
WHERE DATE=CURRENT_DATE GROUP BY STORE ORDER BY STORE
below the results
STORE | VAL | DATE
1 10 CURRENT_DATE (2018-27-03)
1 20 2018-26-03
1 30 2018-25-03
2 20 CURRENT_DATE (2018-27-03)
2 20 2018-26-02
and i need this
STORE | VALUE CURRENT YEAR | VALUE LAST YEAR
1 60 30 (CALCULATED)
2 40 50 (CALCULATED)
STORE | VALUE CURRENT MONTH | VALUE SAME MONTH OF LAST YEAR
1 60 30 (CALCULATED)
2 20 50 (CALCULATED)
Thank you
You could just join two sub-selects together.
E.g with this DDL and Data
CREATE TABLE MYTABLE (STORE int, VAL int, D DATE);
INSERT INTO MYTABLE VALUES
( 1, 10, '2018-03-27')
,( 1, 20, '2018-03-26')
,( 1, 10, '2018-02-25')
,( 1, 35, '2017-03-25')
,( 2, 20, '2018-03-27')
,( 2, 15, '2017-03-26');
This will get you current month and last month last year values
SELECT C.*, LY.VAL_CURR_MONTH_LY
FROM (
SELECT STORE, SUM(VAL) as VAL_CURR_MONTH
FROM MYTABLE WHERE INT(D)/100=INT(CURRENT_DATE)/100
GROUP BY STORE ) AS C
LEFT JOIN
(SELECT STORE
, SUM(VAL) AS VAL_CURR_MONTH_LY
FROM MYTABLE
WHERE INT(D)/100 = INT(CURRENT_DATE)/100 -100
GROUP BY STORE ) LY
ON
C.STORE = LY.STORE
Then this for years
SELECT C.*, LY.VAL_LY
FROM (
SELECT STORE, SUM(VAL) as VAL_CURR_YEAR
FROM MYTABLE WHERE INT(D)/10000=INT(CURRENT_DATE)/10000
GROUP BY STORE ) AS C
LEFT JOIN
(SELECT STORE
, SUM(VAL) AS VAL_LY
FROM MYTABLE
WHERE INT(D)/10000 = INT(CURRENT_DATE)/10000 -1
GROUP BY STORE ) LY
ON
C.STORE = LY.STORE
P.S. there are many other ways to manipulate dates, but casting to INT is maybe one of the easier ways
Also, here is a more flexible way to get the "Same Month of Last Year" value. A similar method can get "last Year" values.
SELECT T.*
, AVG(VAL) OVER(
PARTITION BY STORE
ORDER BY YEAR_MONTH
RANGE BETWEEN 101 PRECEDING AND 100 PRECEDING
) AS SAME_MONTH_PREV_YEAR
FROM
( SELECT STORE
, INTEGER(D)/100 AS YEAR_MONTH
, SUM(VAL) AS VAL
FROM
MYTABLE T
GROUP BY
STORE
, INTEGER(D)/100
) AS T
;
Gives
STORE YEAR_MONTH VAL SAME_MONTH_PREV_YEAR
----- ---------- --- --------------------
1 201703 35 NULL
1 201802 10 NULL
1 201803 30 35
2 201703 15 NULL
2 201803 20 15
It is better to avoid functions on table columns in where clauses. Check following SQLs which are based on P. Vernon sample table.
Note: These SQLs are for DB2 LUW 11.1
For month:
SELECT STORE,
SUM(CASE WHEN YEAR(D) = year(current date) THEN val
ELSE 0 END) as VAL_CURR_MONTH,
SUM(CASE WHEN YEAR(D) = year(current date) - 1 THEN vaL
ELSE 0 END) as VAL_CURR_MONTH_LY
FROM MYTABLE
WHERE D between first_day(current date) and last_day(current date)
or D between first_day(current date - 1 year) and last_day(current date - 1 year)
GROUP BY STORE
ORDER BY STORE
For year:
SELECT STORE, SUM(CASE WHEN YEAR(D) = year(current date) THEN val
ELSE 0 END) as VAL_CY,
SUM(CASE WHEN YEAR(D) = year(current date) - 1 THEN vaL
ELSE 0 END) as VAL_LY
FROM MYTABLE
WHERE D between first_day(current date - (month(current date) - 1) months)
and last_day(current date + (12 - month(current date)) months)
or D between first_day(current date - (month(current date) - 1) months - 1 year)
and last_day(current date + (12 - month(current date)) months - 1 year)
GROUP BY STORE
ORDER BY STORE

Teradata - Split date range into month columns with day count

I need to split different date ranges over a quarter period into month columns with only the days actually used in that month. Each record (range) would be different.
Example:
Table
Record_ID Start_Date End_Date
1 10/27 11/30
2 11/30 12/14
3 12/14 12/31
Range 1 = 10/5 to 12/14
Range 2 = 11/20 to 12/31
Range 3 = 10/28 to 12/2
Output:
Range 1
Oct Nov Dec
27 30 14
Similar to #ULick's answer using sys_calendar.calendar, but a little more succinct:
CREATE VOLATILE MULTISET TABLE datetest (record_id int, start_date date, end_date date) ON COMMIT PRESERVE ROWS;
INSERT INTO datetest VALUES (1, '2017-10-05', '2017-12-14');
INSERT INTO datetest VALUES (2, '2017-11-20','2017-12-31');
SELECT record_id,
SUM(CASE WHEN month_of_year = 10 THEN 1 ELSE 0 END) as October,
SUM(CASE WHEN month_of_year = 11 THEN 1 ELSE 0 END) as November,
SUM(CASE WHEN month_of_year = 12 THEN 1 ELSE 0 END) as December
FROM datetest
INNER JOIN sys_calendar.calendar cal
ON cal.calendar_date BETWEEN start_date and end_date
GROUP BY record_id;
DROP TABLE datetest;
Because Quarter was mentioned in the question (I'm not sure how it relates here) there is also quarter_of_year and month_of_quarter available in the sys_calendar to slice and dice this even further.
Also, if you are on 16.00+ There is PIVOT functionality which may help get rid of the CASE statements here.
First join with the calendar to get all the dates within the range and get the number of days per each month (incl. full month, not mentioned in Start_Date and End_Date).
Then sum up each month in a column per Range.
create table SplitDateRange ( Range bigint, Start_Date date, End_Date date );
insert into SplitDateRange values ( 1, '2018-10-05', '2018-12-14' );
insert into SplitDateRange values ( 2, '2018-11-20', '2018-12-31' );
insert into SplitDateRange values ( 3, '2018-10-28', '2018-12-02' );
select
Range
, sum(case when mon = 10 then days else 0 end) as "Oct"
, sum(case when mon = 11 then days else 0 end) as "Nov"
, sum(case when mon = 12 then days else 0 end) as "Dec"
from (
select
Range
, extract(MONTH from C.calendar_date) as mon
, max(C.calendar_date) - min(calendar_date) +1 as days
from Sys_Calendar.CALENDAR as C
inner join SplitDateRange as DR
on C.calendar_date between DR.Start_Date and DR.End_Date
group by 1,2
) A
group by Range
order by Range
;
Different approach, avoids the cross join to the calendar by applying Teradata Expand On feature for creating time series. More text, but should be more efficient for larger tables/ranges:
SELECT record_id,
Sum(CASE WHEN mth = 10 THEN days_in_month ELSE 0 END) AS October,
Sum(CASE WHEN mth = 11 THEN days_in_month ELSE 0 END) AS November,
Sum(CASE WHEN mth = 12 THEN days_in_month ELSE 0 END) AS December
FROM
( -- this Derived Table simply avoids repeating then EXTRACT/INTERVAL calculations (can't be done directly in the nested Select)
SELECT record_id,
Extract(MONTH From Begin(expanded_pd)) AS mth,
Cast((INTERVAL( base_pd P_INTERSECT expanded_pd) DAY) AS INT) AS days_in_month
FROM
(
SELECT record_id,
PERIOD(start_date, end_date+1) AS base_pd,
expanded_pd
FROM datetest
-- creates one row per month
EXPAND ON base_pd AS expanded_pd BY ANCHOR PERIOD Month_Begin
) AS dt
) AS dt
GROUP BY 1

Sum Based on Date

I currently have this code that I want to sum every quantity based on the year. I have written a code that I thought would sum all the charges in 2016 and 2017, but it isn't running correctly.
I added the two different types of partition by statements to test and see if either would work and they don't. When I take them out, the Annual column just shows me the quantity for that specific receipt.
Here is my current code:
SELECT
ReceiptNumber
,Quantity
,Date
,sum(CASE WHEN (Date >= '2016-01-01' and Date < '2017-01-01') THEN
Quantity
ELSE 0 END)
OVER (PARTITION BY Date)
as Annual2016
,sum(CASE WHEN (Date >= '2017-01-01' and Date < '2018-01-01') THEN
Quantity
ELSE 0 END)
OVER (PARTITION BY ReceiptNumber)
as Annual2017
FROM Table1
GROUP BY ReceiptNumber, Quantity, Date
I would like my data to look like this
ReceiptNumber Quantity Date Annual2016 Annual2017
1 5 2016-01-05 17 13
2 11 2017-04-03 17 13
3 12 2016-11-11 17 13
4 2 2017-09-09 17 13
Here is a sample of some of the data I am pulling from:
ReceiptNumber Quantity Date
1 5 2016-01-05
2 11 2017-04-03
3 12 2016-11-11
4 2 2017-09-09
5 96 2015-07-08
6 15 2016-12-12
7 24 2016-04-19
8 31 2017-01-02
9 10 2017-0404
10 18 2015-10-10
11 56 2017-06-02
Try something like this
Select
..
sum(CASE WHEN (Date >= '2016-01-01' and Date < '2017-01-01') THEN
Quantity
ELSE 0 END)
OVER () as Annual2016
sum(CASE WHEN (Date >= '2017-01-01' and Date < '2018-01-01') THEN
Quantity
ELSE 0 END)
OVER ()as Annual2017
..
Where Date >= '2016-01-01' and Date < '2018-01-01'
If you want it printed only once at the top then you should run it in a separate query like:
SELECT YEAR(Date) y, sum(Quantity) s FROM Table1 GROUP BY YEAR(Date)
and then do the main query like this:
SELECT * FROM table1
Easy, peasey ... ;-)
Your original question could also be answered with:
SELECT *,
(SELECT SUM(Quantity) FROM Table1 WHERE YEAR(Date)=2016 ) Annual2016,
(SELECT SUM(Quantity) FROM Table1 WHERE YEAR(Date)=2017 ) Annual2017
FROM table1
You need some conditional aggreation over a Window Aggregate. Simply remove both PARTITION BY as you're already filtering the year in the CASE:
SELECT
ReceiptNumber
,Quantity
,Date
,sum(CASE WHEN (Date >= '2016-01-01' and Date < '2017-01-01') THEN
Quantity
ELSE 0 END)
OVER () as Annual2016
,sum(CASE WHEN (Date >= '2017-01-01' and Date < '2018-01-01') THEN
Quantity
ELSE 0 END)
OVER () as Annual2017
FROM Table1
You probably don't need the final GROUP BY ReceiptNumber, Quantity, Date

SQL Results group by month

I'm trying to return some results spread over a rolling 12 month period eg:
MONTH IN OUT
January 210 191
February 200 111
March 132 141
April 112 141
May 191 188
etc...
How do I spread the results over a date range, populating the first column with the month name?
IN MSSQL it would be something like:
SELECT COUNT(problem.problem_type = 'IN') AS IN,
COUNT(problem.problem_type = 'OUT') AS OUT,
DATEPART(year, DateTime) as Year,
DATEPART(month, DateTime) as Month
FROM problem
WHERE (DateTime >= dbo.FormatDateTime('2010-01-01'))
AND
(DateTime < dbo.FormatDateTime('2010-01-31'))
GROUP BY DATEPART(year, DateTime),
DATEPART(month, DateTime);
But this is against an Oracle database so DATEPART and DateTime are not available.
My Problem table is roughly:
problem_ID Problem_type IN_Date OUT_Date
1 IN 2010-01-23 16:34:29.0 2010-02-29 13:06:28.0
2 IN 2010-01-27 12:34:29.0 2010-01-29 12:01:28.0
3 OUT 2010-02-13 13:24:29.0 2010-09-29 15:04:28.0
4 OUT 2010-02-15 16:31:29.0 2010-07-29 11:03:28.0
Use:
SELECT SUM(CASE WHEN p.problem_type = 'IN' THEN 1 ELSE 0 END) AS IN,
SUM(CASE WHEN p.problem_type = 'OUT' THEN 1 ELSE 0 END) AS OUT,
TO_CHAR(datetime, 'YYYY') AS year,
TO_CHAR(datetime, 'MM') AS month
FROM PROBLEM p
WHERE p.DateTime >= TO_DATE('2010-01-01', 'YYYY-MM-DD')
AND p.DateTime < TO_DATE('2010-01-31', 'YYYY-MM-DD')
GROUP BY TO_CHAR(datetime, 'YYYY'), TO_CHAR(datetime, 'MM')
You could also use:
SELECT SUM(CASE WHEN p.problem_type = 'IN' THEN 1 ELSE 0 END) AS IN,
SUM(CASE WHEN p.problem_type = 'OUT' THEN 1 ELSE 0 END) AS OUT,
TO_CHAR(datetime, 'MM-YYYY') AS mon_year
FROM PROBLEM p
WHERE p.DateTime >= TO_DATE('2010-01-01', 'YYYY-MM-DD')
AND p.DateTime < TO_DATE('2010-01-31', 'YYYY-MM-DD')
GROUP BY TO_CHAR(datetime, 'MM-YYYY')
Reference:
TO_CHAR
TO_DATE
You probably want something like
SELECT SUM( (CASE WHEN problem_type = 'IN' THEN 1 ELSE 0 END) ) in,
SUM( (CASE WHEN problem_type = 'OUT' THEN 1 ELSE 0 END) ) out,
EXTRACT( year FROM DateTime ) year,
EXTRACT( month FROM DateTime ) month
FROM problem
WHERE DateTime >= date '2010-01-01'
AND DateTime < date '2010-01-31'
GROUP BY EXTRACT( year FROM DateTime ),
EXTRACT( month FROM DateTime )