How to Convert From one currency into another in Oracle query - oracle

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

Related

Vertica - Join on MAX(Date)

I m trying to replicate this sql syntax in Vertica, but it returns "ERROR: Subqueries in the ON clause are not supported".
The aim is to join two tables, table1 and table2, on column and date, if a.date = b.date or the closest but lesser b.date.
Any hint?
SELECT *
FROM table1 a
LEFT JOIN table2 b
ON a.column = b.column
AND b.Date = (SELECT MAX (b2.Date)
FROM table2 b2
WHERE a.column = b2.column
AND b2.Date <= a.Date)
Vertica has something handier for that: the event series join. With that, you can OUTER JOIN two tables so that they match the same or the immediately preceding value of the join column of the other table. The predicate is INTERPOLATE PREVIOUS VALUE instead of an equi-predicate.
Here's an example with the oil pressure curve and the engine rpm curve with the timestamp matching only once.
DROP TABLE IF EXISTS oilpressure;
CREATE TABLE oilpressure (
op_vid,op_ts,op_psi
) AS (
SELECT 42,TIMESTAMP '2020-04-01 07:00:00', 25.356
UNION ALL SELECT 42,TIMESTAMP '2020-04-01 07:00:12', 35.124
UNION ALL SELECT 42,TIMESTAMP '2020-04-01 07:00:23', 47.056
UNION ALL SELECT 42,TIMESTAMP '2020-04-01 07:00:34', 45.225
)
;
DROP TABLE IF EXISTS revspeed;
CREATE TABLE revspeed (
rs_vid,rs_ts,rpm
) AS (
SELECT 42,TIMESTAMP '2020-04-01 07:00:00', 2201
UNION ALL SELECT 42,TIMESTAMP '2020-04-01 07:00:10', 3508
UNION ALL SELECT 42,TIMESTAMP '2020-04-01 07:00:20', 6504
UNION ALL SELECT 42,TIMESTAMP '2020-04-01 07:00:30', 6608
)
;
-- without
\pset null '(null)'
SELECT *
FROM oilpressure
LEFT OUTER JOIN revspeed
ON op_vid=rs_vid AND op_ts=rs_ts ;
-- out Null display is "(null)".
-- out op_vid | op_ts | op_psi | rs_vid | rs_ts | rpm
-- out --------+---------------------+--------+--------+---------------------+--------
-- out 42 | 2020-04-01 07:00:00 | 25.356 | 42 | 2020-04-01 07:00:00 | 2201
-- out 42 | 2020-04-01 07:00:12 | 35.124 | (null) | (null) | (null)
-- out 42 | 2020-04-01 07:00:23 | 47.056 | (null) | (null) | (null)
-- out 42 | 2020-04-01 07:00:34 | 45.225 | (null) | (null) | (null)
-- with
SELECT *
FROM oilpressure
LEFT OUTER JOIN revspeed
ON op_vid=rs_vid AND rs_ts INTERPOLATE PREVIOUS VALUE op_ts;
-- out op_vid | op_ts | op_psi | rs_vid | rs_ts | rpm
-- out --------+---------------------+--------+--------+---------------------+------
-- out 42 | 2020-04-01 07:00:00 | 25.356 | 42 | 2020-04-01 07:00:00 | 2201
-- out 42 | 2020-04-01 07:00:12 | 35.124 | 42 | 2020-04-01 07:00:10 | 3508
-- out 42 | 2020-04-01 07:00:23 | 47.056 | 42 | 2020-04-01 07:00:20 | 6504
-- out 42 | 2020-04-01 07:00:34 | 45.225 | 42 | 2020-04-01 07:00:30 | 6608

Select SUM with GROUP BY by day interval

I have this table in Oracle Database:
date | column1 | column2
01/05/2020 00:00 | 50 | 20
01/05/2020 00:15 | 60 | 30
01/05/2020 00:30 | 70 | 40
... | |
01/05/2020 23:45 | 80 | 50
02/05/2020 00:00 | 100 | 40
02/05/2020 00:15 | 110 | 35
And I have this SELECT script:
SELECT
period,
liquid
FROM
(
SELECT
( nvl(SUM(m.column1), 0) - nvl(SUM(m.column2), 0) ) liquid,
TO_CHAR(trunc(m.date), 'dd/MM/YYYY') period
FROM
table m
WHERE
m.id_register IN (id_register)
AND m.date BETWEEN TO_DATE('01/05/2020 00:15:00', 'dd/mm/yyyy hh24:mi:ss') AND TO_DATE('01/06/2020 00:00:00', 'dd/mm/yyyy hh24:mi:ss')
GROUP BY
TO_CHAR(trunc(m.date), 'dd/MM/YYYY')
) vrdm
I need to sum (difference column1 and column2) all day registers in interval between 00:15 and 00:00 (next day) per day. How to make it?
The result that I need is like this:
period | liquid
01/05/2020 | 3000 -> SUM(all differences between column1 and column2 between 01/05 00:15 and 02/05 00:00)
02/05/2020 | 4000 -> SUM(all differences between column1 and column2 between 02/05 00:15 and 03/05 00:00)
03/05/2020 | 3500 -> SUM(all differences between column1 and column2 between 03/05 00:15 and 04/05 00:00)
Just check time part in where clause:
SELECT to_char(trunc(m.dt), 'dd/MM/YYYY') period,
nvl(SUM(m.column1), 0) - nvl(SUM(m.column2), 0) liquid
FROM m
WHERE to_char(m.dt, 'hh24:mi') >= '00:15'
GROUP BY trunc(m.dt)
dbfiddle

Return a value if null

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;

Edit Total line in Oracle report 1

The data structure:
sql-> desc t1
- List item
p_code number,
acc_date date ,
debit number,
credit number
Data inside my table:
sql-> select * from t1;
| p_code | acc_date | debit | credit |
| 001 | 01-01-15 | 100 | 25 |
| 001 | 02-01-15 | 0 | 125 |
| 001 | 03-01-15 | 415 | 85 |
I would like to do something like this:
select * from t1
where acc_date between :fromdate and :todate
union all
select p_code, (sum(nvl(debit,0))- sum(nvl(credit,0))) open_balance
from t1
where acc_date < :fromdate
;
But, I can't figure out what are my mistakes.
Type and number of columns in union must be the same -
select p_code, acc_date, debit, credit, null as open_balance
from t1
where acc_date between :fromdate and :todate
union all
select p_code, null as acc_date, null as debit, null as credit,
(sum(nvl(debit, 0)) - sum(nvl(credit, 0))) open_balance
from t1
where acc_date < :fromdate

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

Resources