Unique rows in oracle 11g - oracle

I have a query which returns a set of records as like the one below:-
Date Dept commission
5-Apr Sales 20
4-Apr Sales 21
1-Jan Marketing 35
case 1: If i run a query between 1 Jan and 5 april I should get
Date Dept commission
5 April Sales 76
case 2: and when I run the query between jan 1 and jan 31 should get the output as
Date Dept commission
1 Jan Marketing 35
Case 2 is simple as when i put hte date range getting the required results , but not sure how to handle case 1 to show the max / latest date , the Dept for that date and a sum of the commission for that Dept , date for the selected date range . The output will be a single row with the latest date and department with a sum(commission) for the selected date range.

SELECT
MAX(Date) AS Date
, ( SELECT tt.Dept
FROM tableX tt
WHERE tt.Date = MAX(t.Date)
) AS Dept
, SUM(Commission) AS Commission
FROM
tableX t
WHERE
Date BETWEEN StartDate AND EndDate
The above works in SQL-Server, MySQL, Postgres as the sql-fiddle, test-1 shows, however it does NOT work in Oracle 11g R2 !
This works though (sql-fiddle, test-2):
SELECT
MAX(t.Date) AS Date
, MIN(tt.Dept) AS Dept --- MIN, MAX irrelevant
, SUM(t.Commission) AS Commission
FROM
( SELECT
MAX(Date) AS Date
, SUM(Commission) AS Commission
FROM
tableX
WHERE
Date BETWEEN StartDate AND EndDate
) t
JOIN
tableX tt
ON tt.Date = t.Date
The MIN(tt.Dept) is used to take care of the case you have more than row with the maximum date, say one row with Sales and one with Marketing, both in Apr-5
This works, too, using the LAST_VALUE analytic function (sql-fiddle, test-3):
SELECT
MAX(Date) AS Date
, MIN(Dept) AS Dept
, SUM(Commission) AS Commission
FROM
( SELECT
Date AS Date
, LAST_VALUE(Dept) OVER( ORDER BY Date
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
) AS Dept
, Commission AS Commission
FROM
tableX
WHERE
Date BETWEEN StartDate AND EndDate
) t

Related

Oracle changing a date of birth to age

I need to convert a date of birth to current age in years using a column Dob in date format and system date but populate the result in the dob column.
The date format is dd-mm-yy.
I have tried using to_char and to_number functions to subtract from sys date
Assuming that you only need years and that your dob column is a date, you may need:
select dob, floor(months_between(sysdate, dob) / 12) age
from yourTable
For example:
with yourTable(dob) as (
select date '2000-02-01' from dual union all
select date '2000-02-02' from dual union all
select date '2000-01-31' from dual
)
select dob, floor(months_between(sysdate, dob) / 12) age
from yourTable
gives:
DOB AGE
---------- ----------
2000-02-01 18
2000-02-02 17
2000-01-31 18
3 rows selected.
If you need years, months and days for the date_of_birth column. You can do the below.
select sysdate,
date_of_birth,
trunc(months_between(sysdate,date_of_birth) / 12) as years,
trunc(months_between(sysdate,date_of_birth) -
(trunc(months_between(sysdate,date_of_birth) / 12) * 12)) as months,
trunc(sysdate) - add_months(date_of_birth, trunc(months_between(sysdate,date_of_birth))) as days
from table_name;

Check whether a date exist in a specific month

Here are some sample rows from my ATTENDANCE table
EMP_NO RECORD_DATE CLOCKIN CLOCKOUT
361 09-AUG-16 08:34:17 16:50:17
361 11-AUG-16 09:09:22 17:32:45
361 15-AUG-16 08:56:09 16:54:42
361 22-AUG-16 08:21:58 16:54:43
361 24-AUG-16 08:29:54 17:02:35
361 26-AUG-16 08:46:42 19:02:40
361 29-AUG-16 08:56:15 19:00:52
361 31-AUG-16 08:31:31 19:00:38
I need to generate an absenteeism report and so I need to find all the dates for a specific employee which doesn't exist in the calendar month.
I thought about following.
SELECT MY_DATE FROM table_FOO
where MY_DATE NOT IN (SELECT RECORD_DATE FROM attendance WHERE emp_no='361')
Is there any way to call all the dates into a column (here my_date in table_FOO) and it should be matched to the month.
Kindly seek u r help
To fill your table_foo you can use loop:
declare
d date;
begin
d := to_date('01-01-2016','dd-mm-yyyy');
for i in 0..364 loop
insert into table_foo values (d + i);
end loop;
commit;
end;
Example code will fill all days for current year.
Assuming data type of column RECORD_DATE and MY_DATE is DATE or TIMESTAMP (if this is not the case, then you should consider to change that) the query can be as this:
SELECT MY_DATE
FROM table_FOO
where TRUNC(MY_DATE, 'MM') NOT IN
(SELECT TRUNC(RECORD_DATE, 'MM') FROM attendance WHERE emp_no='361')
Assuming the table table_foo and attandence date column have same datatype you can use the below query to create our attendence report.
SELECT MY_DATE
FROM table_FOO
WHERE NOT EXISTS (SELECT 1
FROM attendance
WHERE emp_no = '361'
and record_date = MY_DATE
and to_char(record_date,'mm') = <input_month_number > );
Here input_month_number can be 1 for Jan and so on..12 for Dec.

Oracle Max Timetamp- subtract 45 minutes

I would like to build a query, which returns all records 45 Minutes before the max timestamp.
For example the record with the latest timestamp is:
01.09.2013 11:00:00
Now I would like to have all records from
01.09.2013 10:15:00 to 11:00:00
You can accomplish this by using Max aggregate/analytic function and interval statement:
Here is an example:
select col
from ( select col
, max(col) over() as max_time
from t1) t
where t.col between t.max_time - interval '45' minute
and t.max_time
Result:
Col
--------------------
01.09.13 11:00:00 AM
01.09.13 10:45:00 AM
01.09.13 10:30:00 AM
01.09.13 10:15:00 AM
SQLFiddle Demo
with cte as
(select max(the_timestamp_field) the_timestamp_field
from test)
select
*
from
test,cte
where
test.the_timestamp_field between cte.the_timestamp_field - (1/24*.75)
and cte.the_timestamp_field
will do it.
It can be simple and a little complicated, depending on how do you want to determine the creation time of the record. If there is a date column in your table which indicates the creation time it's simple:
SELECT columns
FROM table
WHERE date_column BETWEEN TO_DATE ('01.09.2013 10:15', 'MM.DD.YYYY HH24:MI') AND
TO_DATE('01.09.2013 11:00', 'MM.DD.YYYY HH24:MI')
If there is no date column you can use SCN_TO_TIMESTAMP(ORA_ROWSCN) pseudo column to determine the creation time, but please note that this feture requires 10g or greater version.

How to find the difference between two timestamp column

I have table name Record which has the following columns, Empid in number column, dat in timestamp
Which has the following values
empid dat
====== ====
101 4/9/2012 9:48:54 AM
101 4/9/2012 9:36:28 AM
101 4/9/2012 6:16:28 PM
101 4/10/2012 9:33:48 AM
101 4/10/2012 12:36:28 PM
101 4/10/2012 8:36:12 PM
101 4/11/2012 9:36:28 AM
101 4/11/2012 4:36:22 PM
Here I need to display the following columns,
empid,min(dat) as start,max(dat) as end and difference(max(dat)-min(dat) for each day
Here 3 different days are exists so It should return 3 records with the above mentioned columns.
Please give some ways to get this.
Simply subtract them: max(dat) - min(dat)
SELECT empid,
min(dat) as strt,
max(dat) as end,
max(dat) - min(dat) as diff
FROM the_table
GROUP BY empid;
If you want to group by the day instead of the empid, use this one:
select trunc(dat),
min(dat) as strt,
max(dat) as end,
max(dat) - min(dat) as diff
from the_table
group by trunc(dat)
Date arithmetic is pretty straightforward in Oracle: the difference betwwen two dates is returned as the number of days. Values of less than a day are returned as *decimals". That is, 75 minutes is 1.25 hours not 1.15. If you want it as hours and minutes you need to work with an interval.
The inner query calculates the difference between the minimum and maximum data for each employee for each day, and converts it to a DAY interval. The outer query extracts the HOUR and MINUTES from that interval.
select empid
, tday
, sdt
, edt
, extract(hour from diff) diff_hours
, extract (minute from diff) diff_minutes
from (
select empid
, trunc(dat) tday
, min(dat) sdt
, max(dat) edt
, numtodsinterval(max(dat) - min(dat), 'DAY') diff
from t42
group by empid, trunc(dat)
)

Grouping by Fiscal Year (Oracle)

Is there a way in Oracle that can pull the FY? I used the script below to pull just two FY. Mytable date range is from FY1998 to FY2009.
SELECT 'FY2008' as FY,
Site,
COUNT(*)
FROM mytable
WHERE date >='10-OCT-2007'
AND date <'10-OCT-2008'
GROUP BY site
SELECT 'FY2008' as FY,
Site,
COUNT(*)
FROM mytable
WHERE date >='10-OCT-2008'
AND date <'10-OCT-2009'
GROUP BY site
Pulling two FY is OK but it's too much repeatative when pulling more than 10 FY.
Add 83 days to your date and truncate it to whole year:
select 'FY'||TRUNC(date + 83, 'YYYY') as FY, Site, count(*)
from mytable
group by 'FY'||TRUNC(date + 83, 'YYYY'), site
Assuming Oracle 9i+, use a CASE expression:
SELECT CASE
WHEN TO_CHAR(t.date, ) = 10 AND EXTRACT(DAY FROM t.date) >= 10 THEN
'FY' || EXTRACT(YEAR FROM t.date) + 1
WHEN TO_CHAR(t.date, ) > 10 THEN
'FY' || EXTRACT(YEAR FROM t.date) + 1
ELSE
'FY' || EXTRACT(YEAR FROM t.date)
END AS FY,
t.site,
COUNT(*)
FROM YOUR_TABLE t
GROUP BY t.site, FY
And for completeness, in addition to #eumiro answer. In countries (such as Australia) which have a financial year running from 1 July to 30 June, you can replace the 83 with 184.
A few options:
You can use the to_char function here. Check this link for an explanation:
http://www.techonthenet.com/oracle/functions/to_char.php
You may also try using a case statement
select case when date >='10-OCT-2007' and date <'10-OCT-2008' then 'FY08'
when date >='10-OCT-2008' and date <'10-OCT-2009' then 'FY09'
else 'Other' end as fiscal_year, count(*)
from mytable
group by case when date >='10-OCT-2007' and date <'10-OCT-2008' then 'FY08'
when date >='10-OCT-2008' and date <'10-OCT-2009' then 'FY09'
else 'Other' end
Ultimately, if you have create table privileges you may want to consider making a date lookup table. Search for "date dimension" in data warehousing guides.
For example:
Your table would have
date, date_desc, fiscal_year, etc....
then you could just join and group by fiscal year, or whatever else you want.
Here is another way to easily determine the Fiscal Year of a date for those who's Fiscal Year runs from July to June:
SELECT 'FY'||TO_CHAR(ROUND(your_date_here,'YEAR'),'YY') AS FY

Resources