Doing Math with Dates given in the problem - sql

The following is the query and code attached to the query.I am not able to figure out how to use the date 31-dec-2006 in the problem.
For each rental property, list the address, include street, city, state.
Also list rental type and number of days listed as "Number of Days Listed". Order results by rental type ascending and number of days listed descending.
Instead of using today's date to determine days listed, use 31-dec-2006
The issue is that Im not receiving any results for this query. I believe I am doing something wrong in the where statement. Im not sure how to assigne a value to the date.
select rp_street, rp_city, rp_state, rp_type, (rp_datelisted - sysdate) as "Number of Days Listed"
from rentproperty
where sysdate = '31-dec-2006'
order by rp_type asc, "Number of Days Listed" desc;

When working with dates, then work with dates, not strings. '31-dec-2006' is just a string. It looks like a date (to us, humans), Oracle will try to convert it to a date (if it can), but you can never be sure it'll work. For example, it won't work in my database:
SQL> select count(*) From emp where hiredate < '31-dec-2006';
select count(*) From emp where hiredate < '31-dec-2006'
*
ERROR at line 1:
ORA-01858: a non-numeric character was found where a numeric was expected
If I fix several things, it will work:
SQL> alter session set nls_date_language = english;
Session altered.
SQL> alter session set nls_date_format = 'dd-mon-yyyy';
Session altered.
SQL> select count(*) From emp where hiredate < '31-dec-2006';
COUNT(*)
----------
14
SQL>
Therefore, either use a date literal (which always looks like date 'yyyy-mm-dd'), or apply the TO_DATE function to a string, with appropriate format mask, e.g. to_date('31.12.2006', 'dd.mm.yyyy') and your query will always work.
Here's what you could have done (I shortened the column list); the RENTPROPERTY CTE lists some sample data; you need code from line 7 onwards.
SQL> with rentproperty (rp_street, rp_type, rp_datelisted) as
2 (select 'Oak street' , 'Type A', date '2000-01-25' from dual union all
3 select '31st street', 'Type B', date '2001-10-30' from dual union all
4 select 'Elm street' , 'Type B', date '2004-08-25' from dual union all
5 select 'Bee street' , 'Type A', date '2006-11-30' from dual
6 )
7 select rp_street,
8 rp_type,
9 (date '2006-12-31' - rp_datelisted) days_listed
10 from rentproperty
11 where rp_datelisted < date '2006-12-31'
12 order by rp_type asc, days_listed desc;
RP_STREET RP_TYP DAYS_LISTED
----------- ------ -----------
Oak street Type A 2532
Bee street Type A 31
31st street Type B 1888
Elm street Type B 858
SQL>

In oracle, sysdate is the current date, so unless today is 31-dec-2006, you'll never get any results. If you've used "sysdate" as the column name, try putting it in quotes.

sysdate = '31-dec-2006' condtion is false that is why no output!!
this is probably what you need:
select rp_street, rp_city, rp_state, rp_type, (rp_datelisted - sysdate) as "Number of
Days Listed"
from rentproperty
where rp_datelisted= '31-dec-2006'
order by rp_type asc, "Number of Days Listed" desc;

The Oracle sysdate returns date and time. Try trunc(sysdate) to compare similar values.
select rp_street, rp_city, rp_state, rp_type, (rp_datelisted - sysdate) as "Number of Days Listed"
from rentproperty
where trunc(sysdate) = '31-dec-2006'
order by rp_type asc, "Number of Days Listed" desc;

Related

BETWEEN Two Dates is Returning Rows of the Same Two Dates instead of the Ones Between

I'm trying to select rows that are between two specific dates, but I'm getting the rows that are of the same dates specified in BETWEEN instead. I tried using operators > and <, but nothing seems to work. Does it have to do with the date format?
SELECT r.RESERVATION_ID, a.AGENT_ID, a.AGENT_FNAME AS AGENT_NAME, t.TRIP_ID,
s.RESERVATION_STATUS
FROM RESERVATION r
INNER JOIN AGENT a
ON
a.AGENT_ID=r.AGENT_ID
INNER JOIN TOURTRIP_RESERVATION t
ON
r.RESERVATION_ID=t.RESERVATION_ID
INNER JOIN RESERVATION_STATUS s
ON
r.RESERVATION_STATUSID=s.RESERVATION_STATUSID
WHERE r.AGENT_ID IS NOT NULL
AND r.RESERVATION_DATE BETWEEN '15-MAR-20' AND '26-MAY-20'
AND r.RESERVATION_STATUSID=100;
I used to_date('03.06.2020','DD.MM.YYYY') format to update the data in the reservation_id column. However, when I use
and RESERVATION_DATE > to_date('15.03.2020','DD.MM.YYYY')
and RESERVATION_DATE < to_date('26.05.2020','DD.MM.YYYY')
it's returning nothing
This is the reservation table
The condition between includes the start and the end.
You can use the operators > and < to exclude it.
Your example simplified:
create table RESERVATION (
RESERVATION_ID number
,RESERVATION_DATE date)
Data:
insert into RESERVATION values (1, to_date('01.06.2020','DD.MM.YYYY'));
insert into RESERVATION values (2, to_date('02.06.2020','DD.MM.YYYY'));
insert into RESERVATION values (3, to_date('03.06.2020','DD.MM.YYYY'));
Query A)
select RESERVATION_ID
from RESERVATION
where RESERVATION_DATE between to_date('01.06.2020','DD.MM.YYYY')
and to_date('03.06.2020','DD.MM.YYYY')
will return:
1
2
3
The Query B)
select RESERVATION_ID
from RESERVATION
where RESERVATION_DATE > to_date('01.06.2020','DD.MM.YYYY')
and RESERVATION_DATE < to_date('03.06.2020','DD.MM.YYYY')
will return
2
I assume that RESERVATION_DATE is a date. Use always explicit datatype conversion.
I changed the date format, try this and tell me if you have other result.
SELECT r.RESERVATION_ID, a.AGENT_ID, a.AGENT_FNAME AS AGENT_NAME, t.TRIP_ID,
s.RESERVATION_STATUS
FROM RESERVATION r
INNER JOIN AGENT a
ON
a.AGENT_ID=r.AGENT_ID
INNER JOIN TOURTRIP_RESERVATION t
ON
r.RESERVATION_ID=t.RESERVATION_ID
INNER JOIN RESERVATION_STATUS s
ON
r.RESERVATION_STATUSID=s.RESERVATION_STATUSID
WHERE r.AGENT_ID IS NOT NULL
AND r.RESERVATION_DATE BETWEEN '2020-03-15' AND '2020-05-26'
AND r.RESERVATION_STATUSID=100;
If RESERVATION_DATE column's datatype is DATE, don't compare it to strings, because '15-MAR-20' is a string. Oracle will try to implicitly convert it to a valid date value; sometimes it'll succeed, sometimes it'll return false value (as its NLS settings differ from what you provided), and sometimes it'll fail and raise an error.
These two:
date '2020-03-15'
to_date('15.03.2020', 'dd.mm.yyyy')
on the other hand, are dates.
BETWEEN is inclusive and will return both limits, if they exist:
AND r.RESERVATION_DATE BETWEEN date '2020-03-15' and date '2020-05-26'
If you want to exclude those limits, then
AND r.RESERVATION_DATE > date '2020-03-15'
AND r.RESERVATION_DATE < date '2020-05-26'
But, if RESERVATION_DATE is a VARCHAR2 column, you're doing a big mistake. Never store dates as strings. If you can't afford modifying datatype, then you'll have to convert it to date:
and to_date(r.reservation_date, 'dd-mon-yy') between date '2020-03-15'
and date '2020-05-26'
This will work as long as there aren't any invalid values in that column. Because, as it is a string, you can put something like 15-AA-2F which certainly isn't a date, but can be stored into such a column. In that case, query will fail and you'll have to fix data.
Comment you posted says that you tried TO_DATE('26-MAY-20'). That's not enough - you should provide format mask as that can be
26th of May 2020
20th of May 2026
and it depends on NLS settings. Furthermore, it'll fail in my database:
Because NLS settings are different:
SQL> select to_date('26-may-20') from dual;
select to_date('26-may-20') from dual
*
ERROR at line 1:
ORA-01858: a non-numeric character was found where a numeric was expected
Because we don't have "may" in Croatia:
SQL> select to_date('26-may-20', 'dd-mon-yy') from dual;
select to_date('26-may-20', 'dd-mon-yy') from dual
*
ERROR at line 1:
ORA-01843: not a valid month
But this works, as I told Oracle what I want:
SQL> select to_date('26-may-20', 'dd-mon-yy', 'nls_date_language = english') from dual;
TO_DATE('26-MAY-20'
-------------------
26.05.2020 00:00:00
Even better, use digits only or date literal (which is always a date 'yyyy-mm-dd'):
SQL> select to_date('26.05.2020', 'dd.mm.yyyy') d1,
2 date '2020-05-26' d2
3 from dual;
D1 D2
------------------- -------------------
26.05.2020 00:00:00 26.05.2020 00:00:00
SQL>
Based on sample data you provided, presuming that reservation_date column's datatype is date:
setting environment first
sample data is from line #1 - 9
query you need begins at line #10
Here you go:
SQL> alter session set nls_date_language = 'english';
Session altered.
SQL> alter session set nls_Date_format = 'dd-mon-yy';
Session altered.
SQL> with reservation (reservation_id, reservation_date, agent_id) as
2 (select 8576, date '2020-03-15', 222 from dual union all
3 select 7325, date '2020-05-26', 333 from dual union all
4 select 3186, date '2020-04-23', 111 from dual union all
5 select 8000, date '2020-04-05', 555 from dual union all
6 select 4120, date '2020-01-03', null from dual union all
7 select 1546, date '2020-02-15', null from dual union all
8 select 1007, date '2020-05-06', null from dual
9 )
10 select *
11 from reservation
12 where reservation_date between date '2020-03-15'
13 and date '2020-05-26'
14 order by reservation_date;
RESERVATION_ID RESERVATI AGENT_ID
-------------- --------- ----------
8576 15-mar-20 222
8000 05-apr-20 555
3186 23-apr-20 111
1007 06-may-20
7325 26-may-20 333
SQL>

Checking if a Birthday was between a 6 month span which crosses the year break

I am trying to write SQL code (using SQL Developer) that checks if a person had a birthday within the past 6 month insurance term.
This is what my code currently looks like.
SELECT DRIVER_KEY, CASE WHEN BDAY BETWEEN EFFDAY AND EXPDAY THEN 1 ELSE 0 END AS BDAYIND FROM (
SELECT DISTINCT A.DRIVER_KEY
, TO_CHAR(A.BIRTH_DATE,'mm/dd') AS BDAY
, TO_CHAR(SUBSTR(A.EFFECTIVE_DATE_KEY,5,2)||'/'||SUBSTR(A.EFFECTIVE_DATE_KEY,7,2) ) AS EFFDAY
, TO_CHAR(SUBSTR(A.EXPIRATION_DATE_KEY,5,2)||'/'||SUBSTR(A.EXPIRATION_DATE_KEY,7,2) ) AS EXPDAY
FROM DRIVER_TABLE A
);
It works - so long as the term doesn't cross the break in year. However, my code currently says that 01/25 is NOT between 09/19 and 03/19... How do I fix this?
EDIT: As APC pointed out, my solution does not work for leap years. I would normally delete this post, but it was already selected as the answer to the question. I updated my code below to use the year logic from Brian Leach's solution instead of the to_date strings. Please upvote Brian or APC's answers instead.
Here is my create statement with arbitrary dates:
create table DRIVER_TABLE
(
BIRTH_DATE date,
EFFECTIVE_DATE_KEY date,
EXPIRATION_DATE_KEY date
);
insert into DRIVER_TABLE
values(to_date('05/01/1980','MM/DD/YYYY'),
to_date('11/01/2016','MM/DD/YYYY'),
to_date('04/01/2017','MM/DD/YYYY'));
Here is the query:
select case when BirthdayEFFYear between EFFECTIVE_DATE_KEY and EXPIRATION_DATE_KEY
or BirthdayEXPYear between EFFECTIVE_DATE_KEY and EXPIRATION_DATE_KEY
or to_number(EXPIRATION_DATE_KEY - EFFECTIVE_DATE_KEY) / 365 > 1
then 1 else 0 end BDAYIND
from(
select add_months(BIRTH_DATE,12 * (extract(year from EFFECTIVE_DATE_KEY) - extract(year from BIRTH_DATE))) BirthdayEFFYear,
add_months(BIRTH_DATE,12 * (extract(year from EXPIRATION_DATE_KEY) - extract(year from BIRTH_DATE))) BirthdayEXPYear,
EFFECTIVE_DATE_KEY,EXPIRATION_DATE_KEY
from DRIVER_TABLE A
)
SQLFiddle
Compare dates as dates, not as strings.
Apparently EFFECTIVE_DATE_KEY contains the year in the first four characters, and as such the following should give you what you're looking for:
SELECT DRIVER_KEY,
CASE
WHEN BDAY BETWEEN EFFDAY AND EXPDAY THEN 1
ELSE 0
END AS BDAYIND
FROM (SELECT DISTINCT A.DRIVER_KEY,
A.BIRTH_DATE AS BDAY,
TO_DATE(A.EFFECTIVE_DATE_KEY, 'YYYYMMDD') AS EFFDAY,
TO_DATE(A.EXPIRATION_DATE_KEY, 'YYYYMMDD') AS EXPDAY
FROM DRIVER_TABLE A);
Best of luck.
'01/25' is not between '09/19' and '03/19' because between() is never true when the second argument is smaller than the first argument. You fall ito this trap because you're working with strings. It is always easier to work with dates using the DATE datatype.
It looks like your columns effective_date and expiry_date may not be stored as dates but rather a string; unfortunately this is a common data modelling mistake. If so, you need to cast them to DATE first before applying the following.
This solution has a subquery which selects the pertinent columns from driver_table and also calculates each driver's current age in years. The age is used to derive the last birthday, which is then compared in the main query to the bounds of the insurance term. Because we derive an actual date we can use Oracle's standard date arithmetic so the bdayind is calculated correctly.
SQL> with cte as (
2 select driver_key
3 , date_of_birth
4 , trunc(months_between(sysdate, date_of_birth)/12) as age
5 , add_months(date_of_birth, 12 * (trunc(months_between(sysdate, date_of_birth)/12))) as last_birthday
6 , effective_date
7 , expiry_date
8 from driver_table
9 )
10 select driver_key
11 , date_of_birth as dob
12 , age
13 , effective_date as eff_date
14 , expiry_date as exp_date
15 , last_birthday as last_bday
16 , case
17 when last_birthday between effective_date and expiry_date
18 then 1
19 else 0 end as bdayind
20 from cte
21 /
DRIVER_KEY DOB AGE EFF_DATE EXP_DATE LAST_BDAY BDAYIND
---------- --------- ---- --------- --------- --------- ----------
12 02-APR-98 19 01-DEC-16 31-MAY-17 02-APR-17 1
22 02-APR-98 19 01-JAN-17 30-JUN-17 02-APR-17 1
32 02-SEP-98 18 01-DEC-16 31-MAY-17 02-SEP-16 0
42 02-SEP-98 18 01-JAN-17 30-JUN-17 02-SEP-16 0
SQL>
The subquery produces both age and last_birthday just for demonstration purposes. In real life you only need the last_birthday column.
This solution differs slightly from the others in that:
It works for any birthday between any effective and expiration dates
It accounts for leap years
The raw_data is just setting up the dates for the example:
WITH
raw_data
AS
(SELECT DATE '1963-08-03' AS birthday
, DATE '2017-04-01' AS effectiveday
, DATE '2017-10-31' AS expirationday
, 'Billy' AS name
FROM DUAL
UNION ALL
SELECT DATE '1995-03-20' AS birthday
, DATE '2017-04-01' AS effectiveday
, DATE '2017-10-31' AS expirationday
, 'Sue' AS name
FROM DUAL
UNION ALL
SELECT DATE '1997-01-15' AS birthday
, DATE '2016-12-01' AS effectiveday
, DATE '2017-05-31' AS expirationday
, 'Olga' AS name
FROM DUAL),
mod_data
AS
(SELECT raw_data.*
, ADD_MONTHS (
birthday
, (extract(year from effectiveday) - extract (year from birthday)) * 12
)
effectiveanniversary
, ADD_MONTHS (
birthday
, (extract(year from expirationday) - extract (year from birthday)) * 12
)
expirationanniversary
FROM raw_data)
SELECT name, mod_data.birthday, effectiveday, expirationday
, CASE
WHEN effectiveanniversary BETWEEN effectiveday AND expirationday
OR expirationanniversary BETWEEN effectiveday AND expirationday
THEN
1
ELSE
0
END
found_between
FROM mod_data
NAME BIRTHDAY EFFECTIVEDAY EXPIRATIONDAY FOUND_BETWEEN
Billy 1963/08/03 2017/04/01 2017/10/31 1
Sue 1995/03/20 2017/04/01 2017/10/31 0
Olga 1997/01/15 2016/12/01 2017/05/31 1

SELECT records FROM amount_history WHERE amount1 is l<= max amount2 in last 3 years

I know basic SQL but am trying to come up with a query that is beyond me.
AMOUNT_HISTORY table is something like this
I can add an input parameter for effective date, say effectiveDate.
The highest Amount1 for RefNo 1 in last 3 years is 12,000 which is the <= the Amount2 at the effectiveDate - thats fine.
The highest Amount1 for RefNo 2 in last 3 years is 22,000, which is > than the Amount2 at the effective Date - I need to select the RefNo in that case.
Note the dates go back further, so will need the last 3 dates criteria. There will only be dates on the anniversary of the effectiveDate. Normally I would add the query I have developed thus far, but I didn't get further than a simple Select From Where so not much progress made really. Any help appreciated.
First, you need to select only the rows where the date is between "effective date minus two years" and "effective date". This is done best in the WHERE clause, with a between condition, using add_months() to subtract two years from the effective date. Note - I used dt as column name (date is reserved in Oracle and shouldn't be used as an identifier); and I pass the effective date as a bind variable.
Then, from the remaining rows, group by refno, and compare max(amount1) to the amount2 on the effective date. This is a comparison at the group level, not at the individual row level, so it goes in the HAVING clause, not in the WHERE clause.
Lastly, amount2 on the effective date is at the row level, not at the group level; so we need a little trick. I use a "conditional maximum" - the max() function applied to an expression that is amount2 when the date is the effective date, but null on the other dates. A case expression is perfect for that.
with
test_data ( refno, dt, amount1, amount2 ) as (
select 1, date '2017-01-01', 12000, 12000 from dual union all
select 1, date '2016-01-01', 11000, null from dual union all
select 1, date '2015-01-01', 10500, null from dual union all
select 2, date '2017-01-01', 20000, 10000 from dual union all
select 2, date '2016-01-01', 21000, null from dual union all
select 2, date '2015-01-01', 22000, null from dual
)
-- End of test data (not part of the solution). SQL query begins below this line.
select refno, max(case when dt = :effective_date then amount2 end) as amount2
from test_data
where dt between add_months(:effective_date, -24) and :efffective_date
group by refno
having max(amount1) > max(case when dt = :effective_date then amount2 end)
;
REFNO AMOUNT2
----- -------
2 10000
I can't provide you the direct answer but definitely will help you getting the correct answer.
So, basically you need to learn -
GROUP BY along with aggregate function - http://www.w3schools.com/sql/sql_groupby.asp.
Group by clause group the results by ref no and then use aggregate function MAX
DATE_ADD - http://www.w3schools.com/sql/func_date_add.asp
sql query for getting data for last 3 months
Use DATE_ADD to add the criteria for interval of last 3 years

Oracle order by date gives different results

Just curious as to why the below two queries would give different results, when the testdate column is of datatype DATE.
select testdate from <table> order by to_date(testdate) desc;
Returns
18-DEC-14
17-DEC-14
14-JUL-14
10-JUL-14
.
select testdate from <table> order by testdate desc;
Returns
13-NOV-13
18-DEC-14
17-DEC-14
14-JUL-14
Why would that November 2013 result appear at the top of the second results when as I say the coumn TESTDATE is of data tyle DATE and to_date() resolves the issueX?
Based on OP's reply via comments, the issue is:
SQL> WITH dates(dt) AS(
2 SELECT to_date('18-DEC-2014', 'DD-MON-RR') FROM dual UNION ALL
3 SELECT to_date('17-DEC-2014', 'DD-MON-RR') FROM dual UNION ALL
4 SELECT to_date('14-JUL-2014', 'DD-MON-RR') FROM dual UNION ALL
5 SELECT to_date('10-JUL-2014', 'DD-MON-RR') FROM dual UNION ALL
6 SELECT to_date('13-NOV-3013', 'DD-MON-RR') FROM dual
7 )
8 SELECT dt yy_date,
9 TO_CHAR(dt, 'DD-MON-YYYY') yyyy_date
10 FROM dates
11 ORDER BY dt DESC;
YY_DATE YYYY_DATE
--------- -----------
13-NOV-13 13-NOV-3013
18-DEC-14 18-DEC-2014
17-DEC-14 17-DEC-2014
14-JUL-14 14-JUL-2014
10-JUL-14 10-JUL-2014
SQL>
Th client is displaying the year as YY due to which year 3013 is displayed as 13 and along with other dates 2014, it looked as if 2013 is ordered before 2014 in descending order.
Use TO_CHAR to display the date in your desired format, and use TO_DATE to convert a literal into date.
On a side note,
Never use TO_DATE on a DATE, It will implicitly convert it into string and then back to date using locale-specific NLS format.
TO_DATE function expects string to convert it to date with a given format. Providing date column to to_date may result in unexpected behaviour even oracle does not complain about the given input.

How to determine birth week in current year for 7 day coupon

I am trying to get my customers birth dates, but with the current year(sysdate). For example, John Brown was born in 01-JAN-65, I would like to create a query that retrieves the previous Monday and next Sunday from John's birthday, so that the customer can see that their coupon is valid between that seven day time frame. So for John Brown, the row would have three columns
FullName, Monday Sunday
John Brown 31-DEC-2012 06-JAN-2013
My table definition is:
Table Name - Customers
Columns
Customer_ID
First_Name
Last_Name
DOB
Phone
I have read Oracle Database 11g's chapter 5 "Storing and Processing Dates and Times", but did not find a solution to my problem. Please help, thank you. I will send 5$ to your PayPal email as appreciation for helping me, I am trying to learn.
I have tried to implement code from How do I get the current year using SQL on Oracle? but I keep getting errors
You'll want to make use of the NEXT_DAY function.
with sourceData as (
select 'John Brown' FullName, DATE '1965-01-1' BirthDate
from dual
), BirthDatesThisYear as
(
select
FullName,
Add_Months(BirthDate, (extract(YEAR from sysdate)- extract(YEAR from birthdate))*12) BirthDateThisYear
from sourceData
)
select
FullName,
NEXT_DAY(BirthDateThisYear-8,'MON') PreviousMonday,
NEXT_DAY(BirthDateThisYear-8,'MON')+6 NextSundayFromPrevMonday
from BirthDatesThisYear;
SQL Fiddle for your testing.
One query using Oracle's TO_CHAR, TO_DATE, and NEXT_DAY:
SELECT name,
TO_DATE(TO_CHAR(SYSDATE, 'YYYY') || TO_CHAR(birthdate, 'MMDD'), 'YYYYMMDD') "This Birthday",
NEXT_DAY(TO_DATE(TO_CHAR(SYSDATE, 'YYYY') || TO_CHAR(birthdate, 'MMDD'), 'YYYYMMDD'), 'Sun') "Sunday After",
NEXT_DAY(TO_DATE(TO_CHAR(SYSDATE, 'YYYY') || TO_CHAR(birthdate, 'MMDD'), 'YYYYMMDD'), 'Sun') - 6 "Monday Before"
FROM Table1;
Here's SQL Fiddle with my test data.