Oracle ORA -932 expected Number got CHAR - oracle

I am running the following query and can't seem to figure out where the error is-
select case when month>=7 then substr(year,3,2)|| '/'||TO_NUMBER( substr(year,3,2))+1
else to_number(substr(year,3,2))-1 || '/'|| substr(year,3,2) end as fiscal_year
FROM ( SELECT DISTINCT to_Char(extract( year from date)) as year,
extract( month from date)as MONTH FROM TABLE )
I want to convert year to fiscal year like 19/20, 20/21 etc

The operator precedence rules means the string concatenation is happening before the addition; this expression:
substr(year,3,2)|| '/'||TO_NUMBER( substr(year,3,2))+1
is evaluated as
substr(year,3,2)|| '/'||TO_NUMBER( substr(year,3,2))
and then it tries to add 1 to that string result. Hence the error you get.
You can add parentheses to make it add 1 to the year number before then concatenating that:
substr(year,3,2)|| '/'|| (TO_NUMBER( substr(year,3,2))+1)
You could also do this without so much string manipulation:
select case when extract (month from your_date) >= 7 then
to_char(your_date, 'YY') || '/' || to_char(add_months(your_date, 12), 'YY')
else
to_char(add_months(your_date, -12), 'YY') || '/' || to_char(your_date, 'YY')
end as fiscal_year
FROM (
SELECT DISTINCT trunc(your_date, 'MM') as your_date
FROM your_table
)
db<>fiddle
and there are other options, of course.

Related

Pivot (Oracle), PL/SQL

select customer_no, account_no, sum(accounted_dr), sum(accounted_cr), to_char(effective_date, 'YYYY-MM') as "Effective Months and Years"
from GL_JOURNAL_LINES
group by account_no, customer_no, to_char(effective_date, 'YYYY-MM')
order by to_char(effective_date, 'YYYY-MM') asc
The Table:
" ";"CUSTOMER_NO";"ACCOUNT_NO";"SUM(ACCOUNTED_DR)";"SUM(ACCOUNTED_CR)";"Effective Months and Years"
"1";"5010011";"19";"141,23";"0";"2021-01"
"2";"5010047";"1";"0";"603";"2021-01"
"3";"5010047";"3";"8436459,22";"10194636,54";"2021-01"
"4";"5010047";"6";"0,95";"0";"2021-01"
"5";"5010047";"17";"3697,88";"3000";"2021-01"
"6";"5010047";"26";"0,95";"0";"2021-01"
"7";"5010047";"31";"2000000";"3439585,07";"2021-01"
"8";"5010047";"155";"0,96";"0,13";"2021-01"
"9";"5010047";"160";"8,36";"0";"2021-01"
"10";"5010047";"165";"2,36";"0";"2021-01"
"11";"5010081";"2";"8287023,64";"8109857,53";"2021-01"
"12";"5010081";"6";"7406954,3";"10900099,1";"2021-01"
"13";"5010081";"9";"0";"135000";"2021-01"
"14";"5010081";"23";"458168";"17000";"2021-01"
"15";"5023913";"7";"3788,03";"5611,5";"2021-01"
"16";"5023913";"24";"4156,4";"4151,4";"2021-01"
"17";"5023913";"26";"3055,4";"993,06";"2021-01"
"18";"5023913";"27";"3055,4";"993,06";"2021-01"
"19";"5023913";"29";"9,93";"30,5";"2021-01"
"20";"5010011";"19";"16";"0";"2021-02"
"21";"5010011";"27";"130500";"0";"2021-02"
"22";"5010047";"1";"0";"25";"2021-02"
"23";"5010047";"2";"116227,01";"6209,1";"2021-02"
"24";"5010047";"3";"7435295,97";"556214,25";"2021-02"
"25";"5010047";"4";"7735,98";"0";"2021-02"
"26";"5010047";"6";"0,95";"0";"2021-02"
"27";"5010047";"17";"3388,76";"0";"2021-02"
"28";"5010047";"26";"169,58";"0";"2021-02"
"29";"5010047";"31";"0";"345670,02";"2021-02"
"30";"5010047";"64";"16800";"0";"2021-02"
"31";"5010047";"154";"1200";"0";"2021-02"
"32";"5010047";"155";"0,22";"1,05";"2021-02"
"33";"5010047";"159";"10000";"0";"2021-02"
"34";"5010047";"160";"0";"8,38";"2021-02"
"35";"5010047";"165";"2,36";"2,33";"2021-02"
"36";"5010081";"2";"4808378,18";"3956792,08";"2021-02"
"37";"5010081";"6";"3817981,11";"0";"2021-02"
"38";"5010081";"9";"0";"135000";"2021-02"
"39";"5010081";"23";"60960";"206380";"2021-02"
"40";"5023913";"7";"5640,14";"5641,5";"2021-02"
"41";"5023913";"24";"5958,4";"5963,4";"2021-02"
"42";"5023913";"26";"2958,4";"2985,37";"2021-02"
"43";"5023913";"27";"2958,4";"2985,37";"2021-02"
"44";"5023913";"29";"29,86";"29,64";"2021-02"
"45";"5010011";"19";"0";"1,74";"2021-03"
"46";"5010011";"21";"1,74";"0";"2021-03"
"47";"5010047";"1";"0";"25";"2021-03"
"48";"5010047";"2";"0";"6599,34";"2021-03"
"49";"5010047";"3";"20656118,36";"8102019,63";"2021-03"
"50";"5010047";"4";"2216630";"0";"2021-03"
"51";"5010047";"5";"1800";"0";"2021-03"
"52";"5010047";"6";"0,95";"0";"2021-03"
"53";"5010047";"17";"3872,46";"6000";"2021-03"
"54";"5010047";"26";"282,27";"0";"2021-03"
"55";"5010047";"31";"4181565,82";"651830,91";"2021-03"
"56";"5010047";"32";"15000";"0";"2021-03"
"57";"5010047";"165";"2,36";"2,33";"2021-03"
"58";"5010081";"2";"6275422,32";"3387645,75";"2021-03"
"59";"5010081";"6";"141666,66";"0";"2021-03"
"60";"5010081";"9";"293282";"0";"2021-03"
"61";"5010081";"22";"141666,66";"0";"2021-03"
"62";"5010081";"23";"465342,03";"511274";"2021-03"
"63";"5023913";"7";"6222,03";"3111,5";"2021-03"
"64";"5023913";"24";"3630,58";"3630,58";"2021-03"
"65";"5023913";"26";"630,58";"2958,4";"2021-03"
"66";"5023913";"27";"630,58";"2958,4";"2021-03"
"67";"5023913";"29";"29,58";"6,31";"2021-03"
"68";"5010047";"1";"0";"25";"2021-04"
"69";"5010047";"2";"0";"6021,6";"2021-04"
"70";"5010047";"3";"2512720,68";"3713671,2";"2021-04"
"71";"5010047";"4";"3890599,59";"494829,12";"2021-04"
"72";"5010047";"6";"0,95";"0";"2021-04"
"73";"5010047";"17";"9150,79";"9000";"2021-04"
"74";"5010047";"23";"285,09";"576,45";"2021-04"
"75";"5010047";"26";"1,9";"0";"2021-04"
"76";"5010047";"31";"200100";"374805,44";"2021-04"
"77";"5010047";"64";"4185";"0";"2021-04"
"78";"5010047";"97";"4185";"0";"2021-04"
"79";"5010047";"164";"2790";"0";"2021-04"
"80";"5010047";"165";"0,86";"3,29";"2021-04"
"81";"5010081";"2";"3264298,78";"3099755,45";"2021-04"
"82";"5010081";"6";"141666,66";"18948,97";"2021-04"
"83";"5010081";"9";"376172";"409481";"2021-04"
"84";"5010081";"23";"319563,97";"115000";"2021-04"
"85";"5023913";"7";"200";"3311,5";"2021-04"
"86";"5023913";"24";"3117,14";"3133,64";"2021-04"
"87";"5023913";"26";"1933,64";"1104,48";"2021-04"
"88";"5023913";"27";"1933,64";"1104,48";"2021-04"
"89";"5023913";"28";"5,01";"5,01";"2021-04"
"90";"5023913";"29";"11,06";"19,35";"2021-04"
"91";"5010047";"1";"0";"25";"2021-05"
"92";"5010047";"2";"207090";"1115589,02";"2021-05"
"93";"5010047";"3";"2510986,53";"1136695,23";"2021-05"
"94";"5010047";"4";"0";"8422,62";"2021-05"
"95";"5010047";"6";"0,95";"0";"2021-05"
"96";"5010047";"8";"200,19";"0";"2021-05"
"97";"5010047";"17";"4821,26";"3245,2";"2021-05"
"98";"5010047";"26";"0,95";"0";"2021-05"
"99";"5010047";"31";"1531537,97";"1363867,48";"2021-05"
"100";"5010047";"167";"400";"0";"2021-05"
"101";"5010081";"1";"17855";"1000000";"2021-05"
"102";"5010081";"2";"3903462";"4648287,38";"2021-05"
"103";"5010081";"3";"1273659,2";"636415,54";"2021-05"
"104";"5010081";"4";"120,06";"0";"2021-05"
"105";"5010081";"6";"141666,66";"283333,32";"2021-05"
"106";"5010081";"9";"601056,52";"946000";"2021-05"
"107";"5010081";"23";"263049,3";"1000000";"2021-05"
"108";"5023913";"7";"5680,08";"3333,5";"2021-05"
"109";"5023913";"24";"3231,02";"3186,02";"2021-05"
"110";"5023913";"26";"1870,02";"1355,74";"2021-05"
"111";"5023913";"27";"1870,02";"1355,74";"2021-05"
"112";"5023913";"29";"13,56";"18,25";"2021-05"
"113";"5010047";"2";"135529,57";"0";"2021-06"
"114";"5010047";"3";"473623,9";"447708,72";"2021-06"
"115";"5010047";"4";"21716,58";"17000";"2021-06"
"116";"5010047";"17";"3500,66";"3000";"2021-06"
"117";"5010047";"23";"98,7";"0";"2021-06"
"118";"5010047";"26";"0,95";"0";"2021-06"
"119";"5010047";"31";"62699,14";"180834,07";"2021-06"
"120";"5010081";"2";"2402807,59";"2872723,45";"2021-06"
"121";"5010081";"9";"600000";"1350000";"2021-06"
"122";"5010081";"13";"369817,68";"242244,9";"2021-06"
"123";"5010081";"23";"352919,5";"0";"2021-06"
"124";"5023913";"7";"7491,42";"9670";"2021-06"
"125";"5023913";"24";"4091,57";"4136,57";"2021-06"
"126";"5023913";"26";"2014,97";"3561,59";"2021-06"
"127";"5023913";"27";"2014,97";"3561,59";"2021-06"
"128";"5023913";"28";"12,91";"12,91";"2021-06"
"129";"5023913";"29";"35,63";"20,61";"2021-06"
I was asked to pivot this table in a way that it showed the sums of all debits and credits divided by month.
"CUSTOMER_NO";"ACCOUNT_NO";"SUM(ACCOUNTED_DR)2021-01";"SUM(ACCOUNTED_CR)2021-01";"SUM(ACCOUNTED_DR)2021-02";"SUM(ACCOUNTED_CR)2021-02" etc. the columns should be like this.
This is what i wrote:
select * from(select customer_no, account_no, sum(accounted_dr), sum(accounted_cr), to_char(effective_date, 'YYYY/MM') as "Effective Months and Years" from GL_JOURNAL_LINES)
pivot(sum(accounted_dr), sum(accounted_cr) for to_char(effective_date, 'YYYY-MM') in ("2021/01", "2021/02", "2021/03", "2021/04", "2021/05", "2021/06"));
order by to_char(effective_date, 'YYYY-MM')
It shows missing In keyword. What should i do?
You can't apply a function as part of the for - which is causing the initial ORA-01738 error - and should be using a value form the subquery instead. You're also aggregating inside the subquery, which you shouldn't be doing, and referring to columns form the base table outside the subquery, where they won't be recognised. And using double quotes when they should be single quotes to represent literals.
So you want something like this, truncating the effective date to the start of the month and treating that as a date:
select * from (
select customer_no, account_no, accounted_dr, accounted_cr,
trunc(effective_date, 'MM') as effective_month
from GL_JOURNAL_LINES
)
pivot (
sum(accounted_dr) as dr, sum(accounted_cr) as cr
for effective_month
in (
date '2021-01-01' as "2021/01", date '2021-02-01' as "2021/02",
date '2021-03-01' as "2021/03", date '2021-04-01' as "2021/04",
date '2021-05-01' as "2021/05", date '2021-06-01' as "2021/06"
)
)
or converting to a string in the subquery as you already were:
select * from (
select customer_no, account_no, accounted_dr, accounted_cr,
to_char(effective_date, 'YYYY-MM') as effective_month
from GL_JOURNAL_LINES
)
pivot (
sum(accounted_dr) as dr, sum(accounted_cr) as cr
for effective_month
in (
'2021-01' as "2021/01", '2021-02' as "2021/02", '2021-03' as "2021/03",
'2021-04' as "2021/04", '2021-05' as "2021/05", '2021-06' as "2021/06"
)
)
(I changed the format of the string slightly to show the literal value being used in the in, and that the result uses the alias - so the string format doesn't matter as long as it's representative.)
db<>fiddle
Double quotes "" are for identifier (column and table aliases) and single quotes '' are for string literals.
You do not need to aggregate in the inner sub-query, leave that to the PIVOT in the outer query.
Like this:
select *
from (
select customer_no,
account_no,
accounted_dr,
accounted_cr,
to_char(effective_date, 'YYYY/MM') as yyyy_mm
from GL_JOURNAL_LINES
)
pivot(
sum(accounted_dr) AS dr,
sum(accounted_cr) AS cr
for yyyy_mm in ( -- use the alias from the sub-query
'2021/01' AS "2021/01", -- literal AS identifier
'2021/02' AS "2021/02",
'2021/03' AS "2021/03",
'2021/04' AS "2021/04",
'2021/05' AS "2021/05",
'2021/06' AS "2021/06"
)
);

Create a PL/SQL procedure that calculate total monthly income. Total must be printed by month

Write a procedure that calculates and displays total income from all sources of all hotels. Totals must be printed by month, and for each month by event and service type. Include discounts.( 10% discount if the reservation date is 2 month before reservation start date).
The tables are:
Hotel Table has:
Hotel_id, hotel_name, Hotel_city, Hotel_state, Hotel_zip,Hotel_phone
Reservation Table has:
Reservation_id, Hotel_id, Room_num, Service_id, Guest_name, Reservation_date, Reservation_start_date, Reservation_end_date, cancelation_date, Num_of_guest, event_type
Room Table has:
Room_num, Hotel_id, Room_type, Room_capacity, Room_cost
service table has:
service_id, Service_type, Service_cost
This is what I tried, but I want to write it in a procedure form; how do I do that? Please help. Thanks
select month (Reservation_end_date) as , event_type,
sum(case when days>= 2 then cost- (cost/100)* 10
else cost) as total_cost)
((select distinct reservation.hotel_id,reservation_date, reservation_start_date,
reservation_end_date, event_type, room.room_type as R_type ,room_cost as R_cost,
months_between(reservation_start_date,reservation_date)as months
from reservation, room
where reservation.hotel_id = room.hotel_id;)
union
(select hotel_name, reservation_date, reservation_start_date,
reservation_end_date, event_type, services_type, services_cost as cost,
months_between(reservation_start_date,reservation_date)as month
from reservation,service, hotel
where reservation.services_id = service.services_id
and reservation.hotel_id = hotel.hotel_id;))
group by month(reservation_end_date),event_type;
The first step is to get the base query right.
To consolidate a set of dates into their common month use trunc(date_col, 'mm'). Presumably room costs and service costs should be calculated on a per night basis.
To calculate the number of nights simply subtract the start date from the end date.
This query should produce the correct result (your stated business rules are incomplete so it's hard to be certain). Like your posted code it has subqueries to calculate the cost of each room reservation and each service reservation. These are aggregated in the outer query:
select to_char(r_month, 'YYYY-MON') as rpt_month
, event_type
, service_type
, sum ( (r_cost - r_discount ) * no_of_nights ) as tot_cost
from (
select trunc(r.reservation_end_date , 'mm') as r_month
, r.event_type
, cast(null as varchar2(10)) as service_type
, rm.room_cost as r_cost
, case when months_between (r.reservation_start_date, r.reservation_date) >= 2
then rm.room_cost * 0.1
else 0 end as r_discount
, (r.reservation_end_date - r.reservation_start_date ) as no_of_nights
from reservation r
join room rm
on ( r.room_num = rm.room_num
and r.hotel_id = rm.hotel_id )
union all
select trunc(r.reservation_end_date , 'mon') as r_month
, r.event_type
, sv.service_type
, sv.service_cost as r_cost
, case when months_between (r.reservation_start_date, r.reservation_date) >= 2
then sv.service_cost * 0.1
else 0 end as r_discount
, (r.reservation_end_date - r.reservation_start_date ) as no_of_nights
from reservation r
join service sv
on ( r.service_id = sv.service_id )
)
group by r_month
, event_type
, service_type
order by r_month
, event_type
, service_type
;
The second step is put this into a procedure. Again your requirements are fuzzy: should the procedure take any parameters? what format should the output be in? As the business domain (hotel bookings) and the format of the question ("write a procedure that ...") this appears to be a homework assignment so here is the simplest interpretation of "display". It uses dbms_output routines to print to the screen, and rpad() and lpad() to give a nice layout (obviously the spacings may be wonky, because you haven't provide the datatypes of the various columns) .
create or replace procedure display_monthly_reservations as
begin
<< heading >>
dbms_output.put(rpad('MONTH', 8));
dbms_output.put( rpad('EVENT_TYPE' , 20 ) || ' ');
dbms_output.put( rpad('SERVICE_TYPE', 20 ) || ' ');
dbms_output.put_line('TOTAL_COST');
<< per_line >>
for r in (
<< insert the query here >>
) loop
dbms_output.put(r.rpt_month || ' ');
dbms_output.put( rpad(r.event_type , 20 ) || ' ');
dbms_output.put( rpad(r.service_type , 20 ) || ' ');
dbms_output.put_line( lpad(to_char(r.tot_cost , '9999999.99'), 10 ) );
end loop per_line;
end display_monthly_reservations;
/

How to get monthly periods between a date range in Oracle PL/SQL

I have say, start and end date as "6/11/1996" and "3/1/2002" in "mm/dd/yyyy" format respectively.
I need to get all the monthly periods as given below.
Start Date End Date
6/11/1996 - 6/30/1996
7/01/1996 - 7/31/1996
8/01/1996 - 8/31/1996
.
.
.
Till
2/01/2002 - 2/28/2002
Any help would be highly appreciated.
Assuming the interval is given by way of two bind variables, :from_dt and :to_dt (strings in the indicated format):
with
inputs ( f_dt, t_dt ) as (
select to_date(:from_dt, 'mm/dd/yyyy'), to_date(:to_dt, 'mm/dd/yyyy') from dual
),
ld ( l_day, lvl ) as (
select add_months(last_day(f_dt), level - 1), level
from inputs
connect by level <= months_between(last_day(t_dt), last_day(f_dt)) + 1
)
select case when ld.lvl = 1 then i.f_dt else add_months(ld.l_day, -1) + 1 end
as start_date,
least(i.t_dt, ld.l_day) as end_date
from inputs i cross join ld
;
This assumes that in the original post you did, in fact, mean to have one more interval, from 3/1/2002 to 3/1/2002; and the query deals correctly with the case when the from-date and the to-date are in the same month: if the inputs are 6/11/1996 to 6/21/1996, then the output is exactly that interval.
Added: creating column aliases in the declaration of factored subqueries (in the WITH clause), as I have done, requires Oracle 11.2 or above. For earlier versions, it is necessary to write it a little differently, like so:
with
inputs as (
select to_date(:from_dt, 'mm/dd/yyyy') as f_dt,
to_date(:to_dt , 'mm/dd/yyyy') as t_dt
from dual
),
ld as (
select add_months(last_day(f_dt), level - 1) as l_day, level as lvl
from inputs ...............
You could use connect by query with functions add_months and months_between:
with p as ( select date '1996-06-11' d1, date '2002-03-01' d2 from dual )
select greatest(trunc(add_months(d1, level - 1), 'month'), d1) as d1,
trunc(add_months(d1, level), 'month') - 1 as d2
from p connect by level <= months_between(trunc(d2, 'month'), trunc(d1, 'month'))
The output is exactly as requested.
DECLARE
L_min_date DATE := '11-Jun-1996';
L_max_date DATE := '28-Feb-2002';
L_number_of_months NUMBER := 0;
L_new_date DATE := NULL;
BEGIN
L_number_of_months := MONTHS_BETWEEN(L_max_date, L_min_date);
DBMS_OUTPUT.PUT_LINE('Start_Date End_Date');
FOR i IN 1..L_number_of_months
LOOP
SELECT (ADD_MONTHS(L_min_date, 1) - 1) INTO L_new_date FROM dual;
DBMS_OUTPUT.PUT_LINE(L_min_date || ' - ' || L_new_date);
L_min_date := L_new_date + 1;
END LOOP;
END;
/
Something like above can help.

sysdate difference ( how to make query between sysdate and my table date)

I am new to oracle.
I have some problems to make query.
Im trying to make query that solves the difference between sysdate() and the date from my own table
select to_char(to_char(sysdate,'YYYYMMDDHH24MISS')
- to_char(S_DATE||S_HOUR||':00' , 'YYYYMMDDHH24MISS'))
from dual;
I'm doing like that.
In my table, I have two columns 'S_DATE' and 'S_HOUR' that means time.
So, I would like to know the time difference and how to make this query.
Thank you in advance.
You can get difference between two dates by simple - (minus) operator. However first you need to convert date string to date using TO_DATE.
Sample in SQL Fidlle is here
The sample query:
select
sysdate , S_DATE || ' ' || S_HOUR "Date",
round((sysdate - to_date(s_date || s_hour ,' YYYY/MM/DDHH24:MI') ) * 24 * 60, 2) "Dif In Min",
round((sysdate - to_date(s_date || s_hour ,' YYYY/MM/DDHH24:MI') ) * 24 * 60 * 60, 2) "Dif In Sec"
from myDate

Oracle Recursive Query - Dates

I've got two sets of dates being passed into a query and I would like to find all the months/years between both sets of dates.
When I try this:
WITH CTE_Dates (cte_date) AS (
SELECT cast(date '2014-01-27' as date) from dual
UNION ALL
SELECT cast(ADD_MONTHS(TRUNC(cte_date, 'MONTH'),1) as date)
FROM CTE_Dates
WHERE ( TO_DATE(ADD_MONTHS(TRUNC(cte_date, 'MONTH'), 1)) BETWEEN TO_DATE ('27-01-2014','DD-MM-YYYY') AND TO_DATE ('27-04-2014','DD-MM-YYYY'))
OR
( TO_DATE(ADD_MONTHS(TRUNC(cte_date, 'MONTH'), 1)) BETWEEN TRUNC(TO_DATE('27-11-2014','DD-MM-YYYY'), 'MONTH') AND TO_DATE ('27-01-2015','DD-MM-YYYY'))
)
SELECT * from CTE_Dates
I get:
27-JAN-14
01-FEB-14
01-MAR-14
01-APR-14
I would also want to get:
01-NOV-14
01-DEC-14
01-JAN-15
It looks like the OR portion of the WHERE clause gets ignored.
Suggestions on how to create this query?
Thanks
Cory
The problem with what you have now (aside from extra cast() and to_date() calls) is that on the fourth iteration both the conditions are false so the recursion stops; there's nothing to make it skip a bit and pick up again, otherwise it would continue forever. I don't think you can achieve both ranges within the recursion.
You can put the latest date you want inside the recursive part, and then filter the two ranges you want afterwards:
WITH CTE_Dates (cte_date) AS (
SELECT date '2014-01-27' from dual
UNION ALL
SELECT ADD_MONTHS(TRUNC(cte_date, 'MONTH'), 1)
FROM CTE_Dates
WHERE ADD_MONTHS(TRUNC(cte_date, 'MONTH'), 1) <= date '2015-01-27'
)
SELECT * from CTE_Dates
WHERE cte_date BETWEEN date '2014-01-27' AND date '2014-04-27'
OR cte_date BETWEEN date '2014-11-27' AND date '2015-01-27';
CTE_DATE
---------
27-JAN-14
01-FEB-14
01-MAR-14
01-APR-14
01-DEC-14
01-JAN-15
6 rows selected
You can replace the hard-coded values with your pairs of start and end dates. If the ranges might overlap or the second range could be (or end) before the first one, you could pick the higher date:
WHERE ADD_MONTHS(TRUNC(cte_date, 'MONTH'), 1)
<= greatest(date '2015-01-27', date '2014-04-27')
... though that only makes sense with variables, not fixed values.

Resources