I'm going to write a if statement within that if condition pass need to run first select statement else second one. But it showing an INTO clause is expected in this SELECT statement
declare
x integer;
begin
select to_char(sysdate,'hh') into x from dual;
if x > 12 then
select sysdate from dual;
else
select sysdate+1 from dual;
end if;
end;
You are running all three queries in a PL/SQL context (within an anonymous PL/SQL block, between the begin and end), so they all need to follow the PL/SQL rules.
That means they all need to be selected into something (or treated as cursors), so you need a date variable to select into, and then you need to do something with that variable:
declare
x integer;
d date;
begin
select to_number(to_char(sysdate, 'hh24')) into x from dual;
if x > 12 then
select sysdate into d from dual;
else
select sysdate+1 into d from dual;
end if;
-- do something with the variable
dbms_output.put_line(to_char(d, 'YYYY-MM-DD HH24:MI:SS'));
end;
/
Notice that I've also changed the first query to use the hh24 format element, because hh gives the 12-hour-clock time, which can never be more than 12.
You don't really need the first query, you can just do:
declare
d date;
begin
if to_number(to_char(sysdate, 'hh24')) > 12 then
select sysdate into d from dual;
...
But it looks like you're only using PL/SQL so you can use the if/then/else construct, which isn't available in plain SQL. That does have case though, so as #jarlh said you can do:
select
case
when to_number(to_char(sysdate,'hh24')) > 12
then sysdate
else sysdate + 1
end as result
from dual;
or
select
case
when extract(hour from systimestamp) > 12
...
You are also retaining the current system time, even if you change the date to tomorrow; you might want to set the time to midnight, which you can do with trunc(sysdate). It's unclear which you want though.
The logic also looks odd, but again it's unclear what you intended; 00-12 is modified to tomorrow and 13-23 kept as today, which might be backwards. If you want any time after (and including?) midday to be treated as tomorrow then you should be adding a day if the hour is 12 or above, so 00-11 is kept as today and 12-23 is modified to tomorrow. If that is what you meant then you can do:
select case
when extract(hour from systimestamp) >= 12
then trunc(sysdate) + 1
else trunc(sysdate)
end as result
from dual;
Or even more simply, assuming you don't want to preserve the actual time this is run, you can do:
select trunc(sysdate + 12/24) as result
from dual;
fiddle
Related
Hi everyone im very very new to messing with oracle but I have to figure out how to somehow loop a query to get an hourly output on a 24 hour interval. I dont know how to declare that the time should start whenever SYSDATE is and then to add hour hour each to that.
For example something like:
Declare the system time here
Add an hour for the start time until 24 hours is complete. Do i use loop while for that so it automatically generates without having to put in new query everyday?
Not sure what are you expecting, but here is sql that will give you 24 hours (records) starting with the hour of Sysdate. You can use it as a cursor to loop through or something else....
Select
To_Char(SYSDATE, 'hh24') "HOUR_START",
CASE
WHEN To_Number(To_Char(SYSDATE, 'hh24')) + LEVEL-1 > '23' THEN
LPAD(To_Number(To_Char(SYSDATE, 'hh24')) + LEVEL-1 - 24, 2, '0')
ELSE
LPAD(To_Number(To_Char(SYSDATE, 'hh24')) + LEVEL-1, 2, '0')
END "HOURS_24"
From
Dual
CONNECT BY LEVEL <= 24
No need to use a loop or a cursor within a PL/SQL block, but using a clean SELECT statement containing XML techniques such as
SELECT TO_NUMBER(TO_CHAR(sysdate, 'hh24')) AS hour_start,
XMLCast(column_value AS INT) AS hours
FROM XMLTable('1 to 24')
ORDER BY CASE WHEN SIGN(hours-hour_start)>=0 THEN hours ELSE hours+24 END
Demo
I'm new to SQL and was hoping for some help turning this into a function:
SELECT
SYSDATE "Today's Date",
NEXT_DAY(trunc(SYSDATE, 'MONTH')-1, 'Tuesday')"First Tuesday this Month",
NEXT_DAY (LAST_DAY(SYSDATE)+1,'TUESDAY') "First Tuesday of Next Month"
FROM DUAL;
Here's my attempt at turning the above into a function (as you can see it didn't go so hot)...
CREATE OR REPLACE FUNCTION first_tuesday
RETURN DATE
AS
date1 DATE;
date2 DATE;
date3 DATE;
BEGIN
date1 := SELECT SYSDATE;
date2 := SELECT NEXT_DAY(trunc(SYSDATE, 'MONTH')-1, 'Tuesday');
date3 := SELECT NEXT_DAY (LAST_DAY(SYSDATE)+1,'TUESDAY');
RETURN 'todays date:' || date1,
'first tuesday this month:' || date2,
'first tuesday next month:' || date3;
END;
/
SELECT first_tuesday FROM DUAL;
What am I doing wrong?
You're really trying to return three values there. I'd suggest breaking it out into separate functions, but leave "today's date" just as a call to sysdate.
You don't need to issue a SELECT query to populate a variable, and in this case you can just return an expression.
CREATE OR REPLACE FUNCTION first_tuesday
RETURN DATE
AS
BEGIN
RETURN NEXT_DAY(trunc(SYSDATE, 'MONTH')-1, 'Tuesday')
END;
/
Something to look out for here is that within the execution of a SQL statement, SYSDATE always has the same value, but that's not the case in PL/SQL. So when sysdate is evaluated in PL/SQL that is called from a SQL statement, you can get a different value in each context. So if you call:
select sysdate col1,
some_function col2
from dual;
... and during it's lengthy execution some_function calculates sysdate, it could be using a slightly different (later) value than the parent SQL statement.
The way to avoid this is to pass sysdate into the function:
CREATE OR REPLACE FUNCTION first_tuesday (sysdate_in date)
RETURN DATE
AS
BEGIN
RETURN NEXT_DAY(trunc(sysdate_in, 'MONTH')-1, 'Tuesday')
END;
/
So then you might ...
Select ...
From invoices
Where invoice_date < first_tuesday(sysdate)
That's a trivial example just to demonstrate the principle, and it really only matters for functions called from SQL.
To be honest, most practitioners in your situation would probably skip the function and just:
Select ...
From invoices
Where invoice_date < NEXT_DAY(trunc(sysdate, 'MONTH')-1, 'Tuesday')
Edit: by the way, you probably want to use:
NEXT_DAY (LAST_DAY(SYSDATE),'TUESDAY')
... not ...
NEXT_DAY (LAST_DAY(SYSDATE)+1,'TUESDAY')
I need to get the difference of 2 date fields, if the greater date is null then I'll use SYSDATE instead. Having this requirement, I created a function to solve this issues (note: this code follows the standard of the organization, not my personal taste)
CREATE FUNCTION F_GET_DIFFERENCE (P_WORKFLOWID NUMBER)
RETURN NUMBER --result in minutes
IS
TIME NUMBER;
BEGIN
TIME := 0
SELECT
F_WORKTIME_DIFF(NVL(X.ENDDATE, SYSDATE), X.STARTDATE)
INTO
TIME
FROM
TABLEX X
WHERE
X.WORKFLOWID = P_WORKFLOWID;
RETURN TIME;
EXCEPTION
WHEN OTHERS THEN
RETURN 0;
END;
The F_WORKTIME_DIFF function already exists and calculates the worktime of the day (assumming nobody works at 12 a.m. and things like that). The problem is when calling this function, the result contains an additional amount of time. That's very strange, because when executing the query in the function, it returns the expected output.
Example (important: date format in Peru is DD/MM/YYYY HH24:MI:SS)
TABLEX
WORKFLOWID STARTDATE ENDDATE
1 '01/12/2012 10:00:00' null
Assumming that the server day is the same day (01/12/2012) but greater time (10:01:00), we execute the function:
SELECT F_GET_DIFFERENCE(1)
FROM DUAL;
The result is: 14.
Now, executing the query in the function and having the server time at 10:02:00, the result is 2 (exact output).
I even tried executing this
SELECT
F_WORKTIME_DIFF(NVL(X.ENDDATE, SYSDATE), X.STARTDATE) SELECT_WAY,
F_GET_DIFFERENCE(1) FUNCTION_WAY
FROM
TABLEX X
WHERE
X.WORKFLOWID = 1
And the result is (having the server time at 10:10:00)
SELECT_WAY FUNCTION_WAY
10 24
Is maybe any consideration that I must take into account when working with Oracle dates in inner functions or anything that could explain this odd behavior?
It is difficult to tell anything without seeing the function F_WORKTIME_DIFF.
Whatever is the datatype returned from F_WORKTIME_DIFF, it is casted to number when assigned to the variable time. This may be a clue.
This may not be exactly what are you looking for but the first example gives you hours diff between two dates:
Select EXTRACT(HOUR FROM (SYSDATE - trunc(SYSDATE )) DAY TO SECOND ) From dual
/
Select
EXTRACT(hour From Cast(SYSDATE as timestamp)) hh,
EXTRACT(minute From Cast(SYSDATE as timestamp)) mi,
EXTRACT(second From Cast(SYSDATE as timestamp)) ss
From dual
/
Table my_table is:
CREATE TABLE MY_TABLE(
ID NUMBER NOT NULL,
MY_DATE DATE NOT NULL);
Typing the following query:
select sysdate from dual
the result is:
10-MAG-12 21:22:32
note that mag = may.
Now, if I type this query:
select *
from my_table
where my_date <= sysdate
the result is:
9918 10-MAG-12 20:00:00
9915 10-MAG-12 21:00:00
9952 10-MAG-12 22:00:00
9951 10-MAG-12 23:00:00
Note that in my_table I have only these 4 records. Why I see all the records and not the first and second record only? Thanks.
I use Oracle SQL Developer.
Edit: please note that when I insert a record with PL/SQL I type something like:
nCount NUMBER;
myDate DATE;
stringDate VARCHAR2(255);
BEGIN
nCount := 0;
stringDate := substr(to_char(trunc(sysdate)),0,9);
myDate := to_date(stringDate || ' 20:00:00','dd-mm-yyyy hh24:mi:ss');
for t in (a cursor) loop
insert into MY_TABLE(ID,MY_DATE)
values (9918,myDate+(nCount/1440));
nCount := nCount + 60;
end loop;
END;
I suspect that the data being stored in your table does not have a year of 2012. It probably has a year of 0012 (two thousand years ago).
What do you see when you run the query
SELECT id, to_char( my_date, 'dd-mm-yyyy hh24:mi:ss' )
FROM my_table
I expect that the year will be 0012 rather than 2012. The reason for that is that the code you're using to insert the data is incorrectly converting a date to a string without using an explicit format mask then converts the string back to a date using an explicit format mask that happens not to match the session's NLS_DATE_FORMAT. In general, if you ever find yourself converting a date to a string and back to a date, you're probably doing something wrong. If you change your code to simply do date manipulation, it will be more efficient, more robust, and less error-prone.
DECLARE
nCount NUMBER;
myDate DATE;
BEGIN
nCount := 0;
myDate := trunc(sysdate) + interval '20' hour;
for t in (a cursor) loop
insert into MY_TABLE(ID,MY_DATE)
values (9918,myDate+(nCount/1440));
nCount := nCount + 60;
end loop;
END;
Walking through why the original code goes wrong
stringDate := substr(to_char(trunc(sysdate)),0,9);
This takes SYSDATE and truncates it to midnight on the current day. So far, so good. Then, it calls TO_CHAR without an explicit format mask. This causes Oracle to use the session's NLS_DATE_FORMAT, meaning that different users with different settings will get different results. If your session's NLS_DATE_FORMAT happens to be 'dd-mon-rr hh24:mi:ss', which I'm guessing based on the query results you posted, that will mean that the string has a 2-digit year. Your SUBSTR appears to assume that the output has just a two-digit year (if you have a different NLS_DATE_FORMAT, your SUBSTR will generate different bugs such as potentially cutting off the 12 from a year of 2012 leaving a year of just 20).
myDate := to_date(stringDate || ' 20:00:00','dd-mm-yyyy hh24:mi:ss');
Assuming stringDate is something like 10-MAG-12, this next line generates a string 10-MAG-12 20:00:00 and then tries to convert it to a date using the format mask dd-mm-yyyy hh24:mi:ss. This assumes that the string has a 4-digit year so when it only finds 2-digits, it assumes that you meant the year 12, not the year 2012.
I am trying to optimize a simple SQL query and was wondering if anyone has any suggestions. I am developing using Oracle SQL Developer (which I don't like) on an Oracle 11g database. The query I am using is:
SELECT count(*)
FROM my_table
WHERE my_date
BETWEEN TO_DATE('2012-5-09T05.00.00','YYYY-MM-DD"T"HH24:MI:SS')
AND TO_DATE('2012-5-10T04.59.59','YYYY-MM-DD"T"HH24:MI:SS')
AND my_code='33'
GROUP BY my_code;
Also, I want to be able to use this query dynamically by changing the part of the date to be whatever the current date is, but I want to be able to specify the hour. So I want to be comparing something like:
getdate() + 'T05.00.00'
I have no idea how to do this and the getdate() function doesn't seem to work in SQL Developer/I don't know how to use it correctly.
So what I'm looking for is optimization suggestions and pointers on how to just dynamically change the day-month-year part of the date I want to constrain my results to. Thanks!
To get current date, you can use SYSDATE. To add x number of hours to it, you can add x/24. So something like this:
Example: Get current date + 5 hours
SELECT SYSDATE + 5/24 FROM dual
So in your example:
SELECT count(*)
FROM my_table
WHERE my_date
BETWEEN sysdate
AND sysdate + 5/24 -- if you want 5 hours ahead, for example
AND my_code='33'
GROUP BY my_code;
If you want to be able to change the number of hours, you could make this code into a function, and pass in the hours and code as variables.
Something like this:
CREATE FUNCTION myfunc
(
p_num_hours INT
, p_my_code VARCHAR
) RETURN INT
AS
l_ret INT;
BEGIN
SELECT count(*)
INTO l_ret
FROM my_table
WHERE my_date
BETWEEN sysdate
AND sysdate + p_num_hours/24
AND my_code=p_my_code
RETURN l_ret;
END;
As an alternative to adding fractional days via expressions such as "5 / 24" you might want to use an INTERVAL constant. For example:
SELECT count(*)
FROM my_table
WHERE my_date BETWEEN (TRUNC(SYSDATE) + INTERVAL '5' HOUR)
AND (TRUNC(SYSDATE) + INTERVAL '1' DAY +
INTERVAL '5' HOUR - INTERVAL '1' SECOND) AND
my_code='33'
GROUP BY my_code
I like to use INTERVAL constants because it's quite clear what these constants represent. With the fractional-day constants I sometimes get confused ('course, I sometimes get confused, regardless... :-)
Share and enjoy.
If I understand correctly, something like
select count(*)
from my_table
where trunc(my_date) = trunc(sysdate)
and my_code = '33'
group by my_code;
or
select count(*)
from my_table
where my_date
between sysdate and sysdate + 5/24
and my_code = '33'
group by my_code;
HTH.
Alessandro