ORACLE Query to check if one field has changed to another specific - oracle

We recently noticed that some workes have the maximum rate (13) when they barely have any bought items, so we need a query that checks if any worker rate changes to the maximum so we can check if its legit.
So I have these two tables:
Table1-> Workers (DNI,RATE,TICKET,PRICE,...)
Table2-> Sells (DNI,NAME,DATE)
edit: DATE and RATE are both Strings, Date follows this structure: YYYYMMDD
Sells table have a special discount depending on the rate of the client.
I want to check if any of those wokers rate changes from something that is not 13 --> to 13
The only way this can happen if is the worker buys a lot, or if someone changes it manually so they get a better discount (NOT ALLOWED).
So, I want to check if yesterday any worker had a 6 rate and today its 13.
SELECT RATE,DNI FROM SELLS WHERE RATE='13' AND DNI IN
(
SELECT DNI FROM WORKERS
)
AND DATE = to_char(sysdate-1, 'yyyymmdd')
Is there any way to UNION those so the matched remain?
SELECT RATE,DNI FROM SELLS WHERE RATE <> '13' AND DNI IN
(
SELECT DNI FROM WORKERS
)
AND DATE to_char(sysdate, 'yyyymmdd')
Or is there any better way?
Edit of what tried:
Real query:
select
coalesce(t.dni, y.dni) as dni,
t.Tarifa as today_rate,
y.Tarifa as yesterday_rate
from (
SELECT Tarifa, DNI FROM pws_ventas_materiales,pws_ventas_cabecera
WHERE Tarifa='13'
AND DNI IN (SELECT DNI FROM trabajadores_sirgo)
AND pws_ventas_materiales.fecha = to_char(sysdate-1, 'yyyymmdd')
and pws_ventas_materiales.ticket = pws_ventas_cabecera.ticket
and pws_ventas_cabecera.fecha = pws_ventas_materiales.fecha
) y
full join (
SELECT Tarifa, DNI FROM pws_ventas_materiales ,pws_ventas_cabecera
WHERE Tarifa <> '13' and Tarifa is not null
AND DNI IN (SELECT DNI FROM trabajadores_sirgo)
AND pws_ventas_materiales.fecha = to_char(sysdate, 'yyyymmdd')
and pws_ventas_materiales.ticket = pws_ventas_cabecera.ticket
and pws_ventas_cabecera.fecha = pws_ventas_materiales.fecha
) t on t.dni = y.dni
;
Table Updates:
pws_ventas_materiales=Sells
trabajadores_sirgo=Workers
pws_ventas_cabecera --> There was a mistake on the Sells table, that table does not contain a DNI column, this table does.
Sorry for that mistake
Also, rate=Tarifa and date=fecha + I added a pws_ventas_materiales.ticket = pws_ventas_cabecera.ticket to make sure that we are talking about the same sell between those two tables.

You can join both queries to compare. In this case I would think that an full outer join is more appropriate. For example:
select
coalesce(t.dni, y.dni) as dni,
t.rate as today_rate,
y.rate as yesterday_rate
from (
SELECT RATE, DNI FROM SELLS
WHERE RATE='13'
AND DNI IN (SELECT DNI FROM WORKERS)
AND DATE = to_char(sysdate-1, 'yyyymmdd')
) y
full join (
SELECT RATE, DNI FROM SELLS
WHERE RATE <> '13'
AND DNI IN (SELECT DNI FROM WORKERS)
AND DATE to_char(sysdate, 'yyyymmdd')
) t on t.dni = y.dni
-- WHERE today_rate is not null -- extra filtering here
Also, the following query can compare all dates, not just today and yesterday:
select *
from (
select
dni, date, rate,
lag(date) over(partition by dni order by date) as prev_date,
lag(rate) over(partition by dni order by date) as prev_rate
from workers
) x
where prev_rate <> '13' and rate = '13'

Related

Using Global Temporary Table in Subquery gives NULL

I am using following query.
SELECT SYSDATE,(SELECT P_PRICE_OPEN FROM GTT_ADJ_PRICE_TABLE WHERE FSYM_ID='P8R3C2-R' AND P_DATE='22-OCT-18' AND P_VOLUME<>0 AND ROWNUM=1) FROM DUAL;
GTT_ADJ_PRICE_TABLE is global temporary table loaded with values when i have executed a function related to it. This GTT preserves rows after commit. This query gives me correct results.
But, If i run the query
SELECT WEEK_END, WEEK_START,
(SELECT P_PRICE_OPEN FROM GTT_ADJ_PRICE_TABLE WHERE P_DATE=WEEK_START) AS WEEKS_OPEN_PRICE,
(SELECT MAX(P_PRICE_HIGH) FROM GTT_ADJ_PRICE_TABLE WHERE FSYM_ID=FID AND P_DATE<=WEEK_END
AND P_DATE>=WEEK_START AND P_VOLUME<>0) AS WEEKLY_HIGH,
(SELECT MIN(P_PRICE_LOW) FROM GTT_ADJ_PRICE_TABLE WHERE FSYM_ID=FID AND P_DATE<=WEEK_END
AND P_DATE>=WEEK_START AND P_VOLUME<>0) AS WEEKLY_LOW,
(SELECT SUM(P_VOLUME) FROM GTT_ADJ_PRICE_TABLE WHERE FSYM_ID=FID AND P_DATE<=WEEK_END
AND P_DATE>=WEEK_START AND P_VOLUME<>0) AS WEEKLY_VOLUME,
P_PRICE
FROM (
SELECT ROWNUM,FID,WEEK_END,P_VOLUME,P_PRICE,
(SELECT P_DATE FROM FP_V2_FP_BASIC_PRICES WHERE FSYM_ID=FID AND P_DATE>=TRUNC(WEEK_END, 'IW') AND P_VOLUME<>0 AND ROWNUM=1) AS WEEK_START
FROM (
SELECT
ROWNUM,FSYM_ID AS FID, WEEK_END,P_VOLUME, P_PRICE,P_PRICE_OPEN,P_PRICE_HIGH,P_PRICE_LOW
FROM (
SELECT ROWNUM,FSYM_ID,P_DATE AS WEEK_END, P_PRICE,P_VOLUME, P_PRICE_OPEN,P_PRICE_HIGH,P_PRICE_LOW,
CASE
WHEN (TO_CHAR(P_DATE,'D') >= AVG(TO_CHAR(P_DATE,'D')) OVER (order by P_DATE DESC rows between 1 preceding and current row) and ROWNUM>=1) or TO_CHAR(P_DATE,'D')=6
THEN 1
ELSE 0
END AS WEEKFLAG
FROM(
SELECT * FROM TABLE (ADJUSTED_PRICE('P8R3C2-R')) WHERE P_VOLUME<>0
)
)WHERE WEEKFLAG=1
)
);
It gives me NULL in WEEKS_OPEN_PRICE,WEEKLY_HIGH,WEEKLY_LOW,WEEKLY_VOLUME.
Please help me to resolve the issue. Thanks in anticipation.
Try with WITH clause. I hope a below query works. I don't have possibility to check.
WITH adjustedPrice AS (
SELECT *
FROM TABLE(adjusted_price('P8R3C2-R'))
WHERE p_volume <> 0
AND TO_CHAR(p_date, 'DY') NOT IN ('SAT', 'SUN')
)
SELECT ap.week,
ap.week_start,
ap.week_end,
ap.weeks_high_price,
ap.weeks_low_price,
ap.weekly_volume,
ws.p_price_open AS weeks_open_price,
we.p_price AS weeks_close_price
FROM (
SELECT TRUNC(p_date, 'IW') AS week,
TRUNC(MIN(p_date)) AS week_start,
TRUNC(MAX(p_date)) AS week_end,
MAX(p_price_high) AS weeks_high_price,
MIN(p_price_low) AS weeks_low_price,
SUM(p_volume) AS weekly_volume
FROM adjustedPrice
GROUP BY TRUNC(p_date, 'IW')
) ap
INNER JOIN adjustedPrice ws ON ws.p_date = ap.week_start
INNER JOIN adjustedPrice we ON we.p_date = ap.week_end
ORDER BY week DESC;
Wanted to post as a comment. But it wont fit there thats why writing it as an answer.
I tried this code to get the week start, end , weeks high price, weeks low price, weekly volume.
select
trunc("P_DATE", 'IW') as week,
min(trunc("P_DATE")) as week_start,
max(trunc("P_DATE")) as week_end,
MAX(P_PRICE_HIGH) AS WEEKS_HIGH_PRICE,
MIN(P_PRICE_LOW) AS WEEKS_LOW_PRICE,
SUM(P_VOLUME) AS WEEKLY_VOLUME
from TABLE
(ADJUSTED_PRICE('P8R3C2-R'))
WHERE
P_VOLUME<>0 AND to_char("P_DATE", 'DY') not in ('SAT','SUN')
group by
trunc("P_DATE", 'IW')
ORDER BY
trunc("P_DATE", 'IW') DESC;
That gave me results in hardly 2-4 seconds. But i want to get the weeks open price where date will be equal to WEEK_START and weeks closing price where date will be equal to WEEK_END.
I tried following approach for the same. But its taking too much time (like 300+ seconds).
SELECT
WEEK,WEEK_START, WEEK_END, WEEKS_HIGH_PRICE,WEEKS_LOW_PRICE,WEEKLY_VOLUME,
(SELECT P_PRICE_OPEN FROM TABLE (ADJUSTED_PRICE('P8R3C2-R')) WHERE P_VOLUME<>0 AND P_DATE=WEEK_START) AS WEEKS_OPEN_PRICE,
(SELECT P_PRICE FROM TABLE (ADJUSTED_PRICE('P8R3C2-R')) WHERE P_VOLUME<>0 AND P_DATE=WEEK_END) AS WEEKS_CLOSE_PRICE
FROM
(
select
trunc("P_DATE", 'IW') as week,
min(trunc("P_DATE")) as week_start,
max(trunc("P_DATE")) as week_end,
MAX(P_PRICE_HIGH) AS WEEKS_HIGH_PRICE,
MIN(P_PRICE_LOW) AS WEEKS_LOW_PRICE,
SUM(P_VOLUME) AS WEEKLY_VOLUME
from TABLE
(ADJUSTED_PRICE('P8R3C2-R'))
WHERE
P_VOLUME<>0 AND to_char("P_DATE", 'DY') not in ('SAT','SUN')
group by
trunc("P_DATE", 'IW')
ORDER BY
trunc("P_DATE", 'IW') DESC
);
If anybody can help me to improve the output time is most welcome.

Oracle month year temp table

I am trying to create a month, year temp table that I can relate to in calculations, however I am having some issues. I am unable to create global temp tables due to restrictions and have to rely on the following kind of query.
WITH Months AS
(
SELECT LEVEL -1 AS ID
FROM DUAL
CONNECT BY LEVEL <=264
)
(SELECT
ROWNUM AS MO_SYS_ID,
TO_CHAR(ADD_MONTHS(TO_DATE('01/01/1999', 'DD/MM/YY'), ID), 'YYYY'||'MM') AS MO_NM,
TO_CHAR(ADD_MONTHS(TO_DATE('01/01/1999', 'DD/MM/YY'), ID), 'MON') AS MO_ABBR_NM,
TO_CHAR(ADD_MONTHS(TO_DATE('01/01/1999', 'DD/MM/YY'), ID), 'MONTH') AS MO_FULL_NM,
TO_CHAR(ADD_MONTHS(TO_DATE('01/01/1999', 'DD/MM/YY'), ID), 'MM')AS MO_NBR,
TO_CHAR(ADD_MONTHS(TO_DATE('01/01/1999', 'DD/MM/YY'), ID), 'YYYY') AS YR_NBR
from Months;
What I really need to do is have this inserted into the temp table that I can recall. I do not have any fields that I can use from other tables either unfortunately. I need it to show 264 months from 1999.
Thank you
You can calculate a date column within the table expression, like this:
WITH Months AS (
SELECT LEVEL -1 AS ID, ADD_MONTHS(TO_DATE('01/01/1999', 'DD/MM/YY'), LEVEL -1) as dt
FROM DUAL
CONNECT BY LEVEL <=264
)
SELECT *
from Months
If you are attempting to create date ranges, you could do this:
WITH Months AS (
SELECT LEVEL -1 AS ID
, ADD_MONTHS(TO_DATE('01/01/1999', 'DD/MM/YY'), LEVEL -1) as start_dt
, ADD_MONTHS(TO_DATE('01/01/1999', 'DD/MM/YY'), LEVEL ) as end_dt
FROM DUAL
CONNECT BY LEVEL <=264
)
SELECT *
from yourtable t
inner join Months m on t.somecol >= m.start_dt and t.somecol < m.end_dt

Calculate average values in Oracle

I want to calculate average values in Oracle tables
CREATE TABLE AGENT_HISTORY(
EVENT_ID INTEGER NOT NULL,
AGENTID INTEGER NOT NULL,
EVENT_DATE DATE NOT NULL
)
/
CREATE TABLE CPU_HISTORY(
CPU_HISTORY_ID INTEGER NOT NULL,
EVENT_ID INTEGER NOT NULL,
CPU_NAME VARCHAR2(50 ) NOT NULL,
CPU_VALUE NUMBER NOT NULL
)
/
I use this SQL query:
----- FOR 24 HOURS CPU
CURSOR LAST_24_CPU_CURSOR IS
--SELECT EVENT_DATE, CPU FROM AGENT_HISTORY WHERE NAME = NAMEIN AND EVENT_DATE >= SYSDATE-(60*24)/1440;
SELECT START_DATE, NVL(AVG(CH.CPU_VALUE),0)
FROM (SELECT START_DATE - (LVL+1)/24 START_DATE, START_DATE - LVL/24 END_DATE
FROM (SELECT SYSDATE START_DATE, LEVEL LVL FROM DUAL CONNECT BY LEVEL <= 24))
LEFT JOIN AGENT_HISTORY AH ON EVENT_DATE BETWEEN START_DATE AND END_DATE
LEFT JOIN CPU_HISTORY CH ON AH.EVENT_ID = CH.EVENT_ID
JOIN AGENT AG ON AH.AGENTID = AG.ID
WHERE AG.NAME = NAMEIN
GROUP BY START_DATE
ORDER BY 1;
This query prints only one average value. I would like to modify it to print 24 values for every hour average value. Can you help me to modify the query?
I guess your input contains data only for one of the given intervals; since you're using an INNER JOIN with AGENT which in turn is filtered by AGENT_HISTORY, you're effectively converting all your LEFT JOINs to inner ones.
I suggest you use a CROSS JOIN between AGENT and the timeslots instead:
with agent_history(event_date, agentid, event_id) as (
select timestamp '2015-11-18 09:00:07', 1, 1001 from dual
),
agent(id, name) as (
select 1, 'myAgent' from dual
),
cpu_history(event_id, cpu_value) as (
select 1001, 75.2 from dual
),
time_slots(start_date, end_date) as (
SELECT START_DATE - (LVL + 1) / 24 START_DATE,
START_DATE - LVL / 24 END_DATE
FROM (SELECT SYSDATE START_DATE,
LEVEL LVL
FROM DUAL
CONNECT BY LEVEL <= 24)
)
SELECT START_DATE,
NVL(AVG(CH.CPU_VALUE),
0)
FROM time_slots ts
CROSS JOIN AGENT AG
LEFT JOIN AGENT_HISTORY AH
ON AH.AGENTID = AG.ID
AND EVENT_DATE BETWEEN START_DATE AND END_DATE
LEFT JOIN CPU_HISTORY CH
ON AH.EVENT_ID = CH.EVENT_ID
WHERE AG.NAME = 'myAgent'
GROUP BY START_DATE
ORDER BY 1;
This ensures you get the full 24 rows (one for each timeslot).
Change start_date to to_char(start_date, 'hh24:mi') both in select and group by clauses.

Calculating Value pro rata over months with Oracle

Say I have a table containing the term of my user subscriptions, like:
user product value start_date end_date
Scott T23K 250 15/01/2014 15/02/2015
Tiger T23K 200 05/01/2014 05/02/2015
Scott T00Z 20 10/01/2014 02/02/2015
...
And I want to build revenue report, based on my billing cycles (monthly) that would look like:
month product value
Jan/2014 T23K 275
Jan/2014 T00Z 19.1
Feb/2014 T23K 275
Feb/2014 T00Z 0.9
I guess I could query like this, though I was wondering if there wasn't a clever analytic function or something that would make it look nicer. Ideas?
I am using Oracle 11gR2
Try this, I think it match your case:
with subscriptions as
(
select 'Scott' user_, 'T23K' product , '250' value , to_date('15/01/2014','DD/MM/YYYY') start_date , to_date('15/02/2015','DD/MM/YYYY') end_date from dual
union all
select 'Tiger' user_, 'T23K' product , '200' value , to_date('05/01/2014','DD/MM/YYYY') start_date , to_date('05/02/2015','DD/MM/YYYY') end_date from dual
union all
select 'Scott' user_, 'T00Z' product , '20' value , to_date('10/01/2014','DD/MM/YYYY') start_date , to_date('02/02/2014','DD/MM/YYYY') end_date from dual
)
, allMonts as
(select add_months(to_date('01/01/2010','DD/MM/YYYY'),rownum) myMonth , lag(add_months(to_date('01/01/2010','DD/MM/YYYY'),rownum)) over (order by rownum desc) -add_months(to_date('01/01/2010','DD/MM/YYYY'),rownum) daymonth from dual CONNECT BY LEVEL <= 100
)
, detail as
(
select s.*,allMonts.myMonth, end_date-start_date duration,
case when trunc(start_date,'MON')=trunc(end_date,'MON') then (end_date-start_date)*value/( end_date-start_date)
when trunc(start_date,'MON')=allMonts.myMonth then (add_months(allMonts.myMonth,1) - start_date)*value/( end_date-start_date)
when trunc(end_date,'MON')=allMonts.myMonth then (end_date - allMonts.myMonth )*value/( end_date-start_date)
else allMonts.daymonth*value/( end_date-start_date)
end value_by_month
from subscriptions s , allMonts
where allMonts.myMonth
between trunc(s.start_date,'MON') and trunc(s.end_date,'MON')+1
)
select myMonth, product, sum(value_by_month)
from detail
group by rollup(myMonth), product

self join with max value

I am have a table with 500k transactions. I want to fetch the last balance for a particular date. So I have have returned a query like below.
SELECT curr_balance
FROM transaction_details
WHERE acct_num = '10'
AND is_deleted = 'N'
AND ( value_date, srl_num ) IN(
SELECT MAX( value_date ), MAX( srl_num )
FROM transaction_details
WHERE TO_DATE( value_date, 'dd/mm/yyyy' )
<= TO_DATE( ADD_MONTHS( '05-APR-2012', 1 ), 'dd/mm/yyyy' )
AND acct_num = '10'
AND is_deleted = 'N'
AND ver_status = 'Y' )
AND ver_status = 'Y'
This has to be executed for incrementing of 12 months to find the last balance for each particular month. But this query is having more cpu cost, 12 times it is taking huge time. How to remodify the above query to get the results in faster way. Whether this can be broken into two part in PL/SQL to achieve the performance. ?
Try:
select * from(
SELECT value_date, srl_num, curr_balance
FROM transaction_details
WHERE acct_num = '10'
AND is_deleted = 'N'
AND ver_status = 'Y'
row_number() over (partition by trunc(value_date - interval '5' day,'MM')
order by srl_num desc
) as rnk
)
where rnk = 1;
You'll get a report with the ballance on last srl_num on each month in your table.
The benefit is that your approach scans the table 24 times for 12 months report and my approach scans the table once.
The analytic function gets the rank of record in current month(partition by clause) ordering the rows in the month after srl_num.
You don't have to query your table twice. Try using analytic functions
SELECT t.curr_balance
-- , any other column you want as long it is in the subselect.
FROM (
SELECT
trans.curr_balance
, trans.value_date
-- any other column you want
, trans.srl_num
, MAX(trans.srl_num) OVER(PARTITION BY trans.value_date, trans.srl_num) max_srl_num
, MAX(trans.value_date) OVER(PARTITION BY trans.value_date, trans.srl_num) max_date
FROM transaction_details trans
WHERE TO_DATE( value_date, 'dd/mm/yyyy' ) <= TO_DATE( ADD_MONTHS( '01-APR-2012', 1 ), 'dd/mm/yyyy' )
AND acct_num = '10'
AND is_deleted = 'N'
AND ver_status = 'Y'
) t
WHERE t.max_date = t.value_date
AND t.max_srl_num = t.srl_num
A couple of thoughts.
Why do you have TO_DATE( value_date...? Isn't your data type DATE? this might be breaking your index if you have one in that column.
Note that (this is a wild guess) if your srl_num is not the highest for the latest date, you will have incorrect results and might not return any rows.

Resources