SQL Missing Right Parenthesis in nested PL/SQL Query - sql

Can anyone help me out. I'm trying to take the date from one table and insert that into another, then base off the date if it's a weekend or weekday insert that string too. I've been able to do it separately so trying to do it in one swoop I've combined my expressions and now I get the notorious "missing right parenthesis.
Here's my block:
INSERT INTO time (sale_day, date_type)
SELECT sd, dt
FROM (
SELECT sale_date AS sd,(
case
when dy IS null or dy='' then 'Date Missing'
when dy='SAT' then 'Weekend'
when dy='SUN' then 'Weekend'
else 'Weekday' END) as date_type
FROM (SELECT TO_CHAR((sale_date), 'DY') AS dy FROM sales) AS dt
FROM sales
);
Thanks in advance!

Your query is far too complicated. There is no need for nesting three levels of select.
INSERT INTO time (sale_day, date_type)
SELECT sale_date AS sd,
case
when sale_date IS null then 'Date Missing'
when TO_CHAR(sale_date, 'DY') in ('SAT', 'SUN') then 'Weekend'
else 'Weekday'
END as date_type
FROM sales;
Unrelated, but: why are you copying that data into a new table? The query is extremely simple, and it would be more efficient if you just created a view for that information:
create or replace view time
as
SELECT sale_date AS sd,
case
when sale_date IS null then 'Date Missing'
when TO_CHAR(sale_date, 'DY') in ('SAT', 'SUN') then 'Weekend'
else 'Weekday'
END as date_type
FROM sales;
That way the information in time is always up-to-date without the need to copy data around.

Related

Dates in tables and charts are out of order in SQL

How am I able to display my results in order of date? I do have a order by clause but it does not seem to help. I am having the same issue when it is displayed on charts as well. Thanks in advance for your help.
select project_name, Dates, sum(records_number) as
from (
select project_name,
case
when :P22_DATE_RANGES = 'Monthly' then
to_char(date_sys, 'Month')
when :P22_DATE_RANGES = 'Daily' then
to_char(date_sys, 'DD-Mon-YYYY')
when :P22_DATE_RANGES = 'Weekly' then
to_char(TRUNC(date_sys, 'IW'), 'DD-Mon-YYYY')
end as Dates,
RECORDS_NUMBER
from BATCH
WHERE date_sys BETWEEN TO_DATE(:P22_START_DATE) AND TO_DATE(:P22_END_DATE)
) my_records
group by project_name, Dates
order by Dates ASC
;
You are ordering by Dates, but by that point it's a string, so string comparison rules apply - not real date comparison.
It looks like you need to check the required format twice, once for the inner query and then again to format as a string in the outer query; something like:
select project_name,
case
when :P22_DATE_RANGES = 'Monthly' then
to_char(Dates, 'Month')
else
to_char(Dates, 'DD-Mon-YYYY')
end as Dates,
sum(records_number) as
from (
select project_name,
case
when :P22_DATE_RANGES = 'Monthly' then
trunc(date_sys, 'MM')
when :P22_DATE_RANGES = 'Daily' then
trunc(date_sys, 'DD')
when :P22_DATE_RANGES = 'Weekly' then
trunc(date_sys, 'IW')
end as Dates,
RECORDS_NUMBER
from BATCH
WHERE date_sys BETWEEN TO_DATE(:P22_START_DATE) AND TO_DATE(:P22_END_DATE)
) my_records
group by project_name, Dates
order by Dates ASC
;
In the inner query the date_sys value is now being truncated, to IW as you already had, or to the day with DD (in case you have non-midnight times), or to the start of the month with MM.
In the outer query that truncated date is used for grouping and ordering, and is formatted as you require.
Incidentally, the Month and Mon format models are NLS-sensitive, so they will be shown in the session's date language. That may not be an issue, or may be what you want; but if not you can specify that they should always be shown in English, say, with:
select project_name,
case
when :P22_DATE_RANGES = 'Monthly' then
to_char(Dates, 'Month', 'nls_date_language=English')
else
to_char(Dates, 'DD-Mon-YYYY', 'nls_date_language=English')
end as Dates,
...
It seems a bit odd that your 'Monthly' format doesn't include the year - but not sure if that is intentional.

SQL query not retrieving data

SELECT DISTINCT
TO_CHAR(CREATION_DATE,'MONTH') creation_month
FROM
AP_INVOICES_ALL;
This query retrieves the month properly. But if I pass the month in where clause it doesn't retrieve data.
SELECT DISTCINT
TO_CHAR(CREATION_DATE, 'MONTH') CREATION_MONTH
FROM
AP_INVOICES_ALL
WHERE
TO_CHAR(CREATION_DATE, 'MONTH') = 'MARCH';
Please assist. I need to pass month as parameter in one report.
This is a known issue with 'MONTH' formats. The string is padded with characters.
Instead, use 'MON'. Check this out:
select to_char(sysdate, 'MONTH'),
(case when to_char(sysdate, 'MONTH')= 'APRIL' then 1 else 0 end),
to_char(sysdate, 'MON'),
(case when to_char(sysdate, 'MON')= 'APR' then 1 else 0 end)
from dual;
The first case expression returns 0 -- no match. The second returns 1, indicating that they do match.
I think passing in just the month, without the year, is not a good idea. And you will be far better off treating dates as dates, comparing them to dates, etc. Might I suggest something like the following?
SELECT TO_CHAR(creation_date, 'MONTH') AS creation_month
FROM ap_invoices_all
WHERE TRUNC(creation_date, 'MONTH') = DATE'2018-03-01';
TRUNC()ing the date to the month will return a value of type DATE of the first of the month at midnight. You can then compare using regular Oracle DATE values or ANSI DATE literals as above.
Hope this helps.

Getting a missing keyword error in CASE WHEN statement

I'm getting a missing keyword error in Oracle SQL and I'm not sure why. Here's my code. I want to have a column split by different times depending on the variable P1_DATE_CHOOSER. (I'm using Apex App Developer if that helps).
Select START_DATE,
Round(Avg(Run_TIME), 3) as "Average_RunTime",
Round(Max(Run_TIME), 3) as "Max_RunTime", Round(Median(Run_time), 3) as "Median_RunTime"
from
(Select job_id,
Case P1_DATE_CHOOSER
WHEN 'Daily' THEN TRUNC(start_time) as 'START_DATE'
WHEN 'Weekly' THEN to_char(start_time, 'WW') as 'START_DATE'
WHEN 'Monthly' THEN to_char(start_time, 'MONTH') as 'START_DATE'
END,
1440*(END_TIME - START_TIME)) as "RUN_TIME"
from NI_INFA_ACTIVITY_LOG_V
order by job_id asc, start_time asc)
group by START_DATE order by START_DATE
Any help would be appreciated.
Select START_DATE,
Round(Avg(Run_TIME), 3) as 'Average_RunTime',
Round(Max(Run_TIME), 3) as 'Max_RunTime',
Round(Median(Run_time), 3) as 'Median_RunTime'
from
(Select job_id,
(Case :P1_DATE_CHOOSER
WHEN 'Daily' THEN TRUNC(start_time)
WHEN 'Weekly' THEN to_char(start_time, 'DAY')
WHEN 'Monthly' THEN to_char(start_time, 'MONTH')
END) AS 'START_DATE',
1440*(END_TIME - START_TIME)) as "RUN_TIME"
from NI_INFA_ACTIVITY_LOG_V
)
group by START_DATE
order by START_DATE
;
First of all when ever you are binding the APEX variables or IDs, you have to
prefix with : followed by item ID.
Second, in case...when...then....end the column alias name must be mentioned after end key word.
Third, dont use group by order by inside the subqueries. No use if you do.
Your subquery needs an alias. If you want to do this:
select something
from
(subquery goes here)
You have to give it an alias
select something
from
(subquery goes here) aliasName
There are other issues with your code as well. The order by clause inside the subquery is useless at best.

Oracle group by work time period and non work time period

I cannot find solution how to use Select statement in Oracle with group by.
I need to group by timestamp column with work time period (from 09:00 to 21:00 of "current day") and second period from (21:01 previous day to 08:59 of "current day")
within one mounth.
any advice is welcome.
thanks.
The key to this grouping is to subtract 9 hours from the datetime to get the "workdate" and then use the hour function to determine whether it is a work hour or something else. Here is an example:
select trunc(worktime - 9.0/24) as workdate,
(case when hour(worktime) between 8 and 20 then 'WorkHours' else 'OtherHours' end),
count(*)
from t
group by trunc(worktime - 9.0/24),
(case when hour(worktime) between 8 and 20 then 'WorkHours' else 'OtherHours' end);
To check for a particular month, you probably want to use the workdate rather than the actual date (so the first nine hours of the month are really part of the previous month).
group by
trunc(timestamp_col, 'MM')
, case
when to_char(timestamp_col, 'hh24') between '08' and '20'
then 'work'
else 'other'
end
trunc(timestamp_col, 'MM') -- give you the month
case
when to_char(timestamp_col, 'hh24') between '08' and '20'
then 'work'
else 'other'
end
-- give you either 'work' or 'other'

Calling a function that return a table with dates

I have a problem with a function.
I need to get a table with dates of a month, name of the day and the amound of weekdays in that month.
I already found some help here and adapted it to my need, but I can't get the function running/compiled
Here is what I have so far:
create or replace TYPE DATE_ROW AS OBJECT
(
MyDate DATE,
Dayname VARCHAR2(12),
Amount Integer
)
create or replace TYPE DATE_TABLE as table of DATE_ROW
create or replace FUNCTION myfinaldays (mydate date)
RETURN DATE_TABLE
PIPELINED
IS
V_MYDATE DATE;
V_DAYNAME VARCHAR2(12);
V_AMOUNT NUMBER;
BEGIN
with this_month as (
select trunc(to_date('mydate'), 'mm')+level-1 dy
from dual
connect by level < (trunc(add_months(to_date('mydate'),1), 'mm')- trunc(to_date('mydate'), 'mm'))+1
)
FOR i IN 0 .. (select count(*) from this_month) LOOP
select (dy) Daydate,
to_char(dy, 'day'), Dayname,
( select count(*)
from this_month
where to_char(dy, 'dy') not in ('sat', 'sun')
) Amount
from this_month
where to_char(dy, 'dy') not in ('sat', 'sun')
and EXTRACT(day from to_date(dy)) = i;
pipe row (date_row(v_mydate,v_dayname, v_amount));
END LOOP;
RETURN;
END;
The function call would then be something like:
select * from date_table(cast(myfinaldays('01.02.12')));
I can only give one date as a parameter.
I hope someone can help me out here since this is slowly driving me mad.
Any ideas, examples, or thoughts would be greatly appreciated.
UPDATE:
Ok here is an update with mybe some more helpfull infos to my problem:
This is working and my aim is to put this in a function so I can call it with 1 parameter:
with this_month as (
select trunc(to_date('01.02.12'), 'mm')+level-1 dy
from dual
connect by level < (trunc(add_months(to_date('01.02.12'),1), 'mm')- trunc(to_date('01.02.12'), 'mm'))+1
)
select (dy) mydate, (select count(*) from this_month) Days_in_month
, to_char(dy, 'day') Dayname
, ( select count(*) from this_month where to_char(dy, 'dy') not in ('sat', 'sun') ) Amount
from this_month
where to_char(dy, 'dy') not in ('sat', 'sun') ;
for the loop I added: 'and EXTRACT(day from to_date(dy))=i' at the end.
I added a date so you can see what I need in the end. If I write mydate instead and enter 01.02.12 as parameter after running it in the developer it still works.
Errors I get compiling:
- Error(10,1): PL/SQL: SQL Statement ignored
- Error(15,5): PL/SQL: ORA-00928: Keyword SELECT missing
- Error(22,8): PLS-00113: END-Definer 'LOOP' must complete 'myfinaldays' in row 1, comlumn 10
- Error(23,4): PLS-00103: Found the symbol "RETURN"
The errors are translated since my Oracle does not run english so I hope to have guessed it correctly.
You have a number of problems here.
You can't use a select directly in for i in 1..x (and it should be 1, not 0), you'd have to select into a local variable and use that as the end value.
You can't use with as an input to a for loop; but you can use a cursor there, as I have below.
The select inside the loop with get no-data-found when i represents a Saturday or Sunday (the 4th in this case, assuming you're running it for February).
Getting the count of all non-weekend rows for every row is inefficient, though it doesn't really matter for such a small amount of data.
You are referring to to_date('mydate'); mydate is already a date, and 'mydate' is a string that has no relation to it.
And maybe some other things too, like assuming the date format and locale, since to_char(x, 'dy') will not return sat or sun everywhere, but I'll assume this is going to be used in a restricted way so you don't care about that too much.
From trying to figure out what output you're expecting, I think this will work (with modified types to get the days_in_month you added to the original question):
create or replace type date_row as object
(
mydate DATE,
dayname VARCHAR2(12),
days_in_month NUMBER,
amount NUMBER
)
/
create or replace type date_table as table of date_row
/
create or replace function myfinaldays (mydate date)
return date_table pipelined deterministic is
begin
for r in (
select *
from (
select dy as daydate,
to_char(dy, 'day') as dayname,
count(*) over () as days_in_month,
sum(case when to_char(dy, 'dy') in ('sat', 'sun')
then 0 else 1 end) over () as amount
from (
select trunc(mydate, 'mm')+level-1 dy
from dual
connect by level < (trunc(add_months(mydate, 1), 'mm')
- trunc(mydate, 'mm')) + 1
)
)
where to_char(daydate, 'dy') not in ('sat', 'sun')
) loop
pipe row (date_row(r.daydate, r.dayname, r.days_in_month, r.amount));
end loop;
end myfinaldays;
/
(If you don't want days_in_month then you can move the sat/sun check up and use the count for amount instead of the sum(case). The outer select is currently filtering on the day of the week so you can count all days, but that's not an issue if you don't want that value).
Then call as:
alter session set nls_date_format = 'DD.MM.RR';
select * from table(myfinaldays(to_date('01.02.12', 'DD.MM.RR')));
MYDATE DAYNAME DAYS_IN_MONTH AMOUNT
-------- ------------ ------------- ----------
01.02.12 wednesday 29 21
02.02.12 thursday 29 21
03.02.12 friday 29 21
06.02.12 monday 29 21
...
29.02.12 wednesday 29 21
21 rows selected.
Since your function is a table valued function (returns a table), you cannot call it as regular function.
you have join with it:
something like this in Oracle:
select * from date_table t
cross join table( myfinaldays(<t.date colum>))