PL/SQL is not my strong suite. I am decent with SQL, but I have a challenge that I could really use your help with, if possible. I am using SQL Developer, if that helps.
I have a table that is a join from two other tables, but suffice it to say, it has the following applicaple columns:
FTE_NAME (VARCHAR2)
PRIMARY_BILLALBE_ROLE (VARCHAR2)
INVOICABLE_ALLOCATION (NUMBER)
CONTRACTED_FTE (NUMBER)
FTE_COUNTRY (VARCHAR2)
BILLING_START_DATE (DATE)
BILLING_END_DATE (DATE)
Here is an example of what I am trying to do:
I have actually done this using VBA and excel and it works great, but now the data is on an Oracle Server and its time for an update.
Example Rows:
| FTE_NAME | PRIMARY_BILLABLE_ROLE | INVOICEABLE_ALLOCATION | CONTRACTED_FTE | FTE_COUNTRY | BILLING_START_DATE | BILLING_END_DATE |
|------------|-----------------------|------------------------|----------------|-------------|--------------------|------------------|
| John Smith | Associate Manager | 1 | 1 | USA | January, 01 2013 | May, 01 2013 |
| John Smith | Manager | 1 | 1 | USA | May, 02 2013 | (null) |
What I would need to happen is that the PL/SQL code would build a monthly table and row by row include or exclude the row in that month, so from 01-JAN-2013 to 05-MAY-2013, the monthly table might look like this now with a MONTH COLUMN up front:
| MONTHLY | FTE_NAME | PRIMARY_BILLABLE_ROLE | INVOICEABLE_ALLOCATION | CONTRACTED_FTE | FTE_COUNTRY | BILLING_START_DATE | BILLING_END_DATE |
|-------------------|------------|-----------------------|------------------------|----------------|-------------|--------------------|------------------|
| January, 01 2013 | John Smith | Associate Manager | 1 | 1 | USA | January, 01 2013 | May, 01 2013 |
| February, 01 2013 | John Smith | Associate Manager | 1 | 1 | USA | January, 01 2013 | May, 01 2013 |
| March, 01 2013 | John Smith | Associate Manager | 1 | 1 | USA | January, 01 2013 | May, 01 2013 |
| April, 01 2013 | John Smith | Associate Manager | 1 | 1 | USA | January, 01 2013 | May, 01 2013 |
| May, 01 2013 | John Smith | Associate Manager | 1 | 1 | USA | January, 01 2013 | May, 01 2013 |
| May, 01 2013 | John Smith | Manager | 1 | 1 | USA | May, 02 2013 | (null) |
The lines for MAY would both be included in the 01-MAY-2013 rows, because that manager still worked as an Associate Manager for those few days. I used the start and end dates to calculate how many days.
The big part I need help with is how to get the table to build with a MONTHLY column using the first day of the month. There will 1000s of lines and each building everyday. I would have this code running in a view which will feed report and dashboards.
I really appreciate any help you can provide.
David
You can populate desired dates from scratch like in this example query:
with report_params as (
-- just to introduce variables
select
2013 year_value,
4 start_month,
6 end_month
from dual
),
report_months as (
-- list of first dates of all required months
select
add_months( -- add months to
trunc( -- January, 1st of year from parameters
to_date(
(select year_value from report_params),
'yyyy'
),
'yyyy'
),
(select start_month from report_params) + level - 2
)
from
dual
connect by
-- select number of rows (levels) from start_month to end_month
level <= (select end_month - start_month + 1 from report_params)
)
select * from report_months;
Another possibility is to analyze a table and generate diapason between minimum and maximum values:
with bound_months as (
select
min(trunc(billing_start_date,'mm')) as first_month,
max(trunc(billing_start_date,'mm')) as last_month
from
applicable_columns
),
report_months as (
select
add_months(
(select first_month from bound_months),
level - 1
)
as first_date
from
dual
connect by
-- select number of rows (levels) from start_month to end_month
level <= (select months_between(last_month,first_month)+1 from bound_months)
)
select * from report_months;
After that you can join list of months to data table/view with applicable columns:
with bound_months as (
select
min(trunc(billing_start_date,'mm')) as first_month,
max(trunc(billing_start_date,'mm')) as last_month
from
applicable_columns
),
report_months as (
select
add_months(
(select first_month from bound_months),
level - 1
)
as first_date
from
dual
connect by
-- select number of rows (levels) from start_month to end_month
level <= (select months_between(last_month,first_month)+1 from bound_months)
)
select
months.first_date,
data.*
from
report_months months,
applicable_columns data
where
data.billing_start_date < add_months(months.first_date,1)
and
nvl(data.billing_end_date, months.first_date) >= months.first_date
order by
first_date, fte_name, primary_billable_role
SQLFiddle test
Related
I would like to convert this SQL query into a LINQ to Entities query. If anyone has a solution, please share it with me.
SELECT
ROW_NUMBER() OVER (PARTITION BY Country, Year ORDER BY InvoiceId ASC) AS RowNo,
Country,
Year,
InvoiceId
FROM
Invoice
My SQL Server table Invoice looks like this:
InvoiceId | FinancialYear | Country | InvoiceNo
----------+---------------+----------+-----------
1 | 2017 | India | 1
2 | 2017 | India | 2
3 | 2018 | Indian | 1
4 | 2017 | USA | 1
5 | 2018 | USA | 2
The table is as follows:
YEAR | MONTH | HOLIDAY |
2015 | 10 | # # # ### |
2015 | 11 | # # # # # |
I want to create the following:
YEAR | MONTH | DATE | VALUE |
2015 | 10 | 01 | # |
2015 | 10 | 02 | |
2015 | 10 | 03 | |
2015 | 10 | 04 | # |
UNTIL
2016 | 11 | 30 | # |
I've seen a few solutions around stack but It couldn't work
Any help would be greatly appreciating
Well, I'm not sure why you would want to go to November 31st, a date that doesn't exist. But, here is an idea:
with n as (
select level as n
from dual
connect by level <= 31
)
select t.year, t.month, n.n as day,
substr(t.holidays, n.n, 1) as holiday
from t join
n
on n.n <= length(t.holiday);
I want to make a table that contains summary value from another table; the purpose is to make a balance sheets report. I'm new in Oracle database so I'm still confused regarding how to do that. Is it using procedure, if yes so how to do it?
Here is the example data
In table 1 :
Year | Periode | Date Trx | Debit | Credit
2014 | Jan | 2/1/2014 | 50 | 0
2014 | Jan | 3/1/2014 | 0 | 20
2014 | Feb | 2/2/2014 | 0 | 100
2014 | Feb | 6/2/2014 | 50 | 0
2015 | Mar | 2/3/2014 | 0 | 80
2015 | Mar | 8/3/2014 | 50 | 0
2015 | Okt | 2/10/2014| 50 | 0
And I want the result in table 2 to like this:
Year | Periode | Debit | Credit
2014 | Jan | 50 | 20
2014 | Feb | 50 | 100
2015 | Mar | 50 | 80
2015 | Okt | 50 | 0
I hope someone could tell me how to make the procedure
from the sample that you provided - it can be resolved
via simple sql. it's common sql and no specific oracle features are required here
select year, period, sum(debit) as debit, sum(credit) as credit
from my_table
group by year, period
As #are notes a procedure is not needed here - but if you really insist on using one it would look something like
CREATE OR REPLACE PROCEDURE POPULATE_BALANCE_SHEET_REPORT IS
BEGIN
INSERT INTO TABLE2
SELECT YEAR, PERIODE, SUM(DEBIT), SUM(CREDIT)
FROM TABLE1
GROUP BY YEAR, PERIODE;
END POPULATE_BALANCE_SHEET_REPORT;
Best of luck.
I am converting a postgres app to an Oracle app.
I came across this query:
WITH cost AS (SELECT
well_schedules.id,
generate_series(well_schedules.start_date::timestamp, well_schedules.end_date, '1 Day') AS "Date",
(well_schedules.drilling_engineering_estimate * well_schedules.well_estimated_working_interest)/((well_schedules.end_date - well_schedules.start_date) + 1) AS "Cost Per Day"
FROM
well_schedules
)
SELECT date_trunc('quarter', "Date"), COUNT("Cost Per Day"), id
FROM cost
GROUP BY id, date_trunc('quarter', "Date")
ORDER BY date_trunc('quarter', "Date")
The part I am struggling with is the generate_series line.
That line takes a start_date and end_date and lists all days between those two dates. We need that information to compile per day/week/month/quarter/year reports (or at least we assume we need that info).
Our data looks like this:
well_schedules
| id | start_date | end_date | cost |
| 1 | '2015-01-01' | '2015-03-20' | 100 |
We assume cost_per_day is equal across all days, so we'd like to generate a report that lets us look at cost_per_day, cost_per_week, cost_per_month, cost_per_year, and cost_per_quarter. cost_per_week/month/quarter/year is calculated by grouping the days by week/month/quarter/year and summing the associated cost_per_days
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE well_schedules ( id, start_date, end_date, cost ) AS
SELECT 1 , DATE '2015-01-01', DATE '2015-01-20', 100 FROM DUAL;
Query 1:
SELECT ID,
COLUMN_VALUE AS Day,
COST / ( end_date - start_date + 1 ) AS Cost_per_day
FROM well_schedules,
TABLE (
CAST(
MULTISET(
SELECT start_date + LEVEL - 1
FROM DUAL
CONNECT BY start_date + LEVEL - 1 <= end_date
)
AS SYS.ODCIDATELIST
)
)
Results:
| ID | DAY | COST_PER_DAY |
|----|---------------------------|--------------|
| 1 | January, 01 2015 00:00:00 | 5 |
| 1 | January, 02 2015 00:00:00 | 5 |
| 1 | January, 03 2015 00:00:00 | 5 |
| 1 | January, 04 2015 00:00:00 | 5 |
| 1 | January, 05 2015 00:00:00 | 5 |
| 1 | January, 06 2015 00:00:00 | 5 |
| 1 | January, 07 2015 00:00:00 | 5 |
| 1 | January, 08 2015 00:00:00 | 5 |
| 1 | January, 09 2015 00:00:00 | 5 |
| 1 | January, 10 2015 00:00:00 | 5 |
| 1 | January, 11 2015 00:00:00 | 5 |
| 1 | January, 12 2015 00:00:00 | 5 |
| 1 | January, 13 2015 00:00:00 | 5 |
| 1 | January, 14 2015 00:00:00 | 5 |
| 1 | January, 15 2015 00:00:00 | 5 |
| 1 | January, 16 2015 00:00:00 | 5 |
| 1 | January, 17 2015 00:00:00 | 5 |
| 1 | January, 18 2015 00:00:00 | 5 |
| 1 | January, 19 2015 00:00:00 | 5 |
| 1 | January, 20 2015 00:00:00 | 5 |
I will suggest the code below that consider the first and last day of the month from two dates:
Example:
Date Initial: 01/10/2014
Date Final: 12/21/2018
The code will return:
01/01/2014
02/01/2014
03/01/2014
04/01/2014
...
12/28/2018
12/29/2018
12/30/2018
12/31/2018
The Code:
SELECT
CAL.DT AS "Date"
,TO_NUMBER(TO_CHAR(CAL.DT,'DD')) AS "Day"
,TO_NUMBER(TO_CHAR(CAL.DT,'MM')) AS "Month"
,TO_NUMBER(TO_CHAR(CAL.DT,'YY')) AS "YearYY"
,TO_NUMBER(TO_CHAR(CAL.DT,'YYYY')) AS "YearYYYY"
,TO_CHAR(CAL.DT,'day') AS "Description_Day"
,TO_CHAR(CAL.DT,'dy') AS "Description_Day_Abrev"
,TO_CHAR(CAL.DT,'Month') AS "Description_Month"
,TO_CHAR(CAL.DT,'Mon') AS "Description_Month_Abrev"
,TO_CHAR(CAL.DT,'dd month yyyy') AS "Date_Text"
FROM (
SELECT
(
TO_DATE(SEQ.MM || SEQ.YYYY, 'MM/YYYY')-1
) + SEQ.NUM AS "DT"
FROM
(
SELECT RESULT NUM,
TO_CHAR(( -- Minimum Date
TO_DATE('01/01/2014', 'DD/MM/YYYY')
) , 'MM') AS "MM",
TO_CHAR(( -- Minimum Date
TO_DATE('01/01/2014', 'DD/MM/YYYY')
) , 'YYYY') AS "YYYY"
FROM
(
SELECT ROWNUM RESULT FROM DUAL CONNECT BY LEVEL <= (
(
-- Maximum Date
LAST_DAY(TO_DATE('31/12/2018', 'DD/MM/YYYY')) -- Always Last Day
-
-- Maximum Date
TRUNC(TO_DATE('01/01/2014', 'DD/MM/YYYY')) -- Always First Day of Month
) + 1 -- Because the First Day (RESULT) don't begin at zero
)
) -- How many sequences (RESULT) to generate
) SEQ
) CAL
;
I'm trying to insert some fields into MYTABLE from views MYVIEW1 and MYVIEW2 and then add a value from a parameter (this is part of a stored procedure) for UPDATED_BY, SYSDATE for UPDATED_ON. How can I correctly do this with INSERT SELECT or some other way entirely?
MYVIEW1
+------+----+-----+-----------+---------+
| YR | MO | QTR | USER_CODE | MO_PERF |
+------+----+-----+-----------+---------+
| 2012 | 1 | 1 | 1099 | 89 |
| 2012 | 2 | 1 | 1099 | 86 |
| 2012 | 3 | 1 | 1099 | 95 |
+------+----+-----+-----------+---------+
MYVIEW2
+------+-----+-----------+----------+
| YR | QTR | USER_CODE | QTR_PERF |
+------+-----+-----------+----------+
| 2012 | 1 | 1099 | 90 |
+------+-----+-----------+----------+
MYTABLE
+------+-----+-----------+---------+---------+---------+---------+-------------+------------+
| YR | QTR | USER_CODE | MO1_PCT | MO2_PCT | MO3_PCT | INC | UPDATED_BY | UPDATED_ON |
+------+-----+-----------+---------+---------+---------+---------+-------------+------------+
| 2012 | 1 | 1099 | 89 | 86 | 95 | 7000 | SAMPLE NAME | 01/16/2013 |
+------+-----+-----------+---------+---------+---------+---------+-------------+------------+
INSERT INTO MYTABLE
(YR,QTR,USER_CODE,MO1_PCT,MO2_PCT,MO3_PCT,INC,UPDATED_BY,UPDATED_ON)
SELECT b.YR,b.QTR,b.USER_CODE,b.MO1_PCT,b.MO2_PCT,b.MO3_PCT,c.INC
FROM MYVIEW1 b,
MYVIEW2 c
How do I insert values for (first month of QTR's MO_PERF) as MO1_PCT and (second month of QTR's MO_PERF) as MO2_PCT and (last month of QTR's MO_PERF) as MO3_PCT, making sure that I've inserted the right month within the right quarter and year.And then check if the MO_PERF values of each month has reached at least 85, else set INC as NULL.
,CASE WHEN MO1_PCT>=85 AND MO2_PCT>=85 AND MO3_PCT>=85 THEN 7000
ELSE NULL
END INC
If you're using oracle 11g then you can use PIVOT like this:
select YR, QTR, USER_CODE, "1_MO_PCT" MO1_PCT, "2_MO_PCT" MO2_PCT, "3_MO_PCT" MO3_PCT ,
case when "1_MO_PCT" >= 85 and "2_MO_PCT" >= 85 and "2_MO_PCT" >= 85 then 7000 end INC,
user updated_by, sysdate updated_on
from (
select m1.yr, m1.mo, m1.qtr, m1.user_code, m1.mo_perf, m2.qtr_perf
from myview1 m1 join myview2 m2 on m1.yr=m2.yr
and m1.qtr = m2.qtr and m1.user_code = m2.user_code )t
pivot(
max(mo_perf) MO_PCT for mo in (1,2,3)
)
Here is a sqlfiddle demo