Return a value if null - oracle

In oracle, I have a piece of code that returns our work order pass rate. It essentially takes the number of failures (MRB) and divides by the number of work orders processed (WO) to give the rate. The problem I have is that unless there is a failure (MRB) I will not get a return for the corresponding month. If there is no MRB failure, I would like a return of 100% for that month. Here is the code I am working with. For the record, I am not a programmer, I've just picked up a bit of info along the way. I have tried adding NVL but that does not seem to help.
Select To_Char(MTH, 'Month') As "Month",WO, MRB,
Round(1 - (MRB / WO), 3) * 100 As "Pass Rate",
'98' As Goal
From
(
Select Trunc(g.START_DATE, 'Month') As mth,
Count(Distinct V_PDAYPROD_CRW1.PDAYPROD_ID) As WO,
Count(Distinct V_WF_HEADER_MRB.ID) As MRB
From GLPERIODS g
Left Join V_PDAYPROD_CRW1 On Trunc(g.START_DATE, 'Month') = Trunc(V_PDAYPROD_CRW1.PROD_DATE, 'Month')
Left Join V_WF_HEADER_MRB On Trunc(g.START_DATE, 'Month') = Trunc(V_WF_HEADER_MRB.OPEN_DATE, 'Month')
Inner Join ARINVT On V_PDAYPROD_CRW1.ARINVT_ID = ARINVT.ID
Where Extract(Year From g.START_DATE) = Extract(Year From SysDate)
And V_WF_HEADER_MRB.WF_TYPE_ID = '99'
And V_WF_HEADER_MRB.EPLANT_ID = 2
And ARINVT.EPLANT_ID = 2
Group By Trunc(g.START_DATE, 'Month'),
V_WF_HEADER_MRB.WF_TYPE_ID
)
Group By To_Char(MTH, 'Month'),WO,MRB,
Round(1 - (MRB / WO), 3) * 100,
'98',MTH
Order By MTH
The above code returns the following:
MONTH | WO | MRB | Pass Rate | GOAL
September | 60 | 1 | 98.3 | 98
December | 30 | 2 | 93.3 | 98
I would like it to return something like this:
MONTH | WO | MRB | Pass Rate | GOAL
January | 25 | 0 | 100 | 98
February | 66 | 0 | 100 | 98
March | 35 | 0 | 100 | 98
April | 22 | 0 | 100 | 98
May | 19 | 0 | 100 | 98
June | 47 | 0 | 100 | 98
July | 52 | 0 | 100 | 98
August | 55 | 0 | 100 | 98
September | 60 | 1 | 98.3 | 98
October | 39 | 0 | 100 | 98
November | 18 | 0 | 100 | 98
December | 30 | 2 | 93.3 | 98
Thank you for any help you can offer.

If you expect all the records from the main table to be returned in the result set then you should not use the filtering criteria on "v_wf_header_mrb" table in the where clause.
Remove "V_WF_HEADER_MRB.WF_TYPE_ID = '99' And V_WF_HEADER_MRB.EPLANT_ID = 2 " condition from Where clause and try.
You can use the below query instead.
SELECT
TO_CHAR(mth,'Month') AS "Month",
wo,
mrb,
round(1 - (mrb / wo),3) * 100 AS "Pass Rate",
'98' AS goal
FROM
(
SELECT
trunc(g.start_date,'Month') AS mth,
COUNT(DISTINCT v_pdayprod_crw1.pdayprod_id) AS wo,
COUNT(DISTINCT v_wf_header_mrb.id) AS mrb
FROM
glperiods g
LEFT JOIN
v_pdayprod_crw1
ON
trunc(g.start_date,'Month') = trunc(v_pdayprod_crw1.prod_date,'Month')
LEFT JOIN
v_wf_header_mrb
ON
trunc(g.start_date,'Month') = trunc(v_wf_header_mrb.open_date,'Month')
AND
v_wf_header_mrb.wf_type_id = '99'
AND
v_wf_header_mrb.eplant_id = 2
INNER JOIN
arinvt
ON
v_pdayprod_crw1.arinvt_id = arinvt.id
WHERE
EXTRACT(YEAR FROM g.start_date) = EXTRACT(YEAR FROM SYSDATE)
AND
arinvt.eplant_id = 2
GROUP BY
trunc(g.start_date,'Month'),
v_wf_header_mrb.wf_type_id
)
GROUP BY
TO_CHAR(mth,'Month'),
wo,
mrb,
round(1 - (mrb / wo),3) * 100,
'98',
mth
ORDER BY mth;

Related

How to Convert From one currency into another in Oracle query

I am selecting filial expenditures in US dollar for a particular date. But I need to convert it into Won currency for that date. Currencies`s value changes at any time according to US dollar. Currency history is in table of V_CURRENCY. I need to multiply dollar to that amount and show in Won. My query is below and giving an error. Error is [22008][1861] ORA-01861. Mismatch of rows.
with cte as (
select t.filial_code,
t.modified_by as emp_code,
sum(t.sum_oper) as summa,
to_char(t.oper_date, 'YYYY-MM-DD') as operation_date
from OPERS t,
DEP_OPERS d
WHERE
And t.modified_by = 213
And t.filial_code = '00116'
And d.currency_code = 840
And t.oper_date >= to_date('07.01.2020', 'DD.MM.YYYY')
And t.oper_date < to_date('11.01.2020', 'DD.MM.YYYY')
group by to_char(t.oper_date, 'YYYY-MM-DD'), t.filial_code, t.modified_by
) select cte.filial_code,cte.emp_code,cte.summa * (select equival from V_CURRENCY
where date_activ = (select max(date_activ)
from V_CURRENCY
where date_activ <= cte.operation_date) and code = 840) as summa,cte.operation_date from cte;
From cte I am taking below result :
FILIAL_CODE | EMP_CODE | SUMMA | OPERATION_DATE
-----------------------------------------------
00116 | 213 | 40000 | 2020-01-14
00116 | 213 | 6000 | 2020-01-10
00116 | 213 | 2800 | 2020-01-06
My V_CURRENCY table is like below:
CODE | DATE_ACTIV | EQUIVAL|
--------------------------------
840 | 2020-01-13 00:00:00 | 576.97
840 | 2020-01-07 00:00:00 | 2008.54
840 | 2020-01-06 00:00:00 | 1941.91
840 | 2019-12-22 00:00:00 | 301.62
190 | 2020-01-13 00:00:00 | 1200.97
270 | 2020-01-13 00:00:00 | 2300.21
800 | 2019-12-22 00:00:00 | 100.62
I need to multiply equival from table V_CURRENCY in date 2020-01-13 00:00:00 to my cte result SUMMA for OPERATION_DATE 2020-01-14, means that 4000 * 576.97, cause OPERATION_DATE is closest one to currency change date. But when DATE_ACTIV exists for date 2020-01-06 then 2800 * 1941.91. I only need curreny value whose code is 840.
My last result should look like :
FILIAL_CODE | EMP_CODE | SUMMA | OPERATION_DATE
-----------------------------------------------
00116 | 213 | 40000 * 576.97 | 2020-01-14
00116 | 213 | 6000 * 2008.54 | 2020-01-10
00116 | 213 | 2800 * 1941.91 | 2020-01-06
Any response appreciated. Thanks in advance.
Use the LEAD analytic function to find the date the currency conversion is valid up until and then join the tables on the range between the start and end dates the currency is valid for:
SELECT y.filial_code,
y.emp_code,
y.summa * c.equival AS summa,
y.operation_date
FROM your_cte y
INNER JOIN (
SELECT c.*,
LEAD( DATE_ACTIV, 1, SYSDATE )
OVER ( PARTITION BY code ORDER BY date_activ )
AS DATE_FINISHED
FROM v_currency c
WHERE currency_code = 840
) c
ON ( y.operation_date >= c.date_activ
AND y.operation_date < c.date_finished )
Outputs:
FILIAL_CODE | EMP_CODE | SUMMA | OPERATION_DATE
:---------- | -------: | -------: | :-------------
00116 | 213 | 23078800 | 14-JAN-20
00116 | 213 | 12051240 | 10-JAN-20
00116 | 213 | 5437348 | 06-JAN-20
db<>fiddle here
As an aside, your CTE doesn't need to convert dates to strings and can use date literals and a modern ANSI join (rather than legacy comma join):
with cte ( filial_code, emp_code, summa, operation_date ) as (
SELECT t.filial_code,
t.modified_by,
sum(t.sum_oper),
t.oper_date
from OPERS t
CROSS JOIN
DEP_OPERS d
WHERE t.modified_by = 213
AND t.filial_code = '00116'
AND d.currency_code = 840
AND t.oper_date >= DATE '2020-01-07'
AND t.oper_date < DATE '2020-01-11'
GROUP BY t.oper_date,
t.filial_code,
t.modified_by
)
...
and you don't even need the CTE:
SELECT t.filial_code,
t.modified_by AS emp_code,
SUM(t.sum_oper * c.equival) AS summa,
t.oper_date AS operation_date
FROM OPERS t
CROSS JOIN
DEP_OPERS d
INNER JOIN (
SELECT c.*,
LEAD( DATE_ACTIV, 1, SYSDATE )
OVER ( PARTITION BY code ORDER BY date_activ )
AS DATE_FINISHED
FROM v_currency c
) c
ON ( t.oper_date >= c.date_activ
AND t.oper_date < c.date_finished
AND d.currency_code = c.currency_code )
WHERE t.modified_by = 213
AND t.filial_code = '00116'
AND d.currency_code = 840
AND t.oper_date >= DATE '2020-01-07'
AND t.oper_date < DATE '2020-01-11'
GROUP BY t.oper_date,
t.filial_code,
t.modified_by

Count Max number of day continious overdraff

Date | Account | Amount | Count max number of day continuous < 0 |
1 | 1001 | 100 | 0 |
2 | 1001 | -100 | 1 |
3 | 1001 | -100 | 2 |
4 | 1001 | 100 | 2 |
5 | 1001 | -100 | 2 |
6 | 1001 | -100 | 2 |
7 | 1001 | -100 | 3 |
8 | 1001 | -100 | 4 |
9 | 1001 | 100 | 4 |
I have sample data. I want have column "Count max number of day continuous < 0". How i can select it in database oracle
In order to find continuous periods you can use Tabibitosian method. Then use analytical count and finally max:
select date_, account, amount,
max(cnt) over (partition by account order by date_) max_overdraft_period
from (
select date_, account, amount,
count(case when amount <= 0 then 1 end)
over (partition by account, grp order by date_) cnt
from (
select date_, account, amount,
date_ - sum(case when amount <= 0 then 1 else 0 end)
over (partition by account order by date_) grp
from t ))
demo
I assumed that dates are continuous, if not then use row numbering at first.

Reset a cumulative sum?

I have the following dataset (table: stk):
S_Date Qty OOS (Out of Stock - 1 true, 0 false)
01/01/2013 0 1
02/01/2013 0 1
03/01/2013 0 1
04/01/2013 5 0
05/01/2013 0 1
06/01/2013 0 1
And what I want is:
S_Date Qty Cumulative_Days_OOS
01/01/2013 0 1
02/01/2013 0 2
03/01/2013 0 3
04/01/2013 5 0 -- No longer out of stock
05/01/2013 0 1
06/01/2013 0 2
The closest I've got so far is the following SQL:
SELECT
S_DATE, QTY,
SUM(OOS) OVER (PARTITION BY OOS ORDER BY S_DATE) CUMLATIVE_DAYS_OOS
FROM
STK
GROUP BY
S_DATE, QTY, OOS
ORDER BY
1
This gives me the following output:
S_Date Qty Cumulative_Days_OOS
01/01/2013 0 1
02/01/2013 0 2
03/01/2013 0 3
04/01/2013 5 0
05/01/2013 0 4
06/01/2013 0 5
It is close to what I want, but understandably, the sum is continued.
Is it possible to reset this cumulative sum and start it again?
I've tried searching around on stackoverflow and google, but I'm not really sure what I should be searching for.
Any help much appreciated.
You need to identify groups of consecutive days where oos = 1 or 0. This can be done by using LAG function to find when oos column changes and then summing over it.
with x (s_date,qty,oos,chg) as (
select s_date,qty,oos,
case when oos = lag(oos,1) over (order by s_date)
then 0
else 1
end
from stk
)
select s_date,qty,oos,
sum(chg) over (order by s_date) grp
from x;
output :
| S_DATE | QTY | OOS | GRP |
|--------------------------------|-----|-----|-----|
| January, 01 2013 00:00:00+0000 | 0 | 1 | 1 |
| January, 02 2013 00:00:00+0000 | 0 | 1 | 1 |
| January, 03 2013 00:00:00+0000 | 0 | 1 | 1 |
| January, 04 2013 00:00:00+0000 | 5 | 0 | 2 |
| January, 05 2013 00:00:00+0000 | 0 | 1 | 3 |
| January, 06 2013 00:00:00+0000 | 0 | 1 | 3 |
Then, you can sum over this oos, partitioned by grp column to get consecutive oos days.
with x (s_date,qty,oos,chg) as (
select s_date,qty,oos,
case when oos = lag(oos,1) over (order by s_date)
then 0
else 1
end
from stk
),
y (s_date,qty,oos,grp) as (
select s_date,qty,oos,
sum(chg) over (order by s_date)
from x
)
select s_date,qty,oos,
sum(oos) over (partition by grp order by s_date) cum_days_oos
from y;
output:
| S_DATE | QTY | OOS | CUM_DAYS_OOS |
|--------------------------------|-----|-----|--------------|
| January, 01 2013 00:00:00+0000 | 0 | 1 | 1 |
| January, 02 2013 00:00:00+0000 | 0 | 1 | 2 |
| January, 03 2013 00:00:00+0000 | 0 | 1 | 3 |
| January, 04 2013 00:00:00+0000 | 5 | 0 | 0 |
| January, 05 2013 00:00:00+0000 | 0 | 1 | 1 |
| January, 06 2013 00:00:00+0000 | 0 | 1 | 2 |
Demo at sqlfiddle.
First we need to divide rows to groups. In this case you can use the count of 0 values before current row as a group number. And then you can use SUM() OVER for these groups. To get 0 for OOS = 0 you can use CASE statement or just OOS*SUM(OOS) as soon as OOS = (0,1)
Something like this:
select T1.*,
OOS*SUM(OOS) OVER (PARTITION BY GRP ORDER BY S_DATE) CUMLATIVE_DAYS_OOS
FROM
(
select T.*,
(select count(*) from STK where S_Date<T.S_Date and OOS=0) GRP
FROM STK T
) T1
ORDER BY S_Date
SQLFiddle demo

ORACLE: INSERT SELECT FROM 2 views and value from param

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

SQL bring back highest sum of rows

I'm looking to calculate the highest basket in my set of data but I can't get my head around how I should do it.
I have data like:
OrderID | CustomerID | BasketID | ProductID | Price
1 | 1 | 1 | 221 | 10
2 | 1 | 1 | 431 | 123
3 | 1 | 2 | 761 | 44
4 | 2 | 3 | 12 | 54
5 | 2 | 3 | 102 | 78
6 | 3 | 4 | 111 | 98
7 | 3 | 4 | 41 | 45
8 | 3 | 5 | 65 | 66
9 | 4 | 6 | 32 | 47
10 | 4 | 6 | 118 | 544
Sorry if it seems quite messy.
But I can easily get the SUM with an obvious
SELECT SUM([Price]), BasketID, CustomerID FROM table
GROUP BY BasketID, CustomerID
But how can I filter the list for only the highest priced Basket ID for that CustomerID
Thanks
You can use a CTE (Common Table Expression) with the ROW_NUMBER function:
;WITH HighestPricePerCustomerAndBasket AS
(
SELECT
ID, UserID, ClassID, SchoolID, Created,
ROW_NUMBER() OVER(PARTITION BY BasketID,CustomerID ORDER BY Price DESC) AS 'RowNum'
FROM dbo.YourTable
)
SELECT
[Price], BasketID, CustomerID
FROM HighestPricePerCustomerAndBasket
WHERE RowNum = 1
This CTE "partitions" your data by BasketID,CustomerID, and for each partition, the ROW_NUMBER function hands out sequential numbers, starting at 1 and ordered by Price DESC - so the first row (highest price) gets RowNum = 1 (for each BasketID,CustomerID "partition") which is what I select from the CTE in the SELECT statement after it.
SELECT *
FROM (SELECT *,
DENSE_RANK() OVER (PARTITION BY CustomerID ORDER BY BasketTotal DESC) AS RNK
FROM (SELECT Sum(Price) AS BasketTotal,
BasketID,
CustomerID
FROM Order a
GROUP BY BasketID,
CustomerID
) a
) b
WHERE RNK = 1
I managed to conjure something up that worked.

Resources