Oracle SQL Query for monthwise report - sql

Guys i have a table called A which has columns as
-----
A
-----
S_DATE
DUR
VIEWS
AMOUNT
The data in the table A has the following rows which is DateWise.
20101023 12 1 10000.0
20101101 2 4 200.0
20101114 4 5 -9
20101201 6 10 150.0
20101219 1 12 130.0
My requirement is that i want to write an SQL query that does monthwise *report*. For example from webGUI , when i select on the dropdown for month as DEC and select year as 2010 . I need to create show the sum of DUR,VIEWS and AMOUNT from 1st of November 2010 to 1st of December 2010.Any Ideas on how to do it.
The result for DEC2010 should be like below
**DEC2010 12 19 341**

this would work (although of course due to your requirement the first day of november will be in both the november and december monthly reports ?!):
SQL> VARIABLE p_months VARCHAR2(3);
SQL> VARIABLE p_year NUMBER;
SQL> EXEC :p_months := 'DEC'; :p_year := 2010;
PL/SQL procedure successfully completed.
SQL> WITH tableA AS (
2 SELECT DATE '2010-10-23's_date , 12 dur, 1 views, 10000.0 amount FROM DUAL
3 UNION ALL SELECT DATE '2010-11-01', 2, 4 , 200.0 FROM DUAL
4 UNION ALL SELECT DATE '2010-11-14', 4, 5 , -9 FROM DUAL
5 UNION ALL SELECT DATE '2010-12-01', 6, 10, 150.0 FROM DUAL
6 UNION ALL SELECT DATE '2010-12-19', 1, 12, 130.0 FROM DUAL
7 )
8 SELECT :p_months||:p_year month,
9 SUM(dur),
10 SUM(views),
11 SUM(amount)
12 FROM tableA
13 WHERE s_date >= add_months(to_date(:p_months||:p_year, 'MONYYYY'), -1)
14 AND s_date < to_date(:p_months||:p_year, 'MONYYYY') + 1;
MONTH SUM(DUR) SUM(VIEWS) SUM(AMOUNT)
---------- ---------- ---------- -----------
DEC2010 12 19 341

Related

sql query to get the salary from past 3 years

I have a table salary that has salary details for an employee for various years like:
person_number date_from date_to salary RN
---------------------------------------------------------------------------
272 03-Mar-2022 31-dec-4712 109000 1
272 05-Mar-2021 02-Mar-2022 100000 1
272 10-Mar-2020 04-Mar-2020 100000 1
10 10-Mar-2019 31-dec-4712 4678 1
I want to get the latest salary for past 2 years along with current year. I created the below query for the same -
SELECT *
FROM
(SELECT
person_number
sal.salary_amount,
ROW_NUMBER() OVER (PARTITION BY person_number
ORDER BY date_to DESC) rn,
sal.date_from
FROM
cmp_salary sal
WHERE
1 = 1
AND TO_CHAR(sal.date_from, 'YYYY') = (SELECT TO_CHAR(Add_months(SYSDATE, -12), 'yyyy')
FROM dual)
)
WHERE
rn = 1
Like this one above I have created a clause for all 3 years (just replaced the 12 with 24,36).
These separate query is returning the correct data for 2 years back i.e till 2020 but the only case it is not working in when the same salary exists in 2 years.
Eg- For person #10, the salary is the same from 2019, 2021 and 2022.
Because I am using the year comparison in my above query, it will not return an output for 2020, 2021 and 2022, because the date_From is in 2019. Ideally it should give me the same salary for 2020, 2021 and 2022.
How to tweak this? I have to create 3 separate queries for this.
I don't think you need 3 separate queries; one would suffice. Also, according to data you posted, there's only one row per year per person so it is a "fixed" 3 years back. Therefore, I'd think of something like this instead:
Sample data:
SQL> WITH
2 cmp_salary (person_number,
3 date_from,
4 date_to,
5 salary_amount)
6 AS
7 (SELECT 272, DATE '2022-03-03', DATE '4712-12-31', 109000 FROM DUAL
8 UNION ALL
9 SELECT 272, DATE '2021-03-05', DATE '2022-03-02', 100000 FROM DUAL
10 UNION ALL
11 SELECT 272, DATE '2020-03-10', DATE '2020-03-04', 100000 FROM DUAL
12 UNION ALL
13 SELECT 10, DATE '2019-03-10', DATE '4712-12-31', 4678 FROM DUAL)
Query you might be interested in begins here:
14 SELECT person_number, sal.salary_amount, sal.date_from
15 FROM cmp_salary sal
16 CROSS JOIN
17 TABLE (CAST (MULTISET ( SELECT LEVEL
18 FROM DUAL
19 CONNECT BY LEVEL <= 3) AS SYS.odcinumberlist))
20 WHERE EXTRACT (YEAR FROM sal.date_from) =
21 EXTRACT (YEAR FROM SYSDATE) - COLUMN_VALUE + 1
22 ORDER BY person_number, date_from DESC;
PERSON_NUMBER SALARY_AMOUNT DATE_FROM
------------- ------------- ----------
272 109000 03.03.2022
272 100000 05.03.2021
272 100000 10.03.2020
SQL>

How to count number of records for each week, from last month activity on a table?

I'm working with Oracle and I have a table with a column of type TIMESTAMP. I was wondering how can I extract the records from last 4 weeks of activity on the database, partitioned by week.
Following rows are inserted on week 1
kc 2 04-10-2021
vc 3 06-10-2021
vk 4 07-10-2021
Following rows are inserted on week2
cv 1 12-10-2021
ck 5 14-10-2021
Following rows are inserted on week3
vv 7 19-10-2021
Following rows are inserted on week4
vx 7 29-10-2021
Table now has
SQL>select * from tab;
NAME VALUE TIMESTAMP
-------------------- ----------
kc 2 04-10-2021
vc 3 06-10-2021
vk 4 07-10-2021
cv 1 12-10-2021
ck 5 14-10-2021
vv 7 19-10-2021
vx 7 29-10-2021
I would like a query which would give me the number of rows added each week, in the last 4 weeks.
This is what I would like to see
numofrows week
--------- -----
3 1
2 2
1 3
1 4
One option is to use to_char function and its iw parameter:
SQL> with test (name, datum) as
2 (select 'kc', date '2021-10-04' from dual union all
3 select 'vc', date '2021-10-06' from dual union all
4 select 'vk', date '2021-10-07' from dual union all
5 select 'cv', date '2021-10-12' from dual union all
6 select 'ck', date '2021-10-14' from dual union all
7 select 'vv', date '2021-10-19' from dual union all
8 select 'vx', DATE '2021-10-29' from dual
9 )
10 select to_char(datum, 'iw') week,
11 count(*)
12 from test
13 where datum >= add_months(sysdate, -1) --> the last month
14 group by to_char(datum, 'iw');
WE COUNT(*)
-- ----------
42 1
43 1
40 3
41 2
SQL>
Line #13: I intentionally used "one month" instead of "4 weeks" as I thought (maybe wrongly) that you, actually, want that (you know, "a month has 4 weeks" - not exactly, but close, sometimes not close enough).
If you want 4 weeks, what is that, then? Sysdate minus 28 days (as every week has 7 days)? Then you'd modify line #13 to
where datum >= trunc(sysdate - 4*7)
Or, maybe it is really the last 4 weeks:
SQL> with test (name, datum) as
2 (select 'kc', date '2021-10-04' from dual union all
3 select 'vc', date '2021-10-06' from dual union all
4 select 'vk', date '2021-10-07' from dual union all
5 select 'cv', date '2021-10-12' from dual union all
6 select 'ck', date '2021-10-14' from dual union all
7 select 'vv', date '2021-10-19' from dual union all
8 select 'vx', DATE '2021-10-29' from dual
9 ),
10 temp as
11 (select to_char(datum, 'iw') week,
12 count(*) cnt,
13 row_number() over (order by to_char(datum, 'iw') desc) rn
14 from test
15 group by to_char(datum, 'iw')
16 )
17 select week, cnt
18 from temp
19 where rn <= 4
20 order by week;
WE CNT
-- ----------
40 3
41 2
42 1
43 1
SQL>
Now you have several options, see which one fits the best (if any).
I "simulated" missing data (see TEST CTE), created a calendar (calend) and ... did the job. Read comments within code:
SQL> with test (name, datum) as
2 -- sample data
3 (select 'vv', date '2021-10-19' from dual union all
4 select 'vx', DATE '2021-10-29' from dual
5 ),
6 calend as
7 -- the last 31 days; 4 weeks are included, obviously
8 (select max_datum - level + 1 datum
9 from (select max(a.datum) max_datum from test a)
10 connect by level <= 31
11 ),
12 joined as
13 -- joined TEST and CALEND data
14 (select to_char(c.datum, 'iw') week,
15 t.name
16 from calend c left join test t on t.datum = c.datum
17 ),
18 last4 as
19 -- last 4 weeks
20 (select week, count(name) cnt,
21 row_number() over (order by week desc) rn
22 from joined
23 group by week
24 )
25 select week, cnt
26 from last4
27 where rn <= 4
28 order by week;
WE CNT
-- ----------
40 0
41 0
42 1
43 1
SQL>

Get count days in current year by day name or day id

name id
----------------
Mon 1
Thu 2
Wen 3
Thr 4
Fri 5
Sat 6
San 7
How get count day where id in eg. (1,2,3,4) and year is 2021
The result should be 208.
Actually, it is 208 for year 2021.
SQL> WITH
2 year AS (SELECT &par_year year FROM DUAL),
3 calendar
4 AS
5 ( SELECT TRUNC (TO_DATE (y.year, 'yyyy'), 'yyyy') + LEVEL - 1 datum
6 FROM year y
7 CONNECT BY LEVEL <=
8 ADD_MONTHS (TRUNC (TO_DATE (y.year, 'yyyy'), 'yyyy'), 12)
9 - TRUNC (TO_DATE (y.year, 'yyyy'), 'yyyy'))
10 SELECT SUM (CASE
11 WHEN TO_NUMBER (TO_CHAR (c.datum, 'd')) IN (1,
12 2,
13 3,
14 4)
15 THEN
16 1
17 ELSE
18 0
19 END) result
20 FROM calendar c;
Enter value for par_year: 2021
RESULT
----------
208
SQL> /
Enter value for par_year: 2020
RESULT
----------
210
SQL>
What does it do?
YEAR CTE contains year you're interested in
CALENDAR CTE creates all dates in that particular year
SUM function conditionally adds 1 if TO_CHAR(datum, 'd') is 1, 2, 3 or 4
that's all

Oracle SQL - return the date record when there is no count result

I have the tables below and I need my query to bring me the amount of operations grouped by date.
For the dates on which there will be no operations, I need to return the date anyway with the zero count.
Kind like that:
OPERATION_DATE | COUNT_OPERATION | COUNT_OPERATION2 |
04/06/2019 | 453 | 81 |
05/06/2019 | 0 | 0 |
-- QUERY I TRIED
SELECT
T1.DATE_OPERATION AS DATE_OPERATION,
NVL(T1.COUNT_OPERATION, '0') COUNT_OPERATION,
NVL(T1.COUNT_OPERATION2, '0') COUNT_OPERATIONX,
FROM
(
SELECT
trunc(t.DATE_OPERATION) as DATE_OPERATION,
count(t.ID_OPERATION) AS COUNT_OPERATION,
COUNT(CASE WHEN O.OPERATION_TYPE = 'X' THEN 1 END) COUNT_OPERATIONX,
from OPERATION o
left join OPERATION_TYPE ot on ot.id_operation = o.id_operation
where ot.OPERATION_TYPE in ('X', 'W', 'Z', 'I', 'J', 'V')
and TRUNC(t.DATE_OPERATION) >= to_date('01/06/2019', 'DD-MM-YYYY')
group by trunc(t.DATE_OPERATION)
) T1
-- TABLES
CREATE TABLE OPERATION
( ID_OPERATION NUMBER NOT NULL,
DATE_OPERATION DATE NOT NULL,
VALUE NUMBER NOT NULL )
CREATE TABLE OPERATION_TYPE
( ID_OPERATION NUMBER NOT NULL,
OPERATION_TYPE VARCHAR2(1) NOT NULL,
VALUE NUMBER NOT NULL)
I guess that it is a calendar you need, i.e. a table which contains all dates involved. Otherwise, how can you display something that doesn't exist?
This is what you currently have (I'm using only the operation table; add another one yourself):
SQL> with
2 operation (id_operation, date_operation, value) as
3 (select 1, date '2019-06-01', 100 from dual union all
4 select 2, date '2019-06-01', 200 from dual union all
5 -- 02/06/2019 is missing
6 select 3, date '2019-06-03', 300 from dual union all
7 select 4, date '2019-06-04', 400 from dual
8 )
9 select o.date_operation,
10 count(o.id_operation)
11 from operation o
12 group by o.date_operation
13 order by o.date_operation;
DATE_OPERA COUNT(O.ID_OPERATION)
---------- ---------------------
01/06/2019 2
03/06/2019 1
04/06/2019 1
SQL>
As there are no rows that belong to 02/06/2019, query can't return anything (you already know that).
Therefore, add a calendar. If you already have that table, fine - use it. If not, create one. It is a hierarchical query which adds level to a certain date. I'm using 01/06/2019 as the starting point, creating 5 days (note the connect by clause).
SQL> with
2 operation (id_operation, date_operation, value) as
3 (select 1, date '2019-06-01', 100 from dual union all
4 select 2, date '2019-06-01', 200 from dual union all
5 -- 02/06/2019 is missing
6 select 3, date '2019-06-03', 300 from dual union all
7 select 4, date '2019-06-04', 400 from dual
8 ),
9 dates (datum) as --> this is a calendar
10 (select date '2019-06-01' + level - 1
11 from dual
12 connect by level <= 5
13 )
14 select d.datum,
15 count(o.id_operation)
16 from operation o full outer join dates d on d.datum = o.date_operation
17 group by d.datum
18 order by d.datum;
DATUM COUNT(O.ID_OPERATION)
---------- ---------------------
01/06/2019 2
02/06/2019 0 --> missing in source table
03/06/2019 1
04/06/2019 1
05/06/2019 0 --> missing in source table
SQL>
Probably a better option is to dynamically create a calendar so that it doesn't depend on any hardcoded values, but uses the min(date_operation) to max(date_operation) time span. Here we go:
SQL> with
2 operation (id_operation, date_operation, value) as
3 (select 1, date '2019-06-01', 100 from dual union all
4 select 2, date '2019-06-01', 200 from dual union all
5 -- 02/06/2019 is missing
6 select 3, date '2019-06-03', 300 from dual union all
7 select 4, date '2019-06-04', 400 from dual
8 ),
9 dates (datum) as --> this is a calendar
10 (select x.min_datum + level - 1
11 from (select min(o.date_operation) min_datum,
12 max(o.date_operation) max_datum
13 from operation o
14 ) x
15 connect by level <= x.max_datum - x.min_datum + 1
16 )
17 select d.datum,
18 count(o.id_operation)
19 from operation o full outer join dates d on d.datum = o.date_operation
20 group by d.datum
21 order by d.datum;
DATUM COUNT(O.ID_OPERATION)
---------- ---------------------
01/06/2019 2
02/06/2019 0 --> missing in source table
03/06/2019 1
04/06/2019 1
SQL>

Update entire table with sequence number in oracle

I want to update table with sequence number, I cant create procedure or sequence object so need a update query for this. I have a table with date column, based on minimum date I need to generate a number.
Table Data:-
Date_Value
----------
5th Feb 11
2nd Jan 11
11th Jan 11
After Update :-
SrNo Date_Value
-------------------
1 2nd Jan 11
2 11th Jan 11
3 5th Feb 11
merge into foo
using
(
select rowid as rid,
row_number() over (order by date_value) as seqno
from foo
) t on (foo.rowid = t.rid)
when matched then update
set srno = t.seqno;
SQLFiddle demo: http://sqlfiddle.com/#!4/d8cc5/2
Your example shows that you store dates as a string using custom representation - not compliant with dates format. So we need to convert your dates to date datatype to be able to order them accordingly.
SQL> create table TB_DATES
2 (
3 DATE_VALUE VARCHAR2(11)
4 )
5 /
Table created
SQL>
SQL> insert into tb_dates(date_value)
2 select '5th Feb 11' from dual union all
3 select '2nd Jan 11' from dual union all
4 select '6th Feb 11' from dual union all
5 select '11th Jan 11' from dual
6 ;
4 rows inserted
SQL> commit;
Commit complete
SQL> select * from tb_dates;
DATE_VALUE
-----------
5th Feb 11
2nd Jan 11
6th Feb 11
11th Jan 11
SQL> alter table tb_dates add srno number;
Table altered
QL> select * from tb_dates;
DATE_VALUE SRNO
----------- ----------
5th Feb 11 null
2nd Jan 11 null
6th Feb 11 null
11th Jan 11 null
SQL> merge into tb_dates t
2 using(
3 select rownum rn
4 , Date_value
5 , rid
6 from ( select to_date(concat(lpad(dy, 2, '0'), tr), 'dd Mon yy') dt
7 , Date_value
8 , rid
9 from (select regexp_substr(substr(Date_value, 1, regexp_instr(date_value, '[[:space:]]') - 1), '[[:digit:]]+') dy
10 , substr(Date_value, regexp_instr(date_value, '[[:space:]]'), length(Date_value)) tr
11 , Date_value
12 , rowid rid
13 from tb_dates)
14 order by 1
15 ) ) q
16 on (q.rid = t.rowid)
17 when matched
18 then update set t.srno = q.rn
19 ;
Done
SQL> commit;
Commit complete
select * from tb_dates;
DATE_VALUE SRNO
----------- ----------
5th Feb 11 3
2nd Jan 11 1
6th Feb 11 4
11th Jan 11 2
SQL> select * from tb_dates order by srno;
DATE_VALUE SRNO
----------- ----------
2nd Jan 11 1
11th Jan 11 2
5th Feb 11 3
6th Feb 11 4