plsql Trying to use number variable (year) to find fiscal year - sql

Thanks for any help,
I am trying to build a stored procedure, in pl/sql, where i can load data from the fiscal year specified.
For example, fyloader(2013) would load pe.contact_date between 10/01/2012 to 09/30/2013. How can I do that?
Thank you again,
Will
create or replace procedure fyloader (
p_fiscal_year number
) as
begin
insert into schemax.tablex
(select distinct pe.pat_id as PATID,
pat.pat_mrn_id as PATMRNID,
pe.pcp_prov_id as PCPPROVID,
pe.department_id as DEPARTMENTID,
pe.contact_date as CONTACTDATE,
trunc((TO_DATE ('30-09-2013 23:59:59', 'DD-MM-YYYY HH24:MI:SS') - pat.birth_date)/365.25) AS AGE,
'' as AGEGROUP,
ce.financial_class as FINCLASS
from pat_enc pe
inner join patient pat on pe.pat_id = pat.pat_id
inner join clarity_dep b on b.department_id = pe.department_id
left join clarity_ser cs on pe.visit_prov_id = cs.prov_id
left join zc_def_division zc on cs.def_division_c = zc.def_division_c
inner join hsp_account ha on pe.hsp_account_id = ha.hsp_account_id
inner join clarity_epm ce on ha.primary_payor_id = ce.payor_id
where
pe.enc_type_c = '101' --include office visits only (add 1001 if you require VirVis)
and (pe.appt_status_c = 2 OR pe.appt_status_c is null)
and (pat.pat_status_c =1 or nvl(pat.pat_status_c,99999) = 99999)
and pe.contact_date between TO_DATE('1-OCT-2012','DD-MON-YY') and TO_DATE('30-SEP-2013','DD-MON-YY')
and (zc.def_division_c in (26,91,266,329,330,331,332,333,389,402)
or pe.visit_prov_id = pe.pcp_prov_id)
and ce.financial_class in ('1','3')
and b.DEPARTMENT_ID in (200101,200102,200104,200201,200202,200204,
200220,200301,200302,200304,200319,200401,200402,200404,200501,200601,
200602,200701,200702,200704,200801,200802,200804,200911,200912,200913,
200914,200916,200917,200921,200923,200924,200925,200926,200927,200928,
201002,201201,201202,202101,202102,202104,202108,202301,202302,202308,
234005,230407,290109));
commit;
end;

You can use the 'to_date' and 'add_months' functions to get the result you need. Assuming your financial year is 01-Jul -> 30-Jun the below code will give you what you want.
declare
p_input_year number := 2014;
v_fy_start_date date;
v_fy_end_date date;
begin
v_fy_start_date := add_months(to_date('0101'||p_input_year,'DDMMYYYY'), -6);
v_fy_end_date := add_months(to_date('0101'||p_input_year,'DDMMYYYY'), 6) - 1;
dbms_output.put_line('FY Start: ' || v_fy_start_date);
dbms_output.put_line('FY End: ' || v_fy_end_date);
end;
The results of dbms_output being:
FY Start: 01-JUL-13
FY End: 30-JUN-14
If your financial year dates are different you will need to adjust the -6 / 6 passed to add_months.

Related

PL/SQL: How to sum a set of values if they fall within a specific time frame?

I have a query (below) that shows the number of terminations since 1/1/17 in one column and the associated date of the terminations in the only other column. If there were no terminations on a specific date, then there is no record for that date.
I want to create rolling 12-month time buckets and sum the number of terminations in those time buckets.
For example, the most recent time bucket would have an ending date of 11:59pm on 6/30/22. The start of that time bucket would start midnight on 7/1/21. I want to sum the number of terminations in that time bucket.
I need to create 12-month time buckets and the associated number of terminations for the last 60 months, resulting in 60 time buckets.
Here is my current query:
select
count(distinct employee_number) Number_of_terminations
, to_char(term_date, 'MM/DD/YYYY') term_date
from
(
select paa.person_id
,max(paa.effective_end_date)+1 term_date
,pap.employee_number
from
apps.per_all_assignments_f paa
, apps.per_assignment_status_types past
,(select distinct paa.person_id
from
apps.per_all_assignments_f paa
, apps.per_assignment_status_types past
where paa.assignment_status_type_id = past.assignment_status_type_id
and sysdate between paa.effective_start_date and paa.effective_end_date
and past.user_status in ('Active Assignment','Transitional - Active','Transitional - Inactive','Sabbatical','Sabbatical 50%')) active_person
, apps.per_all_people_f pap
, apps.hr_organization_units org
,(select case when orgp.name = 'Random University' then orgc.attribute1 else orgp.attribute1 end unit_number
,case when orgp.name = 'Random State University' then orgc.name else orgp.name end unit_name
,orgc.attribute1 dept_number
,orgc.name dept_name
from apps.per_org_structure_elements_v2 pose
,apps.per_org_structure_versions posv
,apps.hr_all_organization_units orgp
,apps.hr_all_organization_units orgc
where pose.org_structure_version_id = posv.org_structure_version_id
and pose.organization_id_parent = orgp.organization_id
and pose.organization_id_child = orgc.organization_id
and trunc(sysdate) between posv.date_from and nvl(posv.date_to,'31-dec-4712')
and pose.org_structure_hierarchy = 'Units'
order by case when orgp.name = 'Colorado State University' then orgc.attribute1 else orgp.attribute1 end
,orgc.attribute1) u
, apps.per_jobs pj
, apps.per_job_definitions pjd
where paa.assignment_status_type_id = past.assignment_status_type_id
and paa.person_id = active_person.person_id(+)
and active_person.person_id is null
and past.user_status in ('Active Assignment','Transitional - Active','Transitional - Inactive','Sabbatical','Sabbatical 50%')
and pap.person_id = paa.person_id
and paa.organization_id = org.organization_id
and org.attribute1 = u.dept_number(+)
and paa.job_id = pj.job_id
and pj.job_definition_id = pjd.job_definition_id
and pap.employee_number is not null
and (
paa.effective_end_date like '%17' or
paa.effective_end_date like '%18' or
paa.effective_end_date like '%19' or
paa.effective_end_date like '%20' or
paa.effective_end_date like '%21' or
paa.effective_end_date like '%22'
)
group by paa.person_id
, pap.employee_number
) terms
--group by substr(term_date, 4, 6)
group by to_char(term_date, 'MM/DD/YYYY')
Here are the first rows of the results:
enter image description here
In Excel the first sum would like be calculated like this: Excel example
I don't have your data and I don't want to spend time generating some test data to match that monster query but here is a simplified example explaining how to do this:
Create a calendar table: 1 record per bucket (monthly) with start and end date.
CREATE TABLE last_60_months (start_dt, end_dt)
AS
(SELECT TRUNC(ADD_MONTHS(SYSDATE,-LEVEL+1), 'MON'), TRUNC(ADD_MONTHS(SYSDATE,-LEVEL+13), 'MON') - 1 FROM DUAL
CONNECT BY LEVEL < 61
);
Create a test table with 10000 employees and a termination date within the test buckets boundaries:
CREATE table test_emps (employee_number NUMBER, term_date DATE);
DECLARE
l_dt DATE;
l_min_dt DATE;
l_max_dt DATE;
BEGIN
SELECT MIN(start_dt), MAX(start_dt) INTO l_min_dt, l_max_dt FROM last_60_months;
FOR r IN 1 .. 10000 LOOP
SELECT TO_DATE(
TRUNC(
DBMS_RANDOM.VALUE(TO_CHAR(l_min_dt,'J')
,TO_CHAR(l_max_dt,'J')
)
),'J'
)
INTO l_dt
FROM DUAL;
INSERT INTO test_emps (employee_number, term_date) VALUES (r, l_dt );
END LOOP;
COMMIT;
END;
/
Put it all together:
SELECT COUNT(e.employee_number) as "Number_of_terminations", d.start_dt, d.end_dt
FROM test_emps e JOIN last_60_months d ON e.term_date BETWEEN d.start_dt AND d.end_dt
GROUP BY start_dt, end_dt
ORDER BY start_dt;
It should be trivial to use this technique for your own data.

Decluttering a SQL query

For a practice project i wrote the following query and i was wondering if there is way to make it more efficient than writing everything 12 times like a for loop for sql.
CREATE TABLE temp (month INT, total_sales INT, market_share decimal(5,2), year_change decimal(5,2))
insert into temp (month)
Values (1)
UPDATE temp
SET total_sales = (
SELECT COUNT(purchases_2020.purchaseid)
FROM purchases_2020
JOIN categories ON purchases_2020.purchaseid = categories.purchase_id
WHERE (categories.category = 'whole milk' OR categories.category = 'yogurt' OR categories.category = 'domestic eggs') AND (purchases_2020.fulldate BETWEEN '2020-01-01' AND '2020-01-31')
)
WHERE month = 1
UPDATE temp
SET market_share = (
SELECT (SELECT 100 * COUNT(purchases_2020.purchaseid)
FROM purchases_2020
JOIN categories ON purchases_2020.purchaseid = categories.purchase_id
WHERE (categories.category = 'whole milk' OR categories.category = 'yogurt' OR categories.category = 'domestic eggs') AND (purchases_2020.fulldate BETWEEN '2020-01-01' AND '2020-01-31'))
* 1. /
(SELECT COUNT(purchases_2020.purchaseid)
FROM purchases_2020
WHERE purchases_2020.fulldate BETWEEN '2020-01-01' AND '2020-01-31')
)
WHERE month = 1
UPDATE temp
SET year_change = (
SELECT market_share -
(SELECT
(SELECT 100 * COUNT(purchases_2019.purchase_id)
FROM purchases_2019
JOIN categories ON purchases_2019.purchase_id = categories.purchase_id
WHERE (categories.category = 'whole milk' OR categories.category = 'yogurt' OR categories.category = 'domestic eggs') AND (purchases_2019.full_date BETWEEN '2019-01-01' AND '2019-01-31'))
* 1./
(SELECT COUNT(purchases_2019.purchase_id)
FROM purchases_2019
WHERE purchases_2019.full_date BETWEEN '2019-01-01' AND '2019-01-31'))
FROM temp
WHERE month = 1
)
WHERE month = 1
EDIT
I was given the 3 tables represented on the following database schema , and im trying to create a table with the total sales of dairy every month, the monthly market share of the dairy products and the difference between the 2020 monthly market share and the 2019 monthly market share (the year change colunm)
There is also an aritmethic error somewhere, when checking the project i get the following message ResultSet does not contain the correct numeric values! and im at my wits end looking for it butmy priority is to decluter the query.
Your error message tells me that you are trying to run this from a reporting tool or a host language.
It also makes no sense to put the data into separate tables by years.
SQL is a declarative language that works with data as sets.
Instead of pushing the results into table temp, try writing a query like this:
with all_data as (
select p.fulldate, p.purchaseid, c.category,
extract(year from p.fulldate) as year,
extract(month from p.fulldate) as month
from purchases_2020 p
join categories c on c.purchase_id = p.purchaseid
union all
select p.fulldate, p.purchaseid, c.category,
extract(year from p.fulldate) as year,
extract(month from p.fulldate) as month
from purchases_2019 p
join categories c on c.purchase_id = p.purchaseid
), kpis as (
select year, month,
count(purchaseid)
filter (where category in ('whole milk', 'yogurt', 'domestic eggs'))
as dairy_sales,
count(purchaseid) * 1.0 as total_sales
from all_data
group by year, month
)
select ty.month, ty.dairy_sales as total_sales,
100.0 * ty.dairy_sales / ty.total_sales as market_share,
100.0 * ( (ty.dairy_sales / ty.total_sales)
- (ly.dairy_sales / ly.total_sales)) as year_change
from kpis ty
join kpis ly
on (ly.year, ly.month) = (ty.year - 1, ty.month);

I want to update a column using PL/SQL

Here is a query. I want bookdate_to to be updated.
SELECT
r.resource_id,
r.type,
r.resort_id,
r.parent_id,
r.code,
r.name,
r.path,
r.cashflowmanager_id,
c.bookdate_from,
c.bookdate_to,
c.usage_date_from,
c.usage_date_to
FROM
resourcebasei18n r
JOIN cashflowrule c ON ( r.cashflowmanager_id = c.cashflowmanager_id )
WHERE
name = '4-Persoons Ranchtent'
AND ( c.usage_date_from BETWEEN '01-APR-20' AND '01-MAY-20'
OR c.usage_date_to BETWEEN '01-APR-20' AND '01-MAY-20' );
I want a proper plsql code using procedure or loop that updates all records of column bookdate_to.
There is no need for PL/SQL loops. We can do this in plain SQL:
update cashflowrule c
set c.bookdate_to = date '2019-09-29'
where ( c.usage_date_from BETWEEN date '2020-04-01' AND date '2020-05-01'
OR c.usage_date_to BETWEEN date '2020-04-01' AND date '2020-05-01' )
and c.bookdate_to is null -- not sure whether this is a rule, your comments confuse me
and c.cashflowmanager_id in ( select r.cashflowmanager_id
from resourcebasei18n r
where r.name = '4-Persoons Ranchtent' )
As has been pointed out this is easily accomplished with a single SQL but since you require plsql here's an anonymous block for it..
declare
cursor c_cashflowrule is
select c.bookdate_to
from cashflowrule c
where ( c.usage_date_from between date '2019-09-29' and date '2020-05-01'
or c.usage_date_to between date '2019-09-29' and date '2020-05-01'
)
and exists (select null
from resourcebasei18n r
where c.cashflowmanager_id = r.cashflowmanager_id
and r.name = '4-Persoons Ranchtent'
)
for update of bookdate_to;
begin
for cflow in c_cashflowrule
loop
update cashflowrule
set bookdate_to = date '2019-09-29'
where current of c_cashflowrule ;
end loop;
end ;

Show a Total for a SQL Table

I am trying to add a Total line at the bottom of this sql syntax but thus far have received no break through. I have viewed the following but both of them do not adhere with my condition. Can someone please provide assistance with regards to this.
Add a summary row with totals
Adding a total row to the end of query result
select dm.Builder ||' ('|| dm.Lot_Size || '''s)' as"Builder",count(sd.Address) "The Count",
dm."Construction_ID"
from input dm
left join data sd on sd.inputfk = dm.inputpk
and sd.Closing Date >= DATE '01/01/2017' and sd.Closing Date < DATE '06/30/2017'
where dm.Construction_ID = 'AJR'
group by dm.Builder,dm.Lot_Size, dm.Project_ID
having count(sd.Address) > 0
order by dm.Builder
When I run it:
Builder The Count Construction_ID
Jake's Homes (55's) 2 AJR
Jake's Homes (65's) 3 AJR
Maggie's Homes (65's) 5 AJR
Maggie's Homes (66's) 2 AJR
Maggie's Homes (75's) 3 AJR
Maggie's Homes (90's) 1 AJR
Total ----------> 16
Your group by has dm.Project_ID, sd.Address which is probably causing it.
For total, you can use ROLLUP:
Try this:
select coalesce(dm.Builder || ' (' || dm.Lot_Size || '''s)', 'Total') as "Builder",
count(sd.Address) "The Count",
dm."Construction_ID"
from input dm
left join data sd on sd.inputfk = dm.inputpk
and sd.Closing date >= date '01/01/2017'
and sd.Closing date < date '06/30/2017'
where dm.Construction_ID = 'AJR'
group by rollup(dm.Builder || ' (' || dm.Lot_Size || '''s)')
having count(sd.Address) > 0
order by "Builder"
Try this:
select dm.Builder ||' ('|| dm.Lot_Size || '''s)' as"Builder",count(sd.Address) "The Count",
dm."Construction_ID"
from input dm
left join data sd on sd.inputfk = dm.inputpk
and sd.Closing Date >= DATE '01/01/2017' and sd.Closing Date < DATE '06/30/2017'
where dm.Construction_ID = 'AJR'
group by rollup( (dm.Builder,dm.Lot_Size, dm.Project_ID) )
having count(sd.Address) > 0
order by dm.Builder
Just... why you need count(sd.Address) > 0 ?
Given the post is tagged with postgresql, assuming it is for that platform; as such, see https://www.postgresql.org/docs/9.5/static/queries-table-expressions.html#QUERIES-GROUPING-SETS

SQL Command Not Properly Ended - Oracle Subquery

I'm using a Crystal Reports 13 Add Command for record selection from an Oracle database connected through the Oracle 11g Client. The error I am receiving is ORA-00933: SQL command not properly ended, but I can't find anything the matter with my code (incomplete):
/* Determine units with billing code effective dates in the previous month */
SELECT "UNITS"."UnitNumber", "BILL"."EFF_DT"
FROM "MFIVE"."BILL_UNIT_ACCT" "BILL"
LEFT OUTER JOIN "MFIVE"."VIEW_ALL_UNITS" "UNITS" ON "BILL"."UNIT_ID" = "UNITS"."UNITID"
WHERE "UNITS"."OwnerDepartment" LIKE '580' AND TO_CHAR("BILL"."EFF_DT", 'MMYYYY') = TO_CHAR(ADD_MONTHS(TRUNC(SYSDATE), -1), 'MMYYYY')
INNER JOIN
/* Loop through previously identified units and determine last billing code change prior to preious month */
(
SELECT "BILL2"."UNIT_ID", MAX("BILL2"."EFF_DT")
FROM "MFIVE"."BILL_UNIT_ACCT" "BILL2"
WHERE TO_CHAR("BILL2"."EFF_DT", 'MMYYYY') < TO_CHAR(ADD_MONTHS(TRUNC(SYSDATE), -1), 'MMYYYY')
GROUP BY "BILL2"."UNIT_ID"
)
ON "BILL"."UNIT_ID" = "BILL2"."UNIT_ID"
ORDER BY "UNITS"."UnitNumber", "BILL"."EFF_DT" DESC
We are a state entity that leases vehicles (units) to other agencies. Each unit has a billing code with an associated effective date. The application is to develop a report of units with billing codes changes in the previous month.
Complicating the matter is that for each unit above, the report must also show the latest billing code and associated effective date prior to the previous month. A brief example:
Given this data and assuming it is now April 2016 (ordered for clarity)...
Unit Billing Code Effective Date Excluded
---- ------------ -------------- --------
1 A 04/15/2016 Present month
1 B 03/29/2016
1 A 03/15/2016
1 C 03/02/2016
1 B 01/01/2015
2 C 03/25/2016
2 A 03/04/2016
2 B 07/24/2014
2 A 01/01/2014 A later effective date prior to previous month exists
3 D 11/28/2014 No billing code change during previous month
The report should return the following...
Unit Billing Code Effective Date
---- ------------ --------------
1 B 03/29/2016
1 A 03/15/2016
1 C 03/02/2016
1 B 01/01/2015
2 C 03/25/2016
2 A 03/04/2016
2 B 07/24/2014
Any assistance resolving the error will be appreciated.
You have a WHERE clause before the INNER JOIN clause. This is invalid syntax - if you swap them it should work:
SELECT "UNITS"."UnitNumber",
"BILL"."EFF_DT"
FROM "MFIVE"."BILL_UNIT_ACCT" "BILL"
LEFT OUTER JOIN
"MFIVE"."VIEW_ALL_UNITS" "UNITS"
ON "BILL"."UNIT_ID" = "UNITS"."UNITID"
INNER JOIN
/* Loop through previously identified units and determine last billing code change prior to preious month */
(
SELECT "UNIT_ID",
MAX("EFF_DT")
FROM "MFIVE"."BILL_UNIT_ACCT"
WHERE TO_CHAR("EFF_DT", 'MMYYYY') < TO_CHAR(ADD_MONTHS(TRUNC(SYSDATE), -1), 'MMYYYY')
GROUP BY "UNIT_ID"
) "BILL2"
ON "BILL"."UNIT_ID" = "BILL2"."UNIT_ID"
WHERE "UNITS"."OwnerDepartment" LIKE '580'
AND TO_CHAR("BILL"."EFF_DT", 'MMYYYY') = TO_CHAR(ADD_MONTHS(TRUNC(SYSDATE), -1), 'MMYYYY')
ORDER BY "UNITS"."UnitNumber", "BILL"."EFF_DT" DESC
Also, you need to move the "BILL2" alias outside the () brackets as you do not need the alias inside the brackets but you do outside.
Are you really sure you need the double-quotes ""? Double-quotes enforce case sensitivity in column names - the default behaviour is for Oracle to convert all table and column names to upper case to abstract the case-sensitivity from the user - since you are using both double-quotes and upper-case names the quotes seems redundant.
SOLUTION:
SELECT DISTINCT "BILL2".*
FROM "MFIVE"."BILL_UNIT_ACCT" "BILL"
LEFT JOIN
/* Returns all rows for each unit with effective date >= maximum prior effective date and effective date < current month */
(
SELECT "UNITS"."UnitNumber" AS "UNITNO",
UPPER("UNITS"."SpecNoDescription") AS "TS_DESCR",
"UNITS"."UsingDepartment" AS "USING_DEPT",
"UNITS"."OwnerDepartment" AS "OWNING_DEPT",
"BILL"."BILLING_CODE",
"BILL"."EFF_DT",
"BILL"."UNIT_ID"
FROM "MFIVE"."BILL_UNIT_ACCT" "BILL"
LEFT OUTER JOIN "MFIVE"."VIEW_ALL_UNITS" "UNITS" ON "BILL"."UNIT_ID" = "UNITS"."UNITID"
WHERE TO_NUMBER(TO_CHAR(TRUNC("BILL"."EFF_DT"), 'YYYYMM'), '999999') < TO_NUMBER(TO_CHAR(TRUNC(ADD_MONTHS(SYSDATE, 0)), 'YYYYMM'), '999999') AND
"UNITS"."OwnerDepartment" = '580' AND
"BILL"."EFF_DT" >=
/* Returns maximum effective date prior to previous month for each unit */
(
SELECT MAX("BILL3"."EFF_DT")
FROM "MFIVE"."BILL_UNIT_ACCT" "BILL3"
WHERE "BILL3"."UNIT_ID" = "BILL"."UNIT_ID" AND
TO_NUMBER(TO_CHAR(TRUNC("BILL3"."EFF_DT"), 'YYYYMM'), '999999') < TO_NUMBER(TO_CHAR(TRUNC(ADD_MONTHS(SYSDATE, -1)), 'YYYYMM'), '999999')
GROUP BY "BILL3"."UNIT_ID"
)
) BILL2
ON "BILL"."UNIT_ID" = "BILL2"."UNIT_ID"
WHERE TO_CHAR("BILL"."EFF_DT", 'YYYYMM') = TO_CHAR(ADD_MONTHS(TRUNC(SYSDATE), -1), 'YYYYMM')
ORDER BY "BILL2"."UNITNO", "BILL2"."EFF_DT" DESC
If the where before Join really matters to you, use a CTE. (Employing with clause for temporary table and joining on the same.)
With c as (SELECT "UNITS"."UnitNumber", "BILL"."EFF_DT","BILL"."UNIT_ID" -- Correction: Was " BILL"."UNIT_ID" (spacetanker)
FROM "MFIVE"."BILL_UNIT_ACCT" "BILL" -- Returning unit id column too, to be used in join
LEFT OUTER JOIN "MFIVE"."VIEW_ALL_UNITS" "UNITS" ON "BILL"."UNIT_ID" = "UNITS"."UNITID"
WHERE "UNITS"."OwnerDepartment" LIKE '580' AND TO_CHAR("BILL"."EFF_DT", 'MMYYYY') = TO_CHAR(ADD_MONTHS(TRUNC(SYSDATE), -1), 'MMYYYY'))
select * from c --Filter out your required columns from c and d alias results, e.g c.UNIT_ID
INNER JOIN
--Loop through previously identified units and determine last billing code change prior to preious month */
(
SELECT "BILL2"."UNIT_ID", MAX("BILL2"."EFF_DT")
FROM "MFIVE"."BILL_UNIT_ACCT" "BILL2"
WHERE TO_CHAR("BILL2"."EFF_DT", 'MMYYYY') < TO_CHAR(ADD_MONTHS(TRUNC(SYSDATE), -1), 'MMYYYY')
GROUP BY "BILL2"."UNIT_ID"
) d
ON c."UNIT_ID" = d."UNIT_ID"
order by c."UnitNumber", c."EFF_DT" desc -- COrrection: Removed semicolon that Crystal Reports didn't like (spacetanker)
It seems this query has lots of scope for tuning though. However, one who has access to the data and requirement specification is the best judge.
EDIT :
You are not able to see data PRIOR to previous month since you are using BILL.EFF_DT in your original question's select statement, which is filtered to give only dates of previous month(..AND TO_CHAR("BILL"."EFF_DT", 'MMYYYY') = TO_CHAR(ADD_MONTHS(TRUNC(SYSDATE), -1), 'MMYYYY'))
If you want the data as you want, I guess you have to use the BILL2 section (d part in my subquery), by giving an alias to Max(EFF_DT), and using that alias in your select clause.