Quarterly breakdown by month - oracle

I'm working in oracle db.
I have a table rent
Me need to create a procedure that will display the column count by month and quarter. Need result like:
month_1 - x
month_2 - y
month_3 - z
quarter - q
I'm create this procedure
create or replace procedure p_money
(c_id in out rent.car_id %TYPE,
RS in out rent.rent_start %TYPE,
RE in out rent.rent_start %TYPE,
v_result IN out sys_refcursor)
as
begin
open v_result for
select sum (money) "TOTAL"
from rent
where c_id = rent.car_id and rent_start between RS and RE and rent_end between RS and RE
group by rollup (money);
end p_money;

Hopefully this simple query will give you some idea
with
sample_data as (
select sysdate + dbms_random.value(1,500) dt,
round(dbms_random.value(100,300),2) money
from dual
connect by level < 1000
),
enriched_data as (
select dt,
trunc(dt,'q') q,
trunc(dt,'mm') m,
money
from sample_data
)
select q,m, count(*), sum(money)
from enriched_data
group by rollup (q,m);
sample_data is just some random sample data in a form of (dt, money)
in the enriched_data we calculate month and quarter for each dt
finally, we group this data using group by rollup (q,m) and receive the subtotals

Related

between function and year variable in oracle

[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

How to use complex condition in insert into select query in oracle

I want to insert some records with insert into select query in oracle. My condition should be when month of CREATE_DATE in SITA_HOSTS table is equal with month of sysdate - 1. Now my question is what should I write for the where statement?
This is my code:
DECLARE
p_year VARCHAR2(50);
n_year NUMBER;
n_month NUMBER;
j_year VARCHAR2(4);
j_month VARCHAR2(4);
c_month NUMBER;
BEGIN
SELECT TO_CHAR(sysdate, 'YYYY-MM-DD','nls_calendar=persian') INTO p_year FROM dual; --Change sysdate to jalali date
SELECT regexp_substr(p_year,'[^-]+', 1, 1) INTO j_year
FROM dual; -- Get year of jalalian sysdate
SELECT regexp_substr(p_year,'[^-]+', 1, 2) INTO j_month
FROM dual;--Get month of jalalian sysdate
n_year := TO_NUMBER(j_year);
n_month := TO_NUMBER(j_month);
insert into sita_orders(REL_MODULE,REL_ID,CREATE_DATE,AMOUNT,CUSTOMER_ID,PROJECT_ID,ORDER_STATUS,TITLE,YEAR)
SELECT 1,ID,sysdate,78787878,CUSTOMER_ID,PROJECT_ID,0,HOSTING_TITLE,j_year
FROM SITA_HOSTS
WHERE ????;
END;
At the end I should say that my date is Jalali date
Here is one way:
WHERE TRUNC(create_date,'MM') = ADD_MONTHS(TRUNC(SYSDATE,'MM'), -1)
TRUNC(date, 'MM') truncates to midnight on the first day of the month of the date.
It really depends on the content/meaning and data type of create_date within sita-hosts table. In addition to that is the requirement also unclear. Shall the insert also cover hosts that were created a couple of years ago or only the ones created last month.
solution for the hosts created during the last month. With trying to enforce the usage of indexes if there are some.
select <your select list>
from sita_hosts
where create_date between add_months(trunc(sysdate,'mm')-1) and trunc(sysdate,'mm')
and create_date < trunc(sysdate,'mm')
the second where clause will exclude all times that are on the start of this month just at midnight.
Thanks a lot guys. I have written this code and it works fine:
insert into sita_orders(REL_MODULE,REL_ID,CREATE_DATE,AMOUNT,CUSTOMER_ID,PROJECT_ID,ORDER_STATUS,TITLE,YEAR)
SELECT 1,ID,sysdate,100000,CUSTOMER_ID,PROJECT_ID,0,HOSTING_TITLE,TO_CHAR(sysdate, 'YYYY','nls_calendar=persian')
FROM SITA_HOSTS
WHERE
to_number(TO_CHAR(CREATE_DATE, 'MM','nls_calendar=persian')) <= to_number(TO_CHAR(sysdate, 'MM','nls_calendar=persian') - 1)
and ID not in (
select REL_ID from sita_orders where REL_MODULE=1 and YEAR=TO_CHAR(sysdate, 'YYYY','nls_calendar=persian')
);

Oracle PL/SQL update based on values calculated from multiple tables

I need to write a PL/SQL that sums up the value from 2 tables and update the value to another table.
I have the following tables: ONLINE_SALES, STORE_SALES, TOTAL_SALES
Assume the tables are structured like this:
ONLINE_SALES: OS_ID, STORE_ID, SEQ, ITEM_NAME, PRICE, PURCHASED_DATE
STORE_SALES: SS_ID, STORE_ID, SEQ, ITEM_NAME, PRICE, PURCHASED_DATE
TOTAL_SALES: STORE_ID, YEAR, TOTAL_INCOME
I want to write a PL/SQL that runs monthly and sums up the income (values in PRICE field) made in the month from both ONLINE_SALES and STORE_SALES of each store (identified by STORE_ID) and add the value to record in TOTAL_SALES with relative year.
My idea is to first filter the record by PURCHASED_DATE from both table with a SELECT, loop through all selected rows and sum to a variable and at last update the result with an UPDATE. But I am stuck in the first step since I found that I cannot use SELECT and only SELECT INTO is available.
Any ideas on how such PL/SQL can be written?
This solution assumes your stored procedure takes a DATE which it uses to identify the month and the year which is being totalled. Perhaps your assignment is expecting a different form of input? No worries. The deriving the values of year and date range is separate from the main process, so it is easy to swap in some different logic.
The loop uses an explicit cursor with the FOR UPDATE syntax. This locks the TOTAL_SALES table, which means you are guaranteed to be able to update all the rows.
create or replace calc_total_sales
( p_month in date )
is
cursor c_tot_sales (p_year number) is
select * from total_sales
where year = p_year
for update of total_income;
c_tot_sales c_tot_sales%rowtype;
l_os_sales number;
l_ss_sales number;
l_year number;
1_first_day date;
1_last_day date;
begin
l_year := to_number( to_char( p_month, 'yyyy') );
l_first_day := trunc( p_month, 'mm');
l_last_day := last_day( p_month);
open c_tot_sales( l_year );
loop
fetch c_tot_sales into r_tot_sales;
exit when c_tot_sales%not found;
select sum(price)
into l_os_sales
from online_sales
where store_id = r_tot_sales.store_id
and purchased_date >= l_first_day
and purchased_date <= l_last_day;
select sum(price)
into l_ss_sales
from store_sales
where store_id = r_tot_sales.store_id
and purchased_date >= l_first_day
and purchased_date <= l_last_day;
update total_sales
set total_income = total_income + nvl(l_ss_sales,0) + nvl(l_os_sales,0)
where current of c_total_sales;
end loop;
close c_tot_sales;
commit;
end;
/

Finding a count of rows in an arbitrary date range using Oracle

The question I need to answer is this "What is the maximum number of page requests we have ever received in a 60 minute period?"
I have a table that looks similar to this:
date_page_requested date;
page varchar(80);
I'm looking for the MAX count of rows in any 60 minute timeslice.
I thought analytic functions might get me there but so far I'm drawing a blank.
I would love a pointer in the right direction.
You have some options in the answer that will work, here is one that uses Oracle's "Windowing Functions with Logical Offset" feature instead of joins or correlated subqueries.
First the test table:
Wrote file afiedt.buf
1 create table t pctfree 0 nologging as
2 select date '2011-09-15' + level / (24 * 4) as date_page_requested
3 from dual
4* connect by level <= (24 * 4)
SQL> /
Table created.
SQL> insert into t values (to_date('2011-09-15 11:11:11', 'YYYY-MM-DD HH24:Mi:SS'));
1 row created.
SQL> commit;
Commit complete.
T now contains a row every quarter hour for a day with one additional row at 11:11:11 AM. The query preceeds in three steps. Step 1 is to, for every row, get the number of rows that come within the next hour after the time of the row:
1 with x as (select date_page_requested
2 , count(*) over (order by date_page_requested
3 range between current row
4 and interval '1' hour following) as hour_count
5 from t)
Then assign the ordering by hour_count:
6 , y as (select date_page_requested
7 , hour_count
8 , row_number() over (order by hour_count desc, date_page_requested asc) as rn
9 from x)
And finally select the earliest row that has the greatest number of following rows.
10 select to_char(date_page_requested, 'YYYY-MM-DD HH24:Mi:SS')
11 , hour_count
12 from y
13* where rn = 1
If multiple 60 minute windows tie in hour count, the above will only give you the first window.
This should give you what you need, the first row returned should have
the hour with the highest number of pages.
select number_of_pages
,hour_requested
from (select to_char(date_page_requested,'dd/mm/yyyy hh') hour_requested
,count(*) number_of_pages
from pages
group by to_char(date_page_requested,'dd/mm/yyyy hh')) p
order by number_of_pages
How about something like this?
SELECT TOP 1
ranges.date_start,
COUNT(data.page) AS Tally
FROM (SELECT DISTINCT
date_page_requested AS date_start,
DATEADD(HOUR,1,date_page_requested) AS date_end
FROM #Table) ranges
JOIN #Table data
ON data.date_page_requested >= ranges.date_start
AND data.date_page_requested < ranges.date_end
GROUP BY ranges.date_start
ORDER BY Tally DESC
For PostgreSQL, I'd first probably write something like this for a "window" aligned on the minute. You don't need OLAP windowing functions for this.
select w.ts,
date_trunc('minute', w.ts) as hour_start,
date_trunc('minute', w.ts) + interval '1' hour as hour_end,
(select count(*)
from weblog
where ts between date_trunc('minute', w.ts) and
(date_trunc('minute', w.ts) + interval '1' hour) ) as num_pages
from weblog w
group by ts, hour_start, hour_end
order by num_pages desc
Oracle also has a trunc() function, but I'm not sure of the format. I'll either look it up in a minute, or leave to see a friend's burlesque show.
WITH ranges AS
( SELECT
date_page_requested AS StartDate,
date_page_requested + (1/24) AS EndDate,
ROWNUMBER() OVER(ORDER BY date_page_requested) AS RowNo
FROM
#Table
)
SELECT
a.StartDate AS StartDate,
MAX(b.RowNo) - a.RowNo + 1 AS Tally
FROM
ranges a
JOIN
ranges b
ON a.StartDate <= b.StartDate
AND b.StartDate < a.EndDate
GROUP BY a.StartDate
, a.RowNo
ORDER BY Tally DESC
or:
WITH ranges AS
( SELECT
date_page_requested AS StartDate,
date_page_requested + (1/24) AS EndDate,
ROWNUMBER() OVER(ORDER BY date_page_requested) AS RowNo
FROM
#Table
)
SELECT
a.StartDate AS StartDate,
( SELECT MIN(b.RowNo) - a.RowNo
FROM ranges b
WHERE b.StartDate > a.EndDate
) AS Tally
FROM
ranges a
ORDER BY Tally DESC

How can I return multiple identical rows based on a quantity field in the row itself?

I'm using oracle to output line items in from a shopping app. Each item has a quantity field that may be greater than 1 and if it is, I'd like to return that row N times.
Here's what I'm talking about for a table
product_id, quanity
1, 3,
2, 5
And I'm looking a query that would return
1,3
1,3
1,3
2,5
2,5
2,5
2,5
2,5
Is this possible? I saw this answer for SQL Server 2005 and I'm looking for almost the exact thing in oracle. Building a dedicated numbers table is unfortunately not an option.
I've used 15 as a maximum for the example, but you should set it to 9999 or whatever the maximum quantity you will support.
create table t (product_id number, quantity number);
insert into t values (1,3);
insert into t values (2,5);
select t.*
from t
join (select rownum rn from dual connect by level < 15) a
on a.rn <= t.quantity
order by 1;
First create sample data:
create table my_table (product_id number , quantity number);
insert into my_table(product_id, quantity) values(1,3);
insert into my_table(product_id, quantity) values(2,5);
And now run this SQL:
SELECT product_id, quantity
FROM my_table tproducts
,( SELECT LEVEL AS lvl
FROM dual
CONNECT BY LEVEL <= (SELECT MAX(quantity) FROM my_table)) tbl_sub
WHERE tbl_sub.lvl BETWEEN 1 AND tproducts.quantity
ORDER BY product_id, lvl;
PRODUCT_ID QUANTITY
---------- ----------
1 3
1 3
1 3
2 5
2 5
2 5
2 5
2 5
This question is propably same as this: how to calc ranges in oracle
Update solution, for Oracle 9i:
You can use pipelined_function() like this:
CREATE TYPE SampleType AS OBJECT
(
product_id number,
quantity varchar2(2000)
)
/
CREATE TYPE SampleTypeSet AS TABLE OF SampleType
/
CREATE OR REPLACE FUNCTION GET_DATA RETURN SampleTypeSet
PIPELINED
IS
l_one_row SampleType := SampleType(NULL, NULL);
BEGIN
FOR cur_data IN (SELECT product_id, quantity FROM my_table ORDER BY product_id) LOOP
FOR i IN 1..cur_data.quantity LOOP
l_one_row.product_id := cur_data.product_id;
l_one_row.quantity := cur_data.quantity;
PIPE ROW(l_one_row);
END LOOP;
END LOOP;
RETURN;
END GET_DATA;
/
Now you can do this:
SELECT * FROM TABLE(GET_DATA());
Or this:
CREATE OR REPLACE VIEW VIEW_ALL_DATA AS SELECT * FROM TABLE(GET_DATA());
SELECT * FROM VIEW_ALL_DATA;
Both with same results.
(Based on my article pipelined function)

Resources