So basically what this query does is SUM some value, and fetch some data from another table (with 1.8 million records - EDTF007 - the one in the WITH). EDTF011 (main table) has around 800k records.
So what's the problem?
Well... the field that I fetch from EDTF007 has some rules (seen in the CASE clause). I fetch the field CCONTA by making different "INNER JOINS" (as seen in the CASE).
The database cries for 1 hour to run this query. I've ran statistics, I've created indexes (rebuild them), tried access directly to the partitions. And it takes around 1 hour to run this query. Not acceptable at this moment.
The thing is... I'm out of ideas... and the problem is the structure of the query because... the WAIT events that I get... are all related to this bad query structure... :(
Any ideas how could I turn this around? An update on the table is also too slow I think...
WITH IMP AS (SELECT CCONTA, CREFERENCIA FROM EDTF007_IMPARIDADE PARTITION (P_IMPARIDADE_201703))
SELECT SUM(GC.MAVALIAA),
V.ID, NVL(GC.CKNUMCTA, '00000000000'), NVL(GC.CKBALCAO, '0000'), GC.CKPRODUT, GC.CKSUBPRO, GC.ZDEPOSIT, GC.MSLDACT, SUM(GC.MAVALIAA), GC.TIPGARBL, GC.CGARANT, NVL(G.TIPO_GAR, 'SEM GAR'), G.TIPO_GAR2, GC.CREFERENCIA_IMP,
CASE
WHEN (SUBSTR(GC.CKPRODUT, 1, 3) ) IN ('096' , '097' , '020' , '021' , '024') AND GC.CKPRODUT != 'MOR' THEN
(SELECT DISTINCT CCONTA FROM IMP WHERE (GC.CKBALCAO || GC.CKNUMCTA) = CCONTA)
WHEN (SUBSTR(GC.CKPRODUT, 1, 3) ) IN ('035') AND GC.CKPRODUT != 'MOR' THEN
(SELECT DISTINCT CCONTA FROM IMP WHERE GC.ZDEPOSIT = CREFERENCIA)
WHEN NVL(GC.CKBALCAO ||GC.CKNUMCTA, '000000000000000') IN (SELECT IMP.CCONTA FROM RE_ED.EDTF007_IMPARIDADE IMP WHERE (IMP.PRODUTO ) = ( '000' ) AND (IMP.SEGMENTO ) IN ('IE' , 'IP' , 'IA' , 'GI')) AND GC.CKPRODUT != 'MOR' THEN
(SELECT DISTINCT CCONTA FROM IMP WHERE (GC.CKBALCAO || GC.CKNUMCTA) = CCONTA)
WHEN SUBSTR(GC.CKPRODUT, 1, 3) NOT IN ('096' , '097' , '020' , '021' , '024' , '035' ) AND GC.CKPRODUT != 'MOR' THEN
(SELECT DISTINCT CCONTA FROM IMP WHERE (GC.CKBALCAO || GC.CKNUMCTA || GC.CREFERENCIA_IMP) = CCONTA || CREFERENCIA)
WHEN GC.CKPRODUT = 'MOR' THEN
(SELECT DISTINCT CCONTA FROM IMP WHERE (GC.CKBALCAO || GC.CKNUMCTA || GC.CREFERENCIA_IMP) = CCONTA || CREFERENCIA)
ELSE '000000000000000'
END CCONTA_IMP,
GC.CREATED_BY, GC.CREATED_DATE
FROM RE_ED.EDTD011_GARANTIAS_CONTRATO GC,
(SELECT MAX(IDVERSAO) AS ID FROM RE_CD.CDTD009_VERSOES WHERE (TABELA) = ('RE_CD.CDTD015_GARANTIAS_CONTRATO')) V,
(SELECT DISTINCT TIPO_GAR, TIPO_GAR2, CODIGO FROM RE_CD.CDTD011_COD_GARANTIAS WHERE FLAG_ATIVO = 1 AND DTBEGIN_VER <= TO_TIMESTAMP(TRUNC(TO_DATE('2017-03-01', 'YYYY-MM-DD'), 'MM')) AND DTEND_VER >= TO_TIMESTAMP(LAST_DAY(TO_DATE('2017-03-01', 'YYYY-MM-DD')))) G
WHERE (GC.ANO) = ('2017')
AND (GC.MES) = ('03')
AND (GC.CGARANT) = (G.CODIGO)
GROUP BY V.ID, GC.CKNUMCTA, GC.CKBALCAO, GC.CKPRODUT, GC.CKSUBPRO, GC.ZDEPOSIT, GC.MSLDACT, GC.TIPGARBL, GC.CGARANT, G.TIPO_GAR, G.TIPO_GAR2, GC.CREFERENCIA_IMP, GC.CREATED_BY, GC.CREATED_DATE;
EDIT: After some conversation with a collegue he sugested to separate the query universe and instead of 1 query, I should try with 5 insert queries for each case. I'm going forward with this approach. Lets see how it goes. The main goal is a Insert into Select by the way.
Related
I need Only one unique result from tableB.Field to tableA.Field
I am using sdo operator sdo_nn, this is the code:
UPDATE table1 t1
SET t1.fieldA = (SELECT T2.fieldB,SDO_NN_DISTANCE(1) distance
FROM table1 T1, table2 T2
WHERE
(sdo_nn(t1.geometry,t2.geometry,'SDO_NUM_RES=1',1)= 'TRUE')
ORDER BY DIST
)
WHERE EXISTS(
SELECT 1
FROM table2 t2
WHERE sdo_nn(t1.geometry, t2.geometry,'SDO_NUM_RES=1',1)='TRUE'
AND(t2.cell_name = 'string1' or t2.cell_name = string2')AND t1.fieldA = NULL
);
In the select sentence of the subquery i get an error because i only use one field(t1.fieldA), but in the sentence i use the operator SDO_NN_DISTANCE(1) and the sql developer count this operator like another field. What is the correct way to write this sentence? I only use sql because i need to insert this code in vba
Thanks!!!
Obviously, you can't (simplified)
set t1.fieldA = (t2.fieldB, distance) --> you want to put two values into a single column
Therefore, get fieldB alone from the subquery which uses analytic function (row_number) to "sort" rows by sdo_nn_distance(1) desc; then get the first row's fieldB value.
Something like this (I hope I set the parenthesis right):
UPDATE table1 t1
SET t1.fieldA =
(SELECT x.fieldB --> only fieldB
FROM (SELECT T2.fieldB, --> from your subquery
SDO_NN_DISTANCE (1) distance,
ROW_NUMBER ()
OVER (ORDER BY sdo_nn_distance (1) DESC) rn
FROM table1 T1, table2 T2
WHERE (sdo_nn (t1.geometry,
t2.geometry,
'SDO_NUM_RES=1',
1) = 'TRUE')) x
WHERE rn = 1) --> where RN = 1
WHERE EXISTS
(SELECT 1
FROM table2 t2
WHERE sdo_nn (t1.geometry,
t2.geometry,
'SDO_NUM_RES=1',
1) = 'TRUE'
AND ( t2.cell_name = 'string1'
OR t2.cell_name = 'string2')
AND t1.fieldA IS NULL);
I have a requirement to load distinct costcenternum and a seq_key to be inserted. I wrote a procedure but this is failing one is distinct and even after removing distinct not able to run this procedure.
Please help me to correct this query to generate a seq key and also distinct cost numbers into the division table.
CREATE OR REPLACE
PROCEDURE POPULATE_DIVISION_DIM AS
BEGIN
INSERT INTO DIVISION(
"COST_CENTER_KEY"
,"COST_CENTER_NUM"
,"COST_CENTER_DESC"
,"DIVISION_CODE"
,"DIVISION_DESC"
,"COMPANY_CODE"
,"INSERT_DT"
,"UPDATE_DT"
)
(
SELECT
cc_sequence.nextval cost_center_key
, distinct (pcaf.segment4) costcenter_num
,ffvv.description costcenter_desc
,hoi.org_information9 division
,(SELECT description
FROM hr_lookups
WHERE lookup_type = 'CAT'
AND lookup_code = hoi.org_information9)
division_desc
, ppg.segment1 company
,TRUNC(SYSDATE) insert_dt
,TRUNC(SYSDATE) update_dt
FROM
hr_organization_information hoi
, hr_all_organization_units haou
, pay_cost_allocation_keyflex pcaf
, fnd_flex_values_vl ffvv
, per_all_assignments_f paaf
, pay_people_groups ppg
WHERE 1=1
AND paaf.people_group_id = ppg.people_group_id
AND haou.cost_allocation_keyflex_id =
pcaf.cost_allocation_keyflex_id(+)
AND pcaf.segment4 = ffvv.flex_value(+)
AND (ffvv.FLEX_VALUE_SET_ID is null or ffvv.FLEX_VALUE_SET_ID=
(SELECT FLEX_VALUE_SET_ID FROM FND_FLEX_VALUE_SETS WHERE
FLEX_VALUE_SET_NAME = 'ABCD'))
AND ffvv.enabled_flag(+) = 'Y'
AND haou.organization_id = hoi.organization_id
AND hoi.org_information_context = 'XX'
)
;
COMMIT;
END POPULATE_DIVISION_DIM;
I have a oracle query
select id from (
select ID, ROW_NUMBER() over (partition by LATEST_RECEIPT order by ID) rownumber
from Table
where LATEST_RECEIPT in
(
select LATEST_RECEIPT from Table
group by LATEST_RECEIPT
having COUNT(1) > 1
)
) t
where rownumber <> 1;
The data type of LATEST_RECEIPT was earlier varchar2(4000) and this query worked fine. Since the length of the column needs to be extended i modified it to CLOB, after which this fails. Could anyone help me fix this issue or provide a work around?
You can change your inner query to look for other rows with the same last_receipt value but a different ID (assuming ID is unique); if another row exists then that is equivalent to your count returning greater than one. But you can't simply test two CLOB values for equality, you need to use dbms_lob.compare:
select ID
from your_table t1
where exists (
select null from your_table t2
where dbms_lob.compare(t2.LATEST_RECEIPT, t1.LATEST_RECEIPT) = 0
and t2.ID != t1.ID
-- or if ID isn't unique: and t2.ROWID != t1.ROWID
);
Applying the row number filter is tricker, as you also can't use a CLOB in the analytic partition by clause. As André Schild suggested, you can use a hash; here passing the integer value 3, which is the equivalent of dbms_crypto.hash_sh1 (though in theory that could change in a future release!):
select id from (
select ID, ROW_NUMBER() over (partition by dbms_crypto.hash(LATEST_RECEIPT, 3)
order by ID) rownumber
from your_table t1
where exists (
select null from your_table t2
where dbms_lob.compare(t2.LATEST_RECEIPT, t1.LATEST_RECEIPT) = 0
and t2.ID != t1.ID
-- or if ID isn't unique: and t2.ROWID != t1.ROWID
)
)
where rownumber > 1;
It is of course possible to get a hash collision, and if that happened - you had two latest_receipt values which both appeared more than once and both hashed to the same value - then you could get too many rows back. That seems pretty unlikely, but it's something to consider.
So rather than ordering you can only look for rows which have the same lastest_receipt and a lower ID:
select ID
from your_table t1
where exists (
select null from your_table t2
where dbms_lob.compare(t2.LATEST_RECEIPT, t1.LATEST_RECEIPT) = 0
and t2.ID < t1.ID
);
Again that assumes ID is unique. If it isn't then you could still use rowid instead, but you would have less control over which rows were found - the lowest rowid isn't necessarily the lowest ID. Presumably you're using this to dine rows to delete. If you actually don't mind which row you keep and which you delete then you could still do:
and t2.ROWID < t1.ROWID
But since you are currently ordering that probably isn't acceptable, and hashing might be preferable, despite the small risk.
I have a table of transactions in SQLite
number date Category Amount runningBalance
I want the running balance column to have a running sum of the amount column after the table is sorted by Date first and number second.
I can do this with a select when reading. But this table has the potential to get very large and I don't want to recalculate every time. I want to make a trigger where all the transactions following (by date then number) the inserted/edited transaction have their runningBalance value updated.
This will mean that the calculations are reduced... as more recent transactions are likely to be edited more often, and older ones rarely. It also will spread the computation over writes so that reads are near instant.
Can anyone provide assistance on how to set up such a trigger?
so far this is what I have but it does not give desired results. And recalculates all every time. Not just the ones following the change.
CREATE TRIGGER RunningTotal AFTER UPDATE ON Transactions FOR EACH ROW
BEGIN
UPDATE Transactions
SET RunningBalance = (
SELECT (
SELECT sum(Amount)
FROM TopInfo t2
WHERE t2.Date <= t1.Date
)
FROM Transactions t1
);
END;
Thanks!
I've managed to find a way that works. Not sure how efficient it is though. Love to hear if anyone knows a more efficient way to update the Balance column.
CREATE TRIGGER Balance AFTER UPDATE OF Amount ON Transactions FOR EACH ROW
BEGIN
UPDATE Transactions
SET Balance = (
SELECT Balance
FROM (
SELECT TransactionID,
(
SELECT sum(t2.Amount)
FROM Transactions t2
WHERE t2.Date <= t1.Date
ORDER BY Date
)
AS Balance
FROM Transactions t1
WHERE TransactionID = Transactions.TransactionID
ORDER BY Date
)
)
WHERE Transactions.Date >= NEW.Date;
END;
UPDATE:
CREATE TRIGGER Balance AFTER UPDATE OF Amount ON Transactions FOR EACH ROW
BEGIN
UPDATE Transactions
SET Balance = (
SELECT Balance
FROM (
SELECT TransactionID,
(
SELECT sum(t2.Amount)
FROM Transactions t2
WHERE CASE WHEN t2.Date = t1.Date THEN t2.TransactionID <= t1.TransactionID ELSE t2.Date <= t1.Date END
ORDER BY Date,
TransactionID
)
AS Balance
FROM Transactions t1
WHERE TransactionID = Transactions.TransactionID
ORDER BY Date,
TransactionID
)
)
WHERE Transactions.Date >= NEW.Date;
END;
I've Done Some more with running total and have come up with 2 ways. The second is much slower than the first. Any ideas why???
method 1
SELECT TransactionID,Date, Account, Amount,
(SELECT sum(t2.Amount)
FROM Transactions t2
WHERE
CASE WHEN t2.Date = t1.Date
THEN t2.TransactionID <= t1.TransactionID
AND t2.Account == t1.Account
ELSE t2.Date <= t1.Date
AND t2.Account == t1.Account
END
ORDER BY Date, TransactionID)
AS Balance
FROM Transactions t1
ORDER BY Date, TransactionID
Method2
SELECT n.TransactionID, n.Date, n.Account, n.Amount,
SUM(o.Amount) As running_total
FROM Transactions n LEFT JOIN Transactions o
ON (
CASE WHEN o.Date = n.Date
THEN n.TransactionID >= o.TransactionID
AND o.Account == n.Account
ELSE n.Date >= o.Date
AND o.Account == n.Account
END
)
GROUP BY n.Account, n.Date, n.TransactionID
ORDER BY n.Date, n.TransactionID;
I'm working in Teradata to Oracle migration project. How can i modify the below query which is using QUALIFY in Teradata.
//QUERY 1
SELECT S.ID as Id,
S.MP_CD as Code,
S.GM_CD as GmCode,
S.GM_MSR_NBR as Mea_Year,
S.STTS_CD as YearCode,
S.TRMNTN_DTM as TerminationDate
FROM PD.RVY S, LOAD_LOG TLL
WHERE S.UPDTD_LOAD = TLL.LOG_KEY AND TLL.BLSH_CD = 'Y' AND S.STTS_CD IN ( 'C', 'P' )
QUALIFY ROW_NUMBER () OVER (PARTITION BY S.GM_CD ,S.MP_CD ,S.GM_MSR_NBR,S.STTS_CD
ORDER BY S.SO_DTM DESC
) = 1;
//Query 2
SELECT SP.ID,
SP.SO_DTM,
SP.TAX_ID,
SP.USER_ID,
SP.FRST_NM,
SP.LAST_NM,
SP.PHONE_NBR,
QSRP.TAX_ID,
QSRP.ROW_ID,
MAX(SP.SO_DTM) OVER (PARTITION BY SP.ID, SP.TAX_ID) MAX_SO_DTM
FROM VOPR_RMSY SP,VOPR_RMSY_SPNS QSRP
WHERE SP.ID =:URVYID AND QSRP.TAX_ID =:RPAXID
AND SP.ID = QSRP.ID AND SP.TAX_ID = QSRP.TAX_ID AND SP.SO_DTM = QSRP.SO_DTM
QUALIFY (SP.SO_DTM=MAX_SO_DTM AND QSRP.SO_DTM = MAX_SO_DTM)
GROUP BY SP.ID,SP.SO_DTM,SP.TAX_ID,SP.USER_ID,SP.FRST_NM,SP.LAST_NM,SP.PHONE_NBR,
QSRP.TAX_ID,QSRP.ROW_ID;
For this tried with HAVING instead of qualify but got an Error:
ORA-00904: "MAX_SO_DTM": invalid identifier
00904. 00000 - "%s: invalid identifier"
Seems like alias used for MAX is not working here....
Any of your help is really appreciated!
EDITED:
SELECT * FROM
(
SP.ID,
SP.SO_DTM,
SP.TAX_ID,
SP.USER_ID,
SP.FRST_NM,
SP.LAST_NM,
SP.PHONE_NBR,
QSRP.TAX_ID,
QSRP.ROW_ID,
MAX(SP.SO_DTM) OVER (PARTITION BY SP.ID, SP.TAX_ID) AS MAX_SO_DTM
FROM VOPR_RMSY SP,VOPR_RMSY_SPNS QSRP
WHERE SP.ID =:URVYID AND QSRP.TAX_ID =:RPAXID AND SP.ID = QSRP.ID AND SP.TAX_ID =
QSRP.TAX_ID AND SP.SO_DTM = QSRP.SO_DTM
GROUP BY SP.ID,SP.SO_DTM,SP.TAX_ID,SP.USER_ID,SP.FRST_NM,SP.LAST_NM,SP.PHONE_NBR,
QSRP.TAX_ID,QSRP.ROW_ID;
)dt WHERE (SP.SO_DTM=MAX_SO_DTM AND QSRP.SO_DTM = MAX_SO_DTM)
i know that i have to use alias dt for outer WHERE instead of SP and QSRP but here MAX_SO_DTM is compared with SO_DTM from two different tables. is there any other way to modify this?
Thanks!
Both QUALIFY and reusing an alias is Teradata specific.
Your try with HAVING is failing, because this is the logical sequence of processing a query:
FROM
WHERE
GROUP BY
HAVING
OLAP-function
QUALIFY -- Teradata specific
SAMPLE or EXPAND ON -- both are Teradata specific
ORDER
To resolve this you have to use a Derived Table/Inline View and move the QUALIFY condition into the outer WHERE.
SELECT *
FROM
(
SELECT S.ID as Id,
S.MP_CD as Code,
S.GM_CD as GmCode,
S.GM_MSR_NBR as Mea_Year,
S.STTS_CD as YearCode,
S.TRMNTN_DTM as TerminationDate,
ROW_NUMBER () OVER (PARTITION BY S.GM_CD ,S.MP_CD ,S.GM_MSR_NBR,S.STTS_CD
ORDER BY S.SO_DTM DESC) AS rn
FROM PD.RVY S, LOAD_LOG TLL
WHERE S.UPDTD_LOAD = TLL.LOG_KEY AND TLL.BLSH_CD = 'Y' AND S.STTS_CD IN ( 'C', 'P' )
) dt
WHERE rn = 1;
The same technique must be used when you want to reuse an alias.
EDIT:
The 2nd query can be rewritten as:
SELECT *
FROM
(
SELECT
SP.ID,
SP.SO_DTM,
SP.TAX_ID,
SP.USER_ID,
SP.FRST_NM,
SP.LAST_NM,
SP.PHONE_NBR,
QSRP.TAX_ID,
QSRP.ROW_ID,
MAX(SP.SO_DTM) OVER (PARTITION BY SP.ID, SP.TAX_ID) AS MAX_SO_DTM
FROM VOPR_RMSY SP,VOPR_RMSY_SPNS QSRP
WHERE SP.ID =:URVYID
AND QSRP.TAX_ID =:RPAXID
AND SP.ID = QSRP.ID
AND SP.TAX_ID = QSRP.TAX_ID
AND SP.SO_DTM = QSRP.SO_DTM
GROUP BY SP.ID,SP.SO_DTM,SP.TAX_ID,SP.USER_ID,SP.FRST_NM,SP.LAST_NM,SP.PHONE_NBR,
QSRP.TAX_ID,QSRP.ROW_ID
)dt
WHERE SO_DTM=MAX_SO_DTM
You don't need to do both comparisons *(SP.SO_DTM=MAX_SO_DTM AND QSRP.SO_DTM = MAX_SO_DTM)*, because they the 2nd is redundant as the tables are joined on *SP.SO_DTM = QSRP.SO_DTM*.
Otherwise you had to add QSRP.SO_DTM to the Derived Table using a different alias (e.g. QSRP_SO_DTM). In the outer level there's no more SP/QSRP but only dt, so it would be:
WHERE (SO_DTM=MAX_SO_DTM AND QSRP_SO_DTM = MAX_SO_DTM)