I have 2 tables. First table TABLE1 has 3 columns (ROLLNO, CODE, ADM_DT).
CREATE TABLE TABLE1
( ROLLNO VARCHAR2(3) NOT NULL,
CODE VARCHAR2(3) NOT NULL,
ADM_DT TIMESTAMP(3) NOT NULL
);
Second table TABLE2 has 3 columns (CODE, YEAR).
CREATE TABLE TABLE2
(
CODE VARCHAR2(3) NOT NULL,
YEAR VARCHAR2(4) NOT NULL
);
TABLE1 has few records mentioned below:
INSERT INTO TABLE1(ROLLNO, CODE, ADM_DT) VALUES ('100','ABC', '12-NOV-21 12.00.00.000000000 AM');
INSERT INTO TABLE1(ROLLNO, CODE, ADM_DT) VALUES ('101','ACD', '12-DEC-21 12.00.00.000000000 AM');
INSERT INTO TABLE1(ROLLNO, CODE, ADM_DT) VALUES ('102','ABD', '15-JAN-21 12.00.00.000000000 AM');
INSERT INTO TABLE1(ROLLNO, CODE, ADM_DT) VALUES ('103','DEF', '14-AUG-21 12.00.00.000000000 AM');
INSERT INTO TABLE1(ROLLNO, CODE, ADM_DT) VALUES ('104','DFE', '17-JUL-21 12.00.00.000000000 AM');
INSERT INTO TABLE1(ROLLNO, CODE, ADM_DT) VALUES ('105','FED', '21-SEP-21 12.00.00.000000000 AM');
ROLLNO CODE ADM_DT
-----------------------------------
100 ABC 12-NOV-21 12.00.00.000000000 AM
101 ACD 12-DEC-21 12.00.00.000000000 AM
102 ABD 15-JAN-21 12.00.00.000000000 AM
103 DEF 14-AUG-21 12.00.00.000000000 AM
104 DFE 17-JUL-21 12.00.00.000000000 AM
105 FED 21-SEP-21 12.00.00.000000000 AM
TABLE2 has few records mentioned below:
INSERT INTO TABLE2(CODE, YEAR) VALUES ('ABC','2022');
INSERT INTO TABLE2(CODE, YEAR) VALUES ('ADC','2022');
INSERT INTO TABLE2(CODE, YEAR) VALUES ('DEF','2021');
INSERT INTO TABLE2(CODE, YEAR) VALUES ('DFE','2021');
CODE YEAR
-------------
ABC 2022
ADC 2022
DEF 2021
DFE 2021
I need to select the records from TABLE1 in such a way that
1) fetch the month present in ADM_DT value and if month is between Sep and Dec and also the year, then check the CODE value present in YEAR+1 of TABLE2, if present then don't select the record from TABLE1 else select.
Ex: Let us take a record of ROLLNO = '102' and CODE is 'ABC' and it has ADM_DT as '12-NOV-21 12.00.00.00.000000000 AM' so here month is November and the year is 2021, so check whether CODE 'ABC' present in TABLE2 with YEAR '2022'...code 'ABC' present in TABLE2, then don't select this record from TABLE1.
2) fetch the month present in ADM_DT value and if month is between Jan and Aug, then check the CODE value present in YEAR of TABLE2, if present then don't select the record from TABLE1 else select.
Ex: Let us take a record of ROLLNO = '103' and CODE is 'DEF' and it has ADM_DT as '14-AUG-21 12.00.00.00.000000000 AM' so here month is August and the year is 2021 so check whether CODE 'DEF' present in TABLE2 with YEAR '2021'...code 'DEF' present in TABLE2, then don't select this record from TABLE1.
This is the Result table I need:
ROLLNO CODE ADM_DT
------------------------------------------------
101 ACD 12-DEC-21 12.00.00.00.000000000 AM
102 ABD 15-JAN-21 12.00.00.00.000000000 AM
105 FED 21-SEP-21 12.00.00.00.000000000 AM
This is the Result table I am getting:
ROLLNO CODE ADM_DT
------------------------------------------------
100 ABC 12-NOV-21 12.00.00.000000000 AM (This shouldn't get picked up as this record has Month as Nov and year as 2021, so we need to go to the TABLE2 and check whether we have CODE 'ABC' in the YEAR '2022', its present so it shouldn't pick up)
101 ACD 12-DEC-21 12.00.00.000000000 AM
102 ABD 15-JAN-21 12.00.00.000000000 AM
105 FED 21-SEP-21 12.00.00.000000000 AM
This is the query I have tried:
SELECT * FROM TABLE1 T1
WHERE NOT EXISTS (
SELECT 1 FROM TABLE2 T2
WHERE T1.CODE = T2.CODE
AND EXTRACT(YEAR FROM T1.ADM_DT) =
CASE
WHEN EXTRACT(MONTH FROM(T1.ADM_DT)) BETWEEN 9 AND 12 THEN TO_NUMBER(T2.YEAR)+1
WHEN EXTRACT(MONTH FROM(T1.ADM_DT)) BETWEEN 1 AND 8 THEN TO_NUMBER(T2.YEAR)
END
);
Is this correct query? because I have executed this query and it gave me wrong result...might be issue with YEAR logic I have written in query (Need to use NOT EXISTS query).
You can create a range from the table2.year rather than trying to match the years based on the month in table1.adm_dt. This has the added bonus that Oracle can use an index on the table1.adm_dt column whereas your query would not use an index and would need separate function-based indexes for each of the EXTRACT functions for MONTH and YEAR.
Either:
SELECT t1.*
FROM table1 T1
LEFT OUTER JOIN table2 T2
ON (t1.code = t2.code
AND t1.adm_dt >= TO_DATE((t2.year - 1) || '-09-01', 'YYYY-MM-DD')
AND t1.adm_dt < TO_DATE(t2.year || '-09-01', 'YYYY-MM-DD')
)
WHERE t2.code IS NULL;
or:
SELECT *
FROM table1 T1
WHERE NOT EXISTS (
SELECT 1
FROM table2 T2
WHERE t1.code = t2.code
AND t1.adm_dt >= TO_DATE((t2.year - 1) || '-09-01', 'YYYY-MM-DD')
AND t1.adm_dt < TO_DATE(t2.year || '-09-01', 'YYYY-MM-DD')
);
Which, for your sample data, both output:
ROLLNO
CODE
ADM_DT
102
ABD
15-JAN-21 00.00.00.000
105
FED
21-SEP-21 00.00.00.000
101
ACD
12-DEC-21 00.00.00.000
db<>fiddle here
Related
[I have a table T1 with 2 columns NAME & DT as shown in figure
CREATE TABLE T1 ( NAME VARCHAR2(1), DT TIMESTAMP(3) );
INSERT INTO T1 VALUES ( ‘A’ , TO_TIMESTAMP(‘2021-02-04 12:00:00.000000000’, ‘YYYY-MM-DD HH24:MI:SS.FF’));
INSERT INTO T1 VALUES ( ‘B’ , TO_TIMESTAMP(‘2021-02-05 12:00:00.000000000’, ‘YYYY-MM-DD HH24:MI:SS.FF’));
INSERT INTO T1 VALUES ( ‘C’ , TO_TIMESTAMP(‘2021-02-15 12:00:00.000000000’, ‘YYYY-MM-DD HH24:MI:SS.FF’));
After executing above query, data gets populated and I want to select the records from DT from 1st jan till today of this year.
I have created a procedure as shown below such that if I run the same procedure next year, it needs to select the records from 1st jan till sysdate of that particular year i.e. here year changes..I have created a variable for the year and assigned current year to it..In next year, year variable will be 2022 etc..
How should I query it in WHERE clause to select the records from 1st Jan till sysdate of particular year (i.e. 2021/2022/2023 so on)..and you can see DT format is Timestamp
CREATE PROCEDURE P1
AS
V_YEAR VARCHAR2(4); -- year variable
V_YEAR := TO_CHAR((SYSDATE), ‘YYYY); -- Assigning current year to this variable
BEGIN
SELECT * FROM T1 WHERE DT BETWEEN TO_DATE(‘01/01/2021’, ‘mm/dd/yyyy’) AND TO_DATE(SYSDATE, ‘mm/dd/yyyy’);
END;
/]
1
You can use trunc in your query as follows:
DT BETWEEN TRUNC(SYSDATE,'YYYY') AND SYSDATE;
TRUNC(SYSDATE,'YYYY') returns the starting day of the year
Yet another option would be
WHERE TRUNC(DT, 'YEAR') = TRUNC(SYSDATE, 'YEAR');
If you wanted to get everything in the current year
[I have created a table as shown in figure
CREATE TABLE TABLE1 (CAL_YEAR VARCHAR2(4), NAME VARCHAR2(1))
INSERT INTO TABLE1 VALUES(‘2020’, ’A’)
INSERT INTO TABLE1 VALUES(‘2020’, ’B’)
INSERT INTO TABLE1 VALUES(‘2020’, ’C’)
INSERT INTO TABLE1 VALUES(‘2020’, ’D’)
INSERT INTO TABLE1 VALUES(‘2021’, ’E’)
INSERT INTO TABLE1 VALUES(‘2021’, ’F’)
Let us assume I am querying the statement in this year like
SELECT * FROM TABLE1 WHERE CAL_YEAR= TO_CHAR((SYSDATE),'YYYY');
so I get present year values.
What I need is if the present year data not present in the table then I need to use previous year data i.e. 2020 data...How can I do this in the where clause itself]1
You can simply get the maximum year that is less than or equal the year you query for in a subquery.
SELECT *
FROM table1
WHERE cal_year = (SELECT max(cal_year)
FROM table1
WHERE cal_year <= 2020);
You want to show the previous year, if no row for the current year exists? Simply select all years until now, order by year, take the last one.
SELECT *
FROM table1
WHERE cal_year <= EXTRACT(YEAR FROM SYSDATE)
ORDER BY cal_year DESC
FETCH FIRST ROW WITH TIES;
Optionally (if you're on a database version which doesn't support FETCH FIRST ROW WITH TIES Thorsten suggested):
with year 2021 in the table:
SQL> select * from table1
2 where cal_year = case when (select max(1) from table1
3 where exists (select null from table1
4 where cal_year = to_char(sysdate, 'yyyy')
5 )
6 ) = 1 then to_char(sysdate, 'yyyy')
7 else to_char(add_months(sysdate, -12), 'yyyy')
8 end;
CAL_ N
---- -
2021 E
2021 F
Without this year:
SQL> delete from table1 where cal_year = 2021;
2 rows deleted.
SQL>
SQL> select * from table1
2 where cal_year = case when (select max(1) from table1
3 where exists (select null from table1
4 where cal_year = to_char(sysdate, 'yyyy')
5 )
6 ) = 1 then to_char(sysdate, 'yyyy')
7 else to_char(add_months(sysdate, -12), 'yyyy')
8 end;
CAL_ N
---- -
2020 A
2020 B
2020 C
2020 D
SQL>
What does it do?
lines #2 - 5: SELECT* (within CASE) returns 1 if EXISTS says that there's at least one row whose CAL_YEAR = this year
if that's so (i.e. 1 has been returned), then CAL_YEAR is compared to this year (to_char(sysdate, 'yyyy'))
otherwise, if SELECT returns something else (most probably NULL), CAL_YEAR is compared to previous year (that's what to_char(add_months(sysdate, -12), 'yyyy') does - subtracts 12 months from today's date and extracts year from it)
I am working on analyzing huge set of data over a year. The approach is to pick the data one day at a time with the help of a cursor and keep on feeding another table with whole year data :-
declare
i_start_date date := date '2019-04-01';
i_end_date date := date '2019-04-02';
begin
for cur_r in (select a.id, b.status
from table1 a join table2 b on a.msg_id = b.msg_id
where b.t_date between i_start_date and i_end_date
)
loop
insert into test_table (id, status)
values (cur_r.id, cur_r.status);
end loop;
end;
/
Could you please help me run this cursor in a PL/SQL block for the whole year with error handling (e.g:- if data is already there for Apr 01 it should not be inserted again in the table creating no duplicates)
Something like below:-
declare
i_start_date date := date '2019-01-01'; --start date set
i_end_date date := date '2019-12-31'; --end date set
begin
for i_start_date<=i_end_date --condition to fetch data & insert
(for cur_r in (select a.id, b.status
from table1 a join table2 b on a.msg_id = b.msg_id
where b.t_date = i_start_date
)
loop
insert into test_table (id, status)
values (cur_r.id, cur_r.status);
end loop;)
i_start_date+1 -- increment start date
end;
/
Thanks,
Why do you even need pl/sql?
insert into test_table (id,
status
)
values (select a.id,
b.status
from table1 a
join table2 b on a.msg_id = b.msg_id
where b.t_date between date '2019-04-01
and date '2019-04-02'
and b.t_date not in (select t_date
from status)
;
But beware in your comparison of DATEs (which I have simply replicated) that oracle DATE always includes a time component, and the above comparison will truncate your supplied dates to midnight. Thus, a row with b.t_date = to_date('2019-04-02 09:10:11','yyyy-mm-dd') will not be selected.
If you have a Primary Key with the date value you can handle the exception with dup_val_on_index and then use a return.
BEGIN
...
EXCEPTION
WHEN DUP_VAL_ON_INDEX THEN
...
RETURN;
END;
Or you can use a MERGE to command when to insert or not.
MERGE INTO TEST_TABLE T
USING CUR_R C
ON (C.DATE = T.DATE)
WHEN NOT MATCHED THEN
INSERT (id, status)
values (cur_r.id, cur_r.status);
You can directly use insert into <table> select ... statement as
SQL> insert into test_table
select a.id, b.status
from table1 a
join table2 b
on a.msg_id = b.msg_id
where b.t_date >= trunc(sysdate) - interval '1' year
and not exists ( select 0 from test_table t where t.id = a.id );
SQL> commit;
through use of b.t_date >= trunc(sysdate) - interval '1' year starting from the one year before to the current day.
If you need to start with a certain date such as date'2019-04-01' and scan for upcoming one year period,
then use b.t_date between date'2019-04-01' and date'2019-04-01' + interval '1' year - 1
and exclude the already existing data within the test_table through
not exists ( select 0 from test_table t where t.id = a.id ) considering those id columns are unique or primary keys in their respective tables.
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.
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