Slow query with WITH and JOIN
Hi,
This code is taking too much time to complete :
WITH rawData AS -- 563 rows in 0.07s OR 59 rows in 0.02s
SELECT
date_releve AS x1,
index_corrige AS y1,
LEAD(date_releve) OVER (PARTITION BY id_compteur ORDER BY date_releve) AS x2,
LEAD(index_corrige) OVER (PARTITION BY id_compteur ORDER BY date_releve) AS y2,
id AS id_releve,
id_compteur
FROM V_relevesCorriges
meteoData AS -- 1082 rows in 1.34s OR 116 rows in 0.16s
SELECT avg(meteo.valeur) AS meteoValue, x2 AS dateMeteo, id_variable, id_releve, id_compteur
FROM meteo, rawData
WHERE date_meteo <= x2 AND date_meteo > x1
GROUP BY id_releve, id_variable, x2, id_compteur
ORDER BY x2
consoData AS -- 1104 rows in 1.43s, 117 rows in 0.2s
SELECT
to_char(x1, 'DD.MM.YYYY') || ' - ' || to_char(x2, 'DD.MM.YYYY') AS periode,
meteoValue AS meteo_moyenne,
(y2 - y1) / nullif((x2 - x1),0) AS conso_par_jour,
(y2 - y1) AS conso,
rawData.id_releve id_releve,
meteoData.id_variable id_variable,
meteoData.id_compteur id_compteur
FROM rawData LEFT OUTER JOIN meteoData ON rawData.id_releve = meteoData.id_releve
ORDER BY x2
SELECT periode, meteo_moyenne, conso_par_jour, consoData.id_variable id_variable, consoData.id_releve id_releve, id_compteur -- 1104 rows in 1.51s, 116 rows in 1.34s
FROM consoData LEFT OUTER JOIN diagnostic2 ON consoData.id_releve = diagnostic2.id_releve AND consoData.id_variable = diagnostic2.id_variable
WHERE Id_compteur = 4If I remove "WHERE Id_compteur = 4" on the last line, it makes almost no difference in execution time. Without this WHERE clause, it returns 1104 rows in 1.51s, with it it returns 116 rows in 1.34s.
I'm saying that it takes too long because when I move this WHERE clause into "consoData" (WHERE meteoData.consoData=4), it returns 116rows in 0.2s, for getting the same data output. If I remove the LEFT OUTER JOIN diagnostic2 (last but one line) it also takes 0.2s.
I think that the solution would be to force "WHERE Id_compteur=..." to take effect before it joins the "diagnostic2" table, but don't know how to do it.
The subquery that takes much time when returning all rows is "meteoData".
This code is meant to be a VIEW, so "WHERE Id_compteur=..." will not be included in it but passed when querying the view. I tested it as a VIEW, same problem.
Explain plan :
Plan hash value: 724835998
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
| 0 | SELECT STATEMENT | | 16364 | 1342K| | 586 (4)| 00:00:08 |
| 1 | TEMP TABLE TRANSFORMATION | | | | | | |
| 2 | LOAD AS SELECT | DIAGNOSTIC2 | | | | | |
| 3 | WINDOW SORT | | 563 | 15764 | | 12 (25)| 00:00:01 |
| 4 | VIEW | V_RELEVESCORRIGES | 563 | 15764 | | 11 (19)| 00:00:01 |
| 5 | SORT GROUP BY | | 563 | 56300 | | 11 (19)| 00:00:01 |
| 6 | VIEW | | 563 | 56300 | | 10 (10)| 00:00:01 |
|* 7 | HASH JOIN RIGHT OUTER | | 563 | 25335 | | 10 (10)| 00:00:01 |
| 8 | TABLE ACCESS FULL | COMPTEURS | 22 | 132 | | 3 (0)| 00:00:01 |
| 9 | VIEW | | 563 | 21957 | | 7 (15)| 00:00:01 |
|* 10 | HASH JOIN OUTER | | 563 | 26461 | | 7 (15)| 00:00:01 |
| 11 | TABLE ACCESS FULL | RELEVES | 563 | 12949 | | 3 (0)| 00:00:01 |
| 12 | VIEW | V_CORRECTIONDATA | 563 | 13512 | | 3 (0)| 00:00:01 |
|* 13 | VIEW | | 563 | 28150 | | 3 (0)| 00:00:01 |
| 14 | WINDOW SORT | | 563 | 67560 | | 3 (0)| 00:00:01 |
| 15 | VIEW | | 563 | 67560 | | 3 (0)| 00:00:01 |
| 16 | NESTED LOOPS OUTER| | 563 | 14638 | | 3 (0)| 00:00:01 |
| 17 | TABLE ACCESS FULL| RELEVES | 563 | 12949 | | 3 (0)| 00:00:01 |
|* 18 | INDEX UNIQUE SCAN| COMPTEURS_PK | 1 | 3 | | 0 (0)| 00:00:01 |
|* 19 | HASH JOIN RIGHT OUTER | | 16364 | 1342K| | 573 (4)| 00:00:07 |
| 20 | INDEX FULL SCAN | DIAGNOSTIC2_PK | 4 | 24 | | 1 (0)| 00:00:01 |
| 21 | VIEW | | 16364 | 1246K| | 572 (4)| 00:00:07 |
| 22 | SORT ORDER BY | | 16364 | 1661K| 3864K| 572 (4)| 00:00:07 |
|* 23 | HASH JOIN | | 16364 | 1661K| | 179 (9)| 00:00:03 |
| 24 | VIEW | | 1157 | 55536 | | 3 (0)| 00:00:01 |
| 25 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6657_90A96D1D | 1157 | 55536 | | 3 (0)| 00:00:01 |
|* 26 | VIEW | | 7963 | 435K| | 175 (8)| 00:00:03 |
| 27 | SORT GROUP BY | | 7963 | 311K| 1768K| 175 (8)| 00:00:03 |
| 28 | MERGE JOIN | | 26409 | 1031K| | 23 (48)| 00:00:01 |
| 29 | SORT JOIN | | 1157 | 28925 | | 4 (25)| 00:00:01 |
| 30 | VIEW | | 1157 | 28925 | | 3 (0)| 00:00:01 |
| 31 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6657_90A96D1D | 1157 | 55536 | | 3 (0)| 00:00:01 |
|* 32 | FILTER | | | | | | |
|* 33 | SORT JOIN | | 9130 | 133K| | 11 (19)| 00:00:01 |
| 34 | TABLE ACCESS FULL | METEO | 9130 | 133K| | 9 (0)| 00:00:01 |
Predicate Information (identified by operation id):
7 - access("RELEVES"."ID_COMPTEUR"="COMPTEURS"."ID"(+))
10 - access("RELEVES"."ID_COMPTEUR"="V_CORRECTIONDATA"."ID_COMPTEUR"(+))
filter("RELEVES"."DATE_RELEVE">="V_CORRECTIONDATA"."DATE_CHANGEMENT"(+))
13 - filter("CHG_COMPTEUR"=1 AND "ID_COMPTEUR"="ID_COMPTEUR_CORR")
18 - access("RELEVES"."ID_COMPTEUR"="COMPTEURS"."ID"(+))
19 - access("CONSODATA"."ID_VARIABLE"="DIAGNOSTIC2"."ID_VARIABLE"(+) AND
"CONSODATA"."ID_RELEVE"="DIAGNOSTIC2"."ID_RELEVE"(+))
23 - access("RAWDATA"."ID_RELEVE"="METEODATA"."ID_RELEVE")
26 - filter("METEODATA"."ID_COMPTEUR"=4)
32 - filter("DATE_METEO">"X1")
33 - access(INTERNAL_FUNCTION("DATE_METEO")<=INTERNAL_FUNCTION("X2"))
filter(INTERNAL_FUNCTION("DATE_METEO")<=INTERNAL_FUNCTION("X2"))Oracle database version : 10.2.0.4.0, I'm accessing it through APEX version 4.1.1.00.23
I hope that my question is not too fuzzy..
Perhaps you need to push the filter on id_compteur up to the rawdata subquery?
Try:
WITH rawdata
AS -- 563 rows in 0.07s OR 59 rows in 0.02s
(SELECT date_releve AS x1,
index_corrige AS y1,
LEAD(date_releve)
OVER (PARTITION BY id_compteur ORDER BY date_releve)
AS x2,
LEAD(index_corrige)
OVER (PARTITION BY id_compteur ORDER BY date_releve)
AS y2,
id AS id_releve,
id_compteur
FROM v_relevescorriges
WHERE id_compteur = 4),
meteodata
AS -- 1082 rows in 1.34s OR 116 rows in 0.16s
(SELECT AVG(meteo.valeur) AS meteovalue,
x2 AS datemeteo,
id_variable,
id_releve,
id_compteur
FROM meteo, rawdata
WHERE date_meteo <= x2
AND date_meteo > x1
GROUP BY id_releve,
id_variable,
x2,
id_compteur
ORDER BY x2),
consodata
AS -- 1104 rows in 1.43s, 117 rows in 0.2s
(SELECT TO_CHAR(x1, 'DD.MM.YYYY') || ' - ' || TO_CHAR(x2, 'DD.MM.YYYY')
AS periode,
meteovalue AS meteo_moyenne,
(y2 - y1) / NULLIF((x2 - x1), 0) AS conso_par_jour,
(y2 - y1) AS conso,
rawdata.id_releve id_releve,
meteodata.id_variable id_variable,
meteodata.id_compteur id_compteur
FROM rawdata
LEFT OUTER JOIN meteodata
ON rawdata.id_releve = meteodata.id_releve
ORDER BY x2)
SELECT periode,
meteo_moyenne,
conso_par_jour,
consodata.id_variable id_variable,
consodata.id_releve id_releve,
id_compteur -- 1104 rows in 1.51s, 116 rows in 1.34s
FROM consodata
LEFT OUTER JOIN diagnostic2
ON consodata.id_releve = diagnostic2.id_releve
AND consodata.id_variable = diagnostic2.id_variable;
Similar Messages
-
Slow Query only with some values??????
I have this tables:
Vehicles: id_vehicle, id_customer, registration_number, model, ... (2.000.000 of records) (1.500.000 distinct id_customer)
Insurance-Customers-Vehicles: id_insurance, id_customer, id_vehicle ... (4.500.000 of records)
Insurance: id_insurance, date_insurance, ...
Customers: id_customer, name, surname, ..
QUERY:
select
vh.id_vehicle,
vh.id_customer,
vd.model,
vd.registration_number
from Insurance-Customers-Vehicles vh
left join Vehicles v on (vh.id_vehicle = v.id_vehicle and vh.id_customer = v.id_customer)
inner join Vehicles vd on (vh.cod_vehiculo = vd.cod_vehiculo)
where vh.id_customer = 123456
and vh.id_vehicle is null
I execute this query because I want to obtain Vehicles that have been associated with a customer but I don’t want to obtain the vehicle-customer on Insurance-Customers-Vehicles table if it’s now associated to the vehicle-customer on vehicles table.
This query is very fast at 99% of the time (less than 1 second), but when a client has many vehicles associated (more than 50) on Insurance-Customers-Vehicles table the query is very slow and takes between 10 and 20 seconds ...
Insurance-Customers-Vehicles
Please, How can I improve it?????
Thanks in advance!!!!!
Operation Object Name Rows Bytes Cost Object Node In/Out PStart PStop
SELECT STATEMENT Optimizer Mode=CHOOSE 3 12
NESTED LOOPS 3 498 12
FILTER
NESTED LOOPS OUTER
TABLE ACCESS BY INDEX ROWID Insurance-Customers-Vehicles 3 408 6
INDEX RANGE SCAN IND_INSCUVEH_ID_CUSTOMER 3 3
TABLE ACCESS BY INDEX ROWID VEHICLES 1 13 2
INDEX UNIQUE SCAN PK_VEHICLES 1 1
TABLE ACCESS BY INDEX ROWID VEHICLES 1 17
INDEX UNIQUE SCAN PK_VEHICLES 1 1I think there is something logically wrong with this query.
You left join the vehicle table, however you already know that id_vehicle is NULL.
I'm not sure what it should achieve, maybe you should replace this join with an EXISTS sub select, maybe you can remove this join completely.
Try to find an example where your query gives a different result than this one. And explain me why the result is different.
select vh.id_vehicle,
vh.id_customer,
vd.model,
vd.registration_number
from Insurance-Customers-Vehicles vh
inner join Vehicles vd on (vh.cod_vehiculo = vd.cod_vehiculo)
where vh.id_customer = 123456
and vh.id_vehicle is null;Message was edited by:
Sven W. -
Slow query times with "contains" and "or"
We're running Oracle 9.2.0.4 on RHEL 3
I have a simple table - "docinfo". I've create a multicolumn Text index for docinfo called "repoidx". I have five cases below with the fourth one being the most difficult to understand. I have a primary key for "docinfo" but do nott have any additional indexes on "docinfo" right now because we're still testing the design. I'm curious about what is magical about using "or" plus "contains" in the same query (case 4).
[case 1 - simple like]
select count(docid)
from sa.docinfo
where
author like '%smith%'
Elapsed: 00:00:00.02
Execution Plan
0 SELECT STATEMENT Optimizer=ALL_ROWS (Cost=1468 Card=1 Bytes=15)
1 0 SORT (AGGREGATE)
2 1 TABLE ACCESS (FULL) OF 'DOCINFO' (Cost=1468 Card=12004 Bytes=180060)
[case 2 - simple contains]
select count(docid)
from sa.docinfo
where contains(repoidx,'facts')>0
Elapsed: 00:00:01.00
Execution Plan
0 SELECT STATEMENT Optimizer=ALL_ROWS (Cost=3905 Card=1 Bytes=12)
1 0 SORT (AGGREGATE)
2 1 TABLE ACCESS (BY INDEX ROWID) OF 'DOCINFO' (Cost=3905 Card=21278 Bytes=255336)
3 2 DOMAIN INDEX OF 'IDX_DOCINFO_REPOIDX' (Cost=3549)
[case 3 - simple like _and_ simple contains]
select count(docid)
from sa.docinfo
where
contains(repoidx,'facts')>0
and
author like '%smith%'
Elapsed: 00:00:00.02
Execution Plan
0 SELECT STATEMENT Optimizer=ALL_ROWS (Cost=3905 Card=1 Bytes= 23)
1 0 SORT (AGGREGATE)
2 1 TABLE ACCESS (BY INDEX ROWID) OF 'DOCINFO' (Cost=3905 Card=1064 Bytes=24472)
3 2 DOMAIN INDEX OF 'IDX_DOCINFO_REPOIDX' (Cost=3549)
[case 4 - simple like _or_ simple contains]
select count(docid)
from sa.docinfo
where
contains(repoidx,'facts')>0
or
author like '%smith%'
Elapsed: 00:01:37.02
Execution Plan
0 SELECT STATEMENT Optimizer=ALL_ROWS (Cost=1468 Card=1 Bytes= 23)
1 0 SORT (AGGREGATE)
2 1 TABLE ACCESS (FULL) OF 'DOCINFO' (Cost=1468 Card=32218 Bytes=741014)
[case 5 - simple like union simple contains]
select count(docid)
from sa.docinfo
where
contains(repoidx,'facts')>0
union
select count(docid)
from sa.docinfo
where
author like '%smith%'
Elapsed: 00:00:00.04
Execution Plan
0 SELECT STATEMENT Optimizer=ALL_ROWS (Cost=5581 Card=2 Bytes= 27)
1 0 SORT (UNIQUE) (Cost=5581 Card=2 Bytes=27)
2 1 UNION-ALL
3 2 SORT (AGGREGATE) (Cost=4021 Card=1 Bytes=12)
4 3 TABLE ACCESS (BY INDEX ROWID) OF 'DOCINFO' (Cost=3905 Card=21278 Bytes=255336)
5 4 DOMAIN INDEX OF 'IDX_DOCINFO_REPOIDX' (Cost=3549)
6 2 SORT (AGGREGATE) (Cost=1560 Card=1 Bytes=15)
7 6 TABLE ACCESS (FULL) OF 'DOCINFO' (Cost=1468 Card=12004 Bytes=180060)Case 1:
There is no index on author and it would not be able to use one if there was, due to the leading %, so it does a full table scan, which is still quick, since that is all there is to the query.
Case 2:
It has an index on repoidx, so it uses it and it is quick.
Case 3:
It has an index on repoidx, so it uses it. Since "and" is used, both conditions must be met. It has quckly obtained the results that match the first condition using the index, so it only has to check those rows, not every row in the table, to see if they also match the second condition.
Case 4:
Either condition may be met. It does not have an index on author, so it cannot use an index for that conditiion. Either condition can be met and it cannot duplicate the rows where both conditions are met, so it cannot use the results of one condition to check the other. So, it has to do a full table scan, in order to check every row for either condition, so it is slow.
Case 5:
select count (docid)
from docinfo
where contains (repoidx, 'facts') > 0
union
select count (docid)
from docinfo
where author like '%smith%';is not the same as:
select count (docid)
from (select docid
from docinfo
where contains (repoidx, 'facts') > 0
union
select docid
from docinfo
where author like '%smith%');which is the same as case 4 and therefore just as slow. Your case 5 is just taking the union of 2 numbers, which could result in one row or two rows, depending on whether the numbers happen to match or not. Consider the following:
scott@ORA92> SELECT job, empno
2 FROM emp
3 /
JOB EMPNO
CLERK 7369
SALESMAN 7499
SALESMAN 7521
MANAGER 7566
SALESMAN 7654
MANAGER 7698
MANAGER 7782
ANALYST 7788
PRESIDENT 7839
SALESMAN 7844
CLERK 7876
CLERK 7900
ANALYST 7902
CLERK 7934
14 rows selected.
scott@ORA92> SELECT job, COUNT (empno)
2 FROM emp
3 GROUP BY job
4 /
JOB COUNT(EMPNO)
ANALYST 2
CLERK 4
MANAGER 3
PRESIDENT 1
SALESMAN 4
scott@ORA92> SELECT COUNT (empno)
2 FROM emp
3 WHERE job = 'SALESMAN'
4 /
COUNT(EMPNO)
4
scott@ORA92> SELECT COUNT (empno)
2 FROM emp
3 WHERE job = 'CLERK'
4 /
COUNT(EMPNO)
4
scott@ORA92> SELECT COUNT (empno)
2 FROM emp
3 WHERE job = 'SALESMAN'
4 UNION
5 SELECT COUNT (empno)
6 FROM emp
7 WHERE job = 'CLERK'
8 /
COUNT(EMPNO)
4
scott@ORA92> -- the above is the same as:
scott@ORA92> SELECT 4 FROM DUAL
2 UNION
3 SELECT 4 FROM DUAL
4 /
4
4
scott@ORA92> -- it is not the same as:
scott@ORA92> SELECT COUNT (empno)
2 FROM (SELECT empno
3 FROM emp
4 WHERE job = 'SALESMAN'
5 UNION
6 SELECT empno
7 FROM emp
8 WHERE job = 'CLERK')
9 /
COUNT(EMPNO)
8
scott@ORA92> -- if the numbers are different, you get 2 rows:
scott@ORA92> SELECT COUNT (empno)
2 FROM emp
3 WHERE job = 'ANALYST'
4 UNION
5 SELECT COUNT (empno)
6 FROM emp
7 WHERE job = 'MANAGER'
8 /
COUNT(EMPNO)
2
3
scott@ORA92> -- the above is the same as:
scott@ORA92> SELECT 2 FROM DUAL
2 UNION
3 SELECT 3 FROM DUAL
4 /
2
2
3
scott@ORA92> -- it is not the same as:
scott@ORA92> SELECT COUNT (empno)
2 FROM (SELECT empno
3 FROM emp
4 WHERE job = 'ANALYST'
5 UNION
6 SELECT empno
7 FROM emp
8 WHERE job = 'MANAGER')
9 /
COUNT(EMPNO)
5 -
Select with transposition and join
Hi Guys!
I'm using Oracle 9i and I'm facing the following problem:
My table looks like this:
Stripe
Unit
Value
1
1
a1
1
2
a2
1
3
a3
2
1
b1
2
2
b2
2
3
b3
2
4
b4
4
1
c1
4
2
c2
4
3
c3
4
4
c4
4
5
c5
My result should look like this:
Unit
Stripe1 Value
Stripe2 Value
Stripe3 Value
Stripe4 Value
1
a1
b1
c1
2
a2
b2
c2
3
a3
b3
c3
4
b4
c4
5
c5
I tried it with one select for each stripe and full joins, but in this example I would only see the first 3 units....
Any ideas?Another way, but only if your on database version 11gR1 and onwards, is to use the PIVOT operator:
SQL> -- generating sample date:
SQL> with t as (
2 select 1 stripe, 1 unit, 'a1' value from dual union
3 select 1, 2, 'a2' from dual union
4 select 1, 3, 'a3' from dual union
5 select 2, 1, 'b1' from dual union
6 select 2, 2, 'b2' from dual union
7 select 2, 3, 'b3' from dual union
8 select 2, 4, 'b4' from dual union
9 select 4, 1, 'c1' from dual union
10 select 4, 2, 'c2' from dual union
11 select 4, 3, 'c3' from dual union
12 select 4, 4, 'c4' from dual union
13 select 4, 5, 'c5' from dual
14 )
15 --
16 -- actual query:
17 --
18 select *
19 from ( select unit
20 , stripe
21 , value
22 from t
23 )
24 pivot (max(value) for (stripe) in ( 1 as stripe1value
25 , 2 as stripe2value
26 , 3 as stripe3value
27 , 4 as stripe4value
28 )
29 )
30 order by unit;
UNIT ST ST ST ST
1 a1 b1 c1
2 a2 b2 c2
3 a3 b3 c3
4 b4 c4
5 c5
5 rows selected.
ORACLE-BASE - PIVOT and UNPIVOT Operators in Oracle Database 11g Release 1 -
UPDATE Statement with subquerry and join
I have 2 tables (say, employee and dept). employee.empno and dept.empno.
These tables are not joined.
I want to update the first name (fname) in employee WHERE employee.empno = dept.empno
My query is :
UPDATE emp SET fname='jack' WHERE exists(select * from emp,dept where emp.empno = dept.empno)
But this query updates all the records.... even where there is no corresponding record in dept
For example, emp has empno values of 10,20,30,40 and dept has empno values of 10 and 20 only,
yet all records in emp are updated.
Can someone help me ???
nullThat is because an UPDATE statemente without a WHERE clause will update all of the rows.
There are three parts to an UPDATE
UPDATE tablename
SET columnlist
WHERE
The UPDATE clause specifies the table to update.
The SET clause specifies the columns to update and the values to use to update them.
The WHERE clause specified which records in the table to update.
No WHERE clause means update all rows.
The correlated subquery in the SET clause has nothing to do with the WHERE clause that determines which rows get updated. You have to be very careful to also include a WHERE clause (that often duplicates the WHERE clause in the subquery) or you will set columns to NULL that don't get values returned from the subquery.
Alias the emp table being updated and do not include the emp table in the subquery.
Also I suggest you use 'X' (or any constant that is not actually in the table) in the EXISTS test.
UPDATE emp e SET fname='jack' WHERE exists(select 'x' from dept where e.empno = dept.empno)
WHERE exists (select 'x' from dept where e.empno = dept.empno -
Slow Query time with Function in Group By
I have a PL/SQL function that computes status based on several inputs. When the function is run in a standard query without a group by, it is very fast. When i try to count or sum other columns in the select (thus requiring the Group By), my query response time explodes exponentially.
My query:
SELECT
ben.atm_class( 'DBT', 'CLA' , 6 , 1245 ),
count (distinct ax.HOUSEHOLD_KEY)
FROM
ADM.PRODUCT p,
ADM.ACCOUNT_CROSS_FUNCTIONAL ax
WHERE
ax.month_key = 1245
AND ( ax.PRODUCT_KEY=ADM.P.PRODUCT_KEY )
AND ( ax.HOUSEHOLD_KEY ) IN (6)
group by
p.ptype, p.stype,
ben.atm_class( 'DBT', 'CLA' , 6 , 1245 )
My explain plan for the query with the Group By:
Operation Object Name Rows Bytes Cost Object Node In/Out PStart PStop
SELECT STATEMENT Optimizer Mode=CHOOSE 3 10
SORT GROUP BY 3 60 10
NESTED LOOPS 3 60 6
TABLE ACCESS BY LOCAL INDEX ROWID ACCOUNT_CROSS_FUNCTIONAL 3 33 3 23 23
INDEX RANGE SCAN NXIF312ACCOUNT_CROSS_FUNCTION 3 2 23 23
TABLE ACCESS BY INDEX ROWID PRODUCT 867 7 K 1
INDEX UNIQUE SCAN PK_PRODUCT_PRODUCTKEY 867
This executes in over 9 minutes.
My query w/o Group by
SELECT
ben.atm_class( 'DBT', 'CLA' , 6 , 1245 ),
ax.HOUSEHOLD_KEY
FROM
ADM.PRODUCT p,
ADM.ACCOUNT_CROSS_FUNCTIONAL ax
WHERE
ax.month_key = 1245
AND ( ax.PRODUCT_KEY=ADM.P.PRODUCT_KEY )
AND ( ax.HOUSEHOLD_KEY ) IN (6)
My explain plan without the Group By:
Operation Object Name Rows Bytes Cost Object Node In/Out PStart PStop
SELECT STATEMENT Optimizer Mode=CHOOSE 3 3
NESTED LOOPS 3 42 3
TABLE ACCESS BY LOCAL INDEX ROWID ACCOUNT_CROSS_FUNCTIONAL 3 33 3 23 23
INDEX RANGE SCAN NXIF312ACCOUNT_CROSS_FUNCTION 3 2 23 23
INDEX UNIQUE SCAN PK_PRODUCT_PRODUCTKEY 867 2 K
This executes in 6 seconds
Any thoughts on why it takes 90 times longer to perform the Group By sort?The plan didn't paste:
no group by:
Operation Object Name Rows Bytes Cost Object Node In/Out PStart PStop
SELECT STATEMENT Optimizer Mode=CHOOSE 3 6
NESTED LOOPS 3 60 6
TABLE ACCESS BY LOCAL INDEX ROWID ACCOUNT_CROSS_FUNCTIONAL 3 33 3 23 23
INDEX RANGE SCAN NXIF312ACCOUNT_CROSS_FUNCTION 3 2 23 23
TABLE ACCESS BY INDEX ROWID PRODUCT 867 7 K 1
INDEX UNIQUE SCAN PK_PRODUCT_PRODUCTKEY 867
group by:
Operation Object Name Rows Bytes Cost Object Node In/Out PStart PStop
SELECT STATEMENT Optimizer Mode=CHOOSE 3 10
SORT GROUP BY 3 60 10
NESTED LOOPS 3 60 6
TABLE ACCESS BY LOCAL INDEX ROWID ACCOUNT_CROSS_FUNCTIONAL 3 33 3 23 23
INDEX RANGE SCAN NXIF312ACCOUNT_CROSS_FUNCTION 3 2 23 23
TABLE ACCESS BY INDEX ROWID PRODUCT 867 7 K 1
INDEX UNIQUE SCAN PK_PRODUCT_PRODUCTKEY 867 -
Error in UPDATE statement with SET and JOIN
Hi
UPDATE lms_assessment_student QS JOIN lms_assessment_student_ans QA ON QS.pk_Assessment_Stud_Id = QA.fk_Assessment_Stud_Id SET QA.Mark = 1, QA.Comment_Field = 1 WHERE QS.pk_Assessment_Stud_Id = 1 AND QA.Question_Id = 1;
The above statement when executing is showing ORA-00971: missing SET keyword. so i changed it to
UPDATE lms_assessment_student QS SET QA.Mark = 1, QA.Comment_Field = 1 WHERE QS.pk_Assessment_Stud_Id = 1 AND QA.Question_Id = 1 JOIN lms_assessment_student_ans QA ON QS.pk_Assessment_Stud_Id = QA.fk_Assessment_Stud_Id ;
and it showing ORA-00933: SQL command not properly ended.So can anyone help me in solving this problem
Thanking you in advance
DinnyHi ,
So many errors
YOUR QUERY :
UPDATE lms_assessment_student QS SET QA.Mark = 1, QA.Comment_Field = 1 WHERE QS.pk_Assessment_Stud_Id = 1 AND QA.Question_Id = 1 JOIN lms_assessment_student_ans QA ON QS.pk_Assessment_Stud_Id = QA.fk_Assessment_Stud_Id ;
and it showing ORA-00933: SQL command not properly ended.So can anyone help me in solving this problem
first thing u want to update qa and u write update QS ??
Second y do u want to join??? just put the condition
SOLUTION
UPDATE lms_assessment_student_ans
SET lms_assessment_student_ans.Mark = 1,
lms_assessment_student_ans.Comment_Field = 1
WHERE lms_assessment_student.pk_Assessment_Stud_Id = 1
AND lms_assessment_student.pk_Assessment_Stud_Id = QA.fk_Assessment_Stud_Id
Hope it works for u..
Bhavesh -
Where clause with XMLExists and join on another table
Hi,
We have table like:
drop table xml_tbl;
create table xml_tbl (
xml_msg_id integer,
xml_msg_text xmltype
insert into xml_tbl values
(1, '<main><id>1</id></main>') ;
insert into xml_tbl values --(xml_msg_id,xml_msg_text)
(1, '<main><id>2</id></main>') ;
Another table like:
create Table Table1
( id1 int);
Insert into Table1 values(2);
Insert into Table1 values(3);
We need to have a view on top of the table xml_tbl where /main/id should have only those values which are in id1 column of table Table1.
Something like
CREATE OR REPLACE VIEW V_xml_tbl
xml_msg_text
AS
SELECT T.xml_msg_text
FROM xml_tbl T
WHERE XMLEXISTS (
'declare namespace Namesp1 ="Abc:Set";
let $Results as xs:boolean := fn:exists($p/main/id in (Select id1 from Table1)) --Now here I know I can't do Select id1 from
Table1*
return if ($Results ) then true() else ()'
PASSING T.xml_msg_text AS "p");
Actually in the real scenario Table1 will have many IDs and xml_tbl has many XML files..
So I am stuck on how to do it. Please help.
Thanks..
Edited by: user8941550 on Nov 20, 2012 7:19 PMOne of these two :
SQL> select t.xml_msg_text
2 from xml_tbl t
3 where exists (
4 select null
5 from table1 t1
6 where t1.id1 = xmlcast(
7 xmlquery('/main/id' passing t.xml_msg_text returning content)
8 as integer
9 )
10 );
XML_MSG_TEXT
<main>
<id>2</id>
</main>
SQL> select t.xml_msg_text
2 from xml_tbl t
3 , xmltable('/main' passing t.xml_msg_text
4 columns id integer path 'id'
5 ) x
6 where exists (
7 select null
8 from table1 t1
9 where t1.id1 = x.id
10 );
XML_MSG_TEXT
<main>
<id>2</id>
</main>
And a third one, using XMLExists :
SQL> select t.xml_msg_text
2 from xml_tbl t
3 where xmlexists (
4 'fn:collection("oradb:/DEV/TABLE1")/ROW[ID1=$d/main/id]'
5 passing t.xml_msg_text as "d"
6 );
XML_MSG_TEXT
<main>
<id>2</id>
</main>
Execution Plan
Plan hash value: 3633580934
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
| 0 | SELECT STATEMENT | | 2 | 116 | 8 (0)| 00:00:01 |
|* 1 | FILTER | | | | | |
| 2 | TABLE ACCESS FULL | XML_TBL | 2 | 116 | 3 (0)| 00:00:01 |
| 3 | NESTED LOOPS | | 1 | 5 | 5 (0)| 00:00:01 |
| 4 | TABLE ACCESS FULL| TABLE1 | 2 | 6 | 3 (0)| 00:00:01 |
|* 5 | XPATH EVALUATION | | | | | |
Predicate Information (identified by operation id):
1 - filter( EXISTS (SELECT 0 FROM "DEV"."TABLE1"
"SYS_ORAVW_2",XPATHTABLE('/main/id' PASSING :B1 COLUMNS "C_00$" XMLTYPE
PATH '.', "C_01$" XQEXVAL CHAR PATH '.') "P" WHERE
TO_BINARY_DOUBLE("ID1")=TO_BINARY_DOUBLE("P"."C_01$")))
5 - filter(TO_BINARY_DOUBLE("ID1")=TO_BINARY_DOUBLE("P"."C_01$"))The plan is similar to that of the second query above (XMLTable/EXISTS).
Still using XMLExists, a plan similar to the first query (EXISTS/XMLCast/XMLQuery) can be achieved by casting id to an integer datatype :
SQL> select t.xml_msg_text
2 from xml_tbl t
3 where xmlexists (
4 'fn:collection("oradb:/DEV/TABLE1")/ROW[ID1=xs:int($d/main/id)]'
5 passing t.xml_msg_text as "d"
6 );
XML_MSG_TEXT
<main>
<id>2</id>
</main>
Execution Plan
Plan hash value: 1149640166
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
| 0 | SELECT STATEMENT | | 1 | 61 | 7 (15)| 00:00:01 |
|* 1 | HASH JOIN SEMI | | 1 | 61 | 7 (15)| 00:00:01 |
| 2 | TABLE ACCESS FULL| XML_TBL | 2 | 116 | 3 (0)| 00:00:01 |
| 3 | TABLE ACCESS FULL| TABLE1 | 2 | 6 | 3 (0)| 00:00:01 |
Predicate Information (identified by operation id):
1 - access("ID1"=SYS_XQ_ATOMCNVCHK(TO_NUMBER(SYS_XQ_UPKXML2SQL(SYS_XQ
EXVAL(SYS_XQEXTRACT(SYS_MAKEXML(0,"T"."SYS_NC00003$"),'/main/id'),1,50,3
3792,8192),50,1,0)),2,37))
Note
- Unoptimized XML construct detected (enable XMLOptimizationCheck for more information)Check each one on your real scenario to see which show best performance.
(I would tend to say the ones involving streaming evaluation)
Edited by: odie_63 on 5 nov. 2012 12:24
Edited by: odie_63 on 5 nov. 2012 12:38 -
SQL QUERY TUNING(PARTITONS AND JOIN)
Hello
My below query is taking very long time to execute ..here is the query and explain plan
EXPLAIN PLAN FOR select w.ref_no,a.*
from table1 a left outer join table2 w
on(a.emp_id=w.emp_id)
where a.joining_date=20090205 ;
PLAN_TABLE_OUTPUT
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Pstart| Pstop |
| 0 | SELECT STATEMENT | | 2415K| 504M| | 226K (2)| | |
|* 1 | HASH JOIN OUTER | | 2415K| 504M| 488M| 226K (2)| | |
|* 2 | TABLE ACCESS BY INDEX ROWID| table1 | 2415K| 460M| | 77975 (1)| | |
|* 3 | INDEX RANGE SCAN | table1_joining_ DATE | 2441K| | | 6463 (2)| | |
| 4 | PARTITION RANGE ALL | | 39M | 718M| | 64745 (2)| 1 | 4 |
| 5 | TABLE ACCESS FULL | table2 | 39M | 718M| | 64745 (2)| 1 | 4 |
table 2 has 40 million rows
Table2 structure
emp_id
ref_no
Project_id
Partitoned on Project_id, Project_id is nowhere used on the above query
Table1 Structure
emp_id
joining_date
other columns
Is indexed on joining_date, not partitioned
Please adviseThe CBO has estimated that you have 2415K table1 rows where joining_date = 20090205. (By the way: is this really a number?!)
Is this estimate accurate?
Please add some more information:
- Which version do you use
- How long is "very long time"?
- How many does your query return
- information as described in this thread: When your query takes too long ...
Regards,
Rob. -
Very Slow Query with CTE inner join
I have 2 tables (heavily simplified here to show relevant columns):
CREATE TABLE tblCharge
(ChargeID int NOT NULL,
ParentChargeID int NULL,
ChargeName varchar(200) NULL)
CREATE TABLE tblChargeShare
(ChargeShareID int NOT NULL,
ChargeID int NOT NULL,
TotalAmount money NOT NULL,
TaxAmount money NULL,
DiscountAmount money NULL,
CustomerID int NOT NULL,
ChargeShareStatusID int NOT NULL)
I have a very basic View to Join them:
CREATE VIEW vwBASEChargeShareRelation as
Select c.ChargeID, ParentChargeID, s.CustomerID, s.TotalAmount, isnull(s.TaxAmount, 0) as TaxAmount, isnull(s.DiscountAmount, 0) as DiscountAmount
from tblCharge c inner join tblChargeShare s
on c.ChargeID = s.ChargeID Where s.ChargeShareStatusID < 3
GO
I then have a view containing a CTE to get the children of the Parent Charge:
ALTER VIEW [vwChargeShareSubCharges] AS
WITH RCTE AS
SELECT ParentChargeId, ChargeID, 1 AS Lvl, ISNULL(TotalAmount, 0) as TotalAmount, ISNULL(TaxAmount, 0) as TaxAmount,
ISNULL(DiscountAmount, 0) as DiscountAmount, CustomerID, ChargeID as MasterChargeID
FROM vwBASEChargeShareRelation Where ParentChargeID is NULL
UNION ALL
SELECT rh.ParentChargeID, rh.ChargeID, Lvl+1 AS Lvl, ISNULL(rh.TotalAmount, 0), ISNULL(rh.TaxAmount, 0), ISNULL(rh.DiscountAmount, 0) , rh.CustomerID
, rc.MasterChargeID
FROM vwBASEChargeShareRelation rh
INNER JOIN RCTE rc ON rh.PArentChargeID = rc.ChargeID and rh.CustomerID = rc.CustomerID
Select MasterChargeID as ChargeID, CustomerID, Sum(TotalAmount) as TotalCharged, Sum(TaxAmount) as TotalTax, Sum(DiscountAmount) as TotalDiscount
from RCTE
Group by MasterChargeID, CustomerID
GO
So far so good, I can query this view and get the total cost for a line item including all children.
The problem occurs when I join this table. The query:
Select t.* from vwChargeShareSubCharges t
inner join
tblChargeShare s
on t.CustomerID = s.CustomerID
and t.MasterChargeID = s.ChargeID
Where s.ChargeID = 1291094
Takes around 30 ms to return a result (tblCharge and Charge Share have around 3.5 million records).
But the query:
Select t.* from vwChargeShareSubCharges t
inner join
tblChargeShare s
on t.CustomerID = s.CustomerID
and t.MasterChargeID = s.ChargeID
Where InvoiceID = 1045854
Takes around 2 minutes to return a result - even though the only charge with that InvoiceID is the same charge as the one used in the previous query.
The same thing occurs if I do the join in the same query that the CTE is defined in.
I ran the execution plan for each query. The first (fast) query looks like this:
The second(slow) query looks like this:
I am at a loss, and my skills at decoding execution plans to resolve this are lacking.
I have separate indexes on tblCharge.ChargeID, tblCharge.ParentChargeID, tblChargeShare.ChargeID, tblChargeShare.InvoiceID, tblChargeShare.ChargeShareStatusID
Any ideas? Tested on SQL 2008R2 and SQL 2012>> The database is linked [sic] to an established app and the column and table names can't be changed. <<
Link? That is a term from pointer chains and network databases, not SQL. I will guess that means the app came back in the old pre-RDBMS days and you are screwed.
>> I am not too worried about the money field [sic], this is used for money and money based calculations so the precision and rounding are acceptable at this level. <<
Field is a COBOL concept; columns are totally different. MONEY is how Sybase mimics the PICTURE clause that puts currency signs, commas, period, etc in a COBOL money field.
Using more than one operation (multiplication or division) on money columns will produce severe rounding errors. A simple way to visualize money arithmetic is to place a ROUND() function calls after
every operation. For example,
Amount = (Portion / total_amt) * gross_amt
can be rewritten using money arithmetic as:
Amount = ROUND(ROUND(Portion/total_amt, 4) *
gross_amt, 4)
Rounding to four decimal places might not seem an
issue, until the numbers you are using are greater
than 10,000.
BEGIN
DECLARE @gross_amt MONEY,
@total_amt MONEY,
@my_part MONEY,
@money_result MONEY,
@float_result FLOAT,
@all_floats FLOAT;
SET @gross_amt = 55294.72;
SET @total_amt = 7328.75;
SET @my_part = 1793.33;
SET @money_result = (@my_part / @total_amt) *
@gross_amt;
SET @float_result = (@my_part / @total_amt) *
@gross_amt;
SET @Retult3 = (CAST(@my_part AS FLOAT)
/ CAST( @total_amt AS FLOAT))
* CAST(FLOAT, @gross_amt AS FLOAT);
SELECT @money_result, @float_result, @all_floats;
END;
@money_result = 13525.09 -- incorrect
@float_result = 13525.0885 -- incorrect
@all_floats = 13530.5038673171 -- correct, with a -
5.42 error
>> The keys are ChargeID(int, identity) and ChargeShareID(int, identity). <<
Sorry, but IDENTITY is not relational and cannot be a key by definition. But it sure works just like a record number in your old COBOL file system.
>> .. these need to be int so that they are assigned by the database and unique. <<
No, the data type of a key is not determined by physical storage, but by logical design. IDENTITY is the number of a parking space in a garage; a VIN is how you identify the automobile.
>> What would you recommend I use as keys? <<
I do not know. I have no specs and without that, I cannot pull a Kabbalah number from the hardware. Your magic numbers can identify Squids, Automobile or Lady Gaga! I would ask the accounting department how they identify a charge.
>> Charge_Share_Status_ID links [sic] to another table which contains the name, formatting [sic] and other information [sic] or a charge share's status, so it is both an Id and a status. <<
More pointer chains! Formatting? Unh? In RDBMS, we use a tiered architecture. That means display formatting is in a presentation layer. A properly created table has cohesion – it does one and only one data element. A status is a state of being that applies
to an entity over a period time (think employment, marriage, etc. status if that is too abstract).
An identifier is based on the Law of Identity from formal logic “To be is to be something in particular” or “A is A” informally. There is no entity here! The Charge_Share_Status table should have the encoded values for a status and perhaps a description if
they are unclear. If the list of values is clear, short and static, then use a CHECK() constraint.
On a scale from 1 to 10, what color is your favorite letter of the alphabet? Yes, this is literally that silly and wrong.
>> I understand what a CTE is; is there a better way to sum all children for a parent hierarchy? <<
There are many ways to represent a tree or hierarchy in SQL. This is called an adjacency list model and it looks like this:
CREATE TABLE OrgChart
(emp_name CHAR(10) NOT NULL PRIMARY KEY,
boss_emp_name CHAR(10) REFERENCES OrgChart(emp_name),
salary_amt DECIMAL(6,2) DEFAULT 100.00 NOT NULL,
<< horrible cycle constraints >>);
OrgChart
emp_name boss_emp_name salary_amt
==============================
'Albert' NULL 1000.00
'Bert' 'Albert' 900.00
'Chuck' 'Albert' 900.00
'Donna' 'Chuck' 800.00
'Eddie' 'Chuck' 700.00
'Fred' 'Chuck' 600.00
This approach will wind up with really ugly code -- CTEs hiding recursive procedures, horrible cycle prevention code, etc. The root of your problem is not knowing that rows are not records, that SQL uses sets and trying to fake pointer chains with some
vague, magical non-relational "id".
This matches the way we did it in old file systems with pointer chains. Non-RDBMS programmers are comfortable with it because it looks familiar -- it looks like records and not rows.
Another way of representing trees is to show them as nested sets.
Since SQL is a set oriented language, this is a better model than the usual adjacency list approach you see in most text books. Let us define a simple OrgChart table like this.
CREATE TABLE OrgChart
(emp_name CHAR(10) NOT NULL PRIMARY KEY,
lft INTEGER NOT NULL UNIQUE CHECK (lft > 0),
rgt INTEGER NOT NULL UNIQUE CHECK (rgt > 1),
CONSTRAINT order_okay CHECK (lft < rgt));
OrgChart
emp_name lft rgt
======================
'Albert' 1 12
'Bert' 2 3
'Chuck' 4 11
'Donna' 5 6
'Eddie' 7 8
'Fred' 9 10
The (lft, rgt) pairs are like tags in a mark-up language, or parens in algebra, BEGIN-END blocks in Algol-family programming languages, etc. -- they bracket a sub-set. This is a set-oriented approach to trees in a set-oriented language.
The organizational chart would look like this as a directed graph:
Albert (1, 12)
Bert (2, 3) Chuck (4, 11)
/ | \
/ | \
/ | \
/ | \
Donna (5, 6) Eddie (7, 8) Fred (9, 10)
The adjacency list table is denormalized in several ways. We are modeling both the Personnel and the Organizational chart in one table. But for the sake of saving space, pretend that the names are job titles and that we have another table which describes the
Personnel that hold those positions.
Another problem with the adjacency list model is that the boss_emp_name and employee columns are the same kind of thing (i.e. identifiers of personnel), and therefore should be shown in only one column in a normalized table. To prove that this is not
normalized, assume that "Chuck" changes his name to "Charles"; you have to change his name in both columns and several places. The defining characteristic of a normalized table is that you have one fact, one place, one time.
The final problem is that the adjacency list model does not model subordination. Authority flows downhill in a hierarchy, but If I fire Chuck, I disconnect all of his subordinates from Albert. There are situations (i.e. water pipes) where this is true, but
that is not the expected situation in this case.
To show a tree as nested sets, replace the nodes with ovals, and then nest subordinate ovals inside each other. The root will be the largest oval and will contain every other node. The leaf nodes will be the innermost ovals with nothing else inside them
and the nesting will show the hierarchical relationship. The (lft, rgt) columns (I cannot use the reserved words LEFT and RIGHT in SQL) are what show the nesting. This is like XML, HTML or parentheses.
At this point, the boss_emp_name column is both redundant and denormalized, so it can be dropped. Also, note that the tree structure can be kept in one table and all the information about a node can be put in a second table and they can be joined on employee
number for queries.
To convert the graph into a nested sets model think of a little worm crawling along the tree. The worm starts at the top, the root, makes a complete trip around the tree. When he comes to a node, he puts a number in the cell on the side that he is visiting
and increments his counter. Each node will get two numbers, one of the right side and one for the left. Computer Science majors will recognize this as a modified preorder tree traversal algorithm. Finally, drop the unneeded OrgChart.boss_emp_name column
which used to represent the edges of a graph.
This has some predictable results that we can use for building queries. The root is always (left = 1, right = 2 * (SELECT COUNT(*) FROM TreeTable)); leaf nodes always have (left + 1 = right); subtrees are defined by the BETWEEN predicate; etc. Here are
two common queries which can be used to build others:
1. An employee and all their Supervisors, no matter how deep the tree.
SELECT O2.*
FROM OrgChart AS O1, OrgChart AS O2
WHERE O1.lft BETWEEN O2.lft AND O2.rgt
AND O1.emp_name = :in_emp_name;
2. The employee and all their subordinates. There is a nice symmetry here.
SELECT O1.*
FROM OrgChart AS O1, OrgChart AS O2
WHERE O1.lft BETWEEN O2.lft AND O2.rgt
AND O2.emp_name = :in_emp_name;
3. Add a GROUP BY and aggregate functions to these basic queries and you have hierarchical reports. For example, the total salaries which each employee controls:
SELECT O2.emp_name, SUM(S1.salary_amt)
FROM OrgChart AS O1, OrgChart AS O2,
Salaries AS S1
WHERE O1.lft BETWEEN O2.lft AND O2.rgt
AND S1.emp_name = O2.emp_name
GROUP BY O2.emp_name;
4. To find the level and the size of the subtree rooted at each emp_name, so you can print the tree as an indented listing.
SELECT O1.emp_name,
SUM(CASE WHEN O2.lft BETWEEN O1.lft AND O1.rgt
THEN O2.sale_amt ELSE 0.00 END) AS sale_amt_tot,
SUM(CASE WHEN O2.lft BETWEEN O1.lft AND O1.rgt
THEN 1 ELSE 0 END) AS subtree_size,
SUM(CASE WHEN O1.lft BETWEEN O2.lft AND O2.rgt
THEN 1 ELSE 0 END) AS lvl
FROM OrgChart AS O1, OrgChart AS O2
GROUP BY O1.emp_name;
5. The nested set model has an implied ordering of siblings which the adjacency list model does not. To insert a new node, G1, under part G. We can insert one node at a time like this:
BEGIN ATOMIC
DECLARE rightmost_spread INTEGER;
SET rightmost_spread
= (SELECT rgt
FROM Frammis
WHERE part = 'G');
UPDATE Frammis
SET lft = CASE WHEN lft > rightmost_spread
THEN lft + 2
ELSE lft END,
rgt = CASE WHEN rgt >= rightmost_spread
THEN rgt + 2
ELSE rgt END
WHERE rgt >= rightmost_spread;
INSERT INTO Frammis (part, lft, rgt)
VALUES ('G1', rightmost_spread, (rightmost_spread + 1));
COMMIT WORK;
END;
The idea is to spread the (lft, rgt) numbers after the youngest child of the parent, G in this case, over by two to make room for the new addition, G1. This procedure will add the new node to the rightmost child position, which helps to preserve the idea
of an age order among the siblings.
6. To convert a nested sets model into an adjacency list model:
SELECT B.emp_name AS boss_emp_name, E.emp_name
FROM OrgChart AS E
LEFT OUTER JOIN
OrgChart AS B
ON B.lft
= (SELECT MAX(lft)
FROM OrgChart AS S
WHERE E.lft > S.lft
AND E.lft < S.rgt);
7. To find the immediate parent of a node:
SELECT MAX(P2.lft), MIN(P2.rgt)
FROM Personnel AS P1, Personnel AS P2
WHERE P1.lft BETWEEN P2.lft AND P2.rgt
AND P1.emp_name = @my_emp_name;
I have a book on TREES & HIERARCHIES IN SQL which you can get at Amazon.com right now. It has a lot of other programming idioms for nested sets, like levels, structural comparisons, re-arrangement procedures, etc.
--CELKO-- Books in Celko Series for Morgan-Kaufmann Publishing: Analytics and OLAP in SQL / Data and Databases: Concepts in Practice Data / Measurements and Standards in SQL SQL for Smarties / SQL Programming Style / SQL Puzzles and Answers / Thinking
in Sets / Trees and Hierarchies in SQL -
Sql query slowness due to rank and columns with null values:
Sql query slowness due to rank and columns with null values:
I have the following table in database with around 10 millions records:
Declaration:
create table PropertyOwners (
[Key] int not null primary key,
PropertyKey int not null,
BoughtDate DateTime,
OwnerKey int null,
GroupKey int null
go
[Key] is primary key and combination of PropertyKey, BoughtDate, OwnerKey and GroupKey is unique.
With the following index:
CREATE NONCLUSTERED INDEX [IX_PropertyOwners] ON [dbo].[PropertyOwners]
[PropertyKey] ASC,
[BoughtDate] DESC,
[OwnerKey] DESC,
[GroupKey] DESC
go
Description of the case:
For single BoughtDate one property can belong to multiple owners or single group, for single record there can either be OwnerKey or GroupKey but not both so one of them will be null for each record. I am trying to retrieve the data from the table using
following query for the OwnerKey. If there are same property rows for owners and group at the same time than the rows having OwnerKey with be preferred, that is why I am using "OwnerKey desc" in Rank function.
declare @ownerKey int = 40000
select PropertyKey, BoughtDate, OwnerKey, GroupKey
from (
select PropertyKey, BoughtDate, OwnerKey, GroupKey,
RANK() over (partition by PropertyKey order by BoughtDate desc, OwnerKey desc, GroupKey desc) as [Rank]
from PropertyOwners
) as result
where result.[Rank]=1 and result.[OwnerKey]=@ownerKey
It is taking 2-3 seconds to get the records which is too slow, similar time it is taking as I try to get the records using the GroupKey. But when I tried to get the records for the PropertyKey with the same query, it is executing in 10 milliseconds.
May be the slowness is due to as OwnerKey/GroupKey in the table can be null and sql server in unable to index it. I have also tried to use the Indexed view to pre ranked them but I can't use it in my query as Rank function is not supported in indexed
view.
Please note this table is updated once a day and using Sql Server 2008 R2. Any help will be greatly appreciated.create table #result (PropertyKey int not null, BoughtDate datetime, OwnerKey int null, GroupKey int null, [Rank] int not null)Create index idx ON #result(OwnerKey ,rnk)
insert into #result(PropertyKey, BoughtDate, OwnerKey, GroupKey, [Rank])
select PropertyKey, BoughtDate, OwnerKey, GroupKey,
RANK() over (partition by PropertyKey order by BoughtDate desc, OwnerKey desc, GroupKey desc) as [Rank]
from PropertyOwners
go
declare @ownerKey int = 1
select PropertyKey, BoughtDate, OwnerKey, GroupKey
from #result as result
where result.[Rank]=1
and result.[OwnerKey]=@ownerKey
go
Best Regards,Uri Dimant SQL Server MVP,
http://sqlblog.com/blogs/uri_dimant/
MS SQL optimization: MS SQL Development and Optimization
MS SQL Consulting:
Large scale of database and data cleansing
Remote DBA Services:
Improves MS SQL Database Performance
SQL Server Integration Services:
Business Intelligence -
Hi all,
this is an example of how to set the READ UNCOMMITED isolation level for a simple query:
select col_a from mytable at isolation 0
What about a query with an equi-join?
Is it:
select col_a
from mytable t1, mytable t2
at isolation 0
I am sorry for the simple question, I did not find a auitable example in the documentation.
Best Regards
ArthurYeah, the docs aren't very good at providing a robust set of examples so you've gotta:
1 - review the command syntax and then
2 - try a few examples until you find the format that works
1 - From the ASE 15.5 Commands reference manual we find the following syntax:
=========================
select ::=
select [all | distinct]
[top unsigned_integer]
select_list
[into_clause]
[from_clause]
[where_clause]
[group_by_clause]
[having_clause]
[order_by_clause]
[compute_clause]
[read_only_clause]
[isolation_clause]
[browse_clause]
[plan_clause]
=========================
This shows us that the isolation clause is attached at the 'end' of the query as a query-level specifier (along with the 'group by', 'order by' and 'having' clauses).
From this syntax I'd say your proposed query looks correct so ...
2 - Try running your example to see if you get a syntax error ("When in doubt, try it out!"), eg:
=========================
select s.name,c.name
from sysobjects s, syscolumns c
where s.id = c.id
at isolation 0
go
name
sysobjects
sysobjects
sysobjects
... snip ...
========================= -
Hierarchical connect by and start with and joins?
I've got an employees table and an identifiers table. The identifiers table is hierarchical, with parents and children. Each employee has one or more identifiers, but only one identifier is considered the "primary" or root identifier for each employee. Unfortunately, the employee table might be pointing at one of the child identifier rows and not the root. I need a fast query to join the employees with their most current (root) identifier.
Here's code to define the problem.
create table employees (employeeid varchar2(8), fakeNationalID varchar2(9), empname varchar2(30));
insert into employees (employeeid, fakeNationalID, empname) values (1,'001000001','John Smith');
insert into employees (employeeid, fakeNationalID, empname) values (2,'002000002','James Jones');
create table realids (realidkey NUMBER, fakeNationalID VARCHAR2(9) not null,
realNationalID VARCHAR2(9) UNIQUE, parent_realidkey number);
insert into realids (realidkey, fakeNationalID, realNationalID, parent_realidkey) values
(1,'001000001','111111111',3);
insert into realids (realidkey, fakeNationalID, realNationalID, parent_realidkey) values
(2,'002000002','222222222',null);
insert into realids (realidkey, fakeNationalID, realNationalID, parent_realidkey) values
(3,'003000003','333333333',null);
commit;
create or replace function get_parentid (fakeID in VARCHAR2) return varchar2 is
tempid VARCHAR2(9);
begin
select realNationalID into tempid
from (
select realNationalID, fakeNationalID
from realids
start with fakeNationalID = fakeID
connect by nocycle prior parent_realidkey = realidkey
order by level desc)
where rownum = 1;
return tempid;
exception
when NO_DATA_FOUND then
return NULL;
when others then raise;
end;
select get_parentid('001000001') from dual; -- returns 333333333 because its linked to a parent
select get_parentid('002000002') from dual; -- returns 222222222 because there is only one child
select get_parentid('003000003') from dual; -- returns 333333333 because it is the parentWhat I want is to get the highest parent node in realids for each row in employees...
This works, but is NOT very efficient:
select employeeid, get_parentid(fakeNationalID) realid, empname from employees;
employeeid realid empname
1 333333333 John Smith
2 222222222 James JonesYou can imagine what this would be like with 100K rows or greater. It takes about 3 minutes to run.
This seemed like a good way to do this, but with a sub query.
select e.employeeid, e.fakenationalid, e.empname, sub.realnationalid
from employees,
(select realidkey, fakenationalid, realnationalid, parent_realidkey
from realids r
start with r.fakenationalid = e.fakenationalid
connect by prior r.parent_realidkey = r.realidkey) subUnfortunately, it produces an invalid identifier on e.fakenationalid (in the start with clause).
Anyone have any ideas on how to get the top most parent node from the realids for each row in the employees table? In real life there are 6 or more employees tables spread across several remote instances some of which point at children in the realids table and some of which point at the parents. We always want the top most parent realid. Any help would be much appreciated.Hi,
Thanks for posting the sample data in such a convenient form!
It always helps to post your Oracle version, too, especially when dealing with CONNECT BY queries.
The following does what you requested in Oracle 10:
WITH got_roots AS
SELECT CONNECT_BY_ROOT fakenationalid AS leaf_id
, realnationalid
FROM realids
WHERE CONNECT_BY_ISLEAF = 1
START WITH fakenationalid IN ( SELECT fakenationalid
FROM employees
CONNECT BY realidKEY = PRIOR parent_realidkey
SELECT e.employeeid
, r.realnationalid
, e.empname
FROM employees e
JOIN got_roots r ON r.leaf_id = e.fakenationalid
;In any query, calling a user-defined function for each row is going to be slow. Fortunately, Oracle now has built-in functions and operators that can take the place of get_parentid. The CONNECT_BY_ROOT operator, which was introduced in Oracle 10, is the key to this problem. In Oracle 9, you can get the same results using SYS_CONNECT_BY_PATH.
It's usually faster to do the CONNECT BY query separately, and then join whatever other tables you need to the results.
You had a good idea in your last query. The problem was that sub and employees were equal tables in the FROM clause, and you can't correlate equals. You can only correlate a sub-query to its super-query. You could make that general idea work by changing sub into a scalar sub-query,which could be correlated to employees, but I think it would be a lot less efficient than what I posted above. -
Doughts on Infoset Joins with Cube and DSO in BI 7
Dear All,
I have a droughts on Infoset Joints.
I am working on a BI Query where I need to take 0Employee from Cube and Employee position time he holds in an Organization is coming form DSO, Apart for this I also have other requirement such as Address fields , Visa Status , are all coming from DSO.
I have created a Infoset for this where
1st is Cube and its 0Employee is linked to all DSO Employee and also Employee position I have linked with DSO and all are inner joints.
1>My doughty is that will this work with inner joins or I have to use other joints.
2>Is sequence of Data Target is correct
which is
1> Cube and in 2> all DSO in Parallel.
Please guide me on joints and sequence.
Thanks V V much in Advance,
Regards,Hi,
Check these links.
http://help.sap.com/saphelp_nw04s/helpdata/en/ed/084e3ce0f9fe3fe10000000a114084/frameset.htm
https://www.sdn.sap.com/irj/sdn/go/portal/prtroot/docs/library/uuid/2f5aa43f-0c01-0010-a990-9641d3d4eef7
http://help.sap.com/saphelp_nw04/helpdata/en/9c/6b7538c9a8ee45e10000009b38f8cf/frameset.htm
if this helpa assign points.
Thanks.....
Vasu..... -
Query with FULL OUTER JOIN , help pleaseeeeeeeeeeee...
Hi everyone,
I'm trying to write a query for a report in Oracle SQL, but i just can't figure out how to do it.
I'm using Oracle 10g release 1.0 database and i execute my queris in SQL* PLUS ( eventually i'm gonna use them in Oracle Report Builder ) .
here's what i have:
i have four tables that are used for our inventory application. lets call them INCOMMING , INCOMMING_ITEMS , OUTGOING , OUTGOING_ITEMS.
as you may have guessed , INCOMMING_ITEMS is the detail table for INCOMMING ( joined by IID column) and also OUTGOING_ITEMS is the detail table for OUTGOING ( joined by OID column ).
here is the structure of them :
INCOMMING
IID varchar2
CDATE date
INCOMMING_ITEM
IID varchar2
PART_NO number
QTY number
OUTGOING
OID varchar2
CDATE date
OUTGOING_ITEM
OID varchar2
PART_NO number
QTY number
now , the query i want, should return part_no , cdate , sum of OUTGOING qty , sum of INCOMMING qty .
the result of the query should be sth like this :
part_no cdate O_qty I_qty
100 01/05/06 10 0
100 01/05/07 20 60
200 01/06/02 0 50
300 01/06/02 30 40
this means that for some dates and for some parts, i may not have INCOMMING or OUTGOING data, but if at least one of the 2 last columns has a non-zero data, i should show the row ( like the first and third rows of my example result), and if both have data for the same PART_NO and the same CDATE, both should be showed in the same row. ( like the second row in my example result)
i tried so much and came up with several huge and also time consuming queries, but then i read abt FULL OUTER JOIN somewhere and tried using that. here is what i came up with :
SELECT
PART_NO , CDATE , sum(II.QTY) I_QTY , SUM (OI.QTY) O_QTY
FROM
(OUTGOING O INNER JOIN OUTGOING_ITEM OI USING ( OID ) )
FULL OUTER JOIN
(INCOMMING I INNER JOIN INCOMMING_ITEM II USING ( IID ) )
ON ( I.CDATE = O.CDATE AND II.PART_NO = OI.PART_NO)
WHERE
I.CDATE = :PARAMETER1
AND O.CDATE = :PARAMETER1
GROUP BY
PART_NO , CDATE
this query is short and fast , but the results r not what i expected. i mean, although i have used FULL OUTER JOIN in the query , but the results i get r sth like this :
part_no cdate O_qty I_qty
100 01/05/07 20 60
300 01/06/02 30 40
which means only the rows that has both values are returned.
any change i make to this query would make the SQL* PLUS hang , like when i use the cartesian product of two large tables, so i guess my changes wheren't in the right direction.
i think its possible to write this query using FULL OUTER JOIN syntax, but i just can't find it.
Can anybody pleaseeeeeeeeeeeee help me?
thanx in advance,
Maryam.Note: I wrote this on the fly -- hope there is no syntax errors, otherwise forgive me -- but you get the idea..
select
fromUnionAll.cdate, fromUnionAll.part_no,
sum(fromUnionAll.O_qty) O_qty,
sum(fromUnionAll.I_qty) I_qty
from
select
iinner.cdate, iinner.part_no, 0 O_qty, iinner.I_qty
from
select
i.cdate, ii.part_no,
/* added the case only for the extreme case when there is
no record anywhere for the given CDATE in INCOMMING_item */
sum( ( case when ii.qty is not null then ii.qty else 0 end) ) I_qty
from
incomming i,
incomming_item ii
where
i.iid = ii.iid (+)
group by i.cdate, ii.part_no
) iinner
union all
select
oinner.cdate, oinner.part_no, oinner.O_qty, 0 I_qty
from
select
o.cdate, oi.part_no,
/* added the case only for the extreme case when there is
no record anywhere for the given CDATE in OUTGOING_item */
sum( ( case when oi.qty is not null then oi.qty else 0 end) ) O_qty
from
outgoing o,
outgoing_item oi
where
o.oid = oi.oid (+)
group by o.cdate, oi.part_no
) oinner
) fromUnionAll
group by fromUnionAll.cdate, fromUnionAll.part_no;
--Samson -
Query with multiple outer joins
I had a doubt with whether the following kind of query is valid with multiple outer-joins. The format of the query is something like this:-
select A.col1, B.col2
from table1 A, table2 B, table3 C where
A.col3=B.col4(+) and B.col5=C.col6(+)
This would mean the follwoing with regard to outer-joins in the query.
1) fetch records with col3 in table A matching or not matching col4 in table B
2) fetch records with col5 in table B matching or not matching col6 in table C
So, this query is valid?
I hope, my question is clear.
Please, help in solving the doubt.
regardsThis is valid and it works fine
Maybe you are looking for
-
Error during installation of photosmart b209a-m drivers on windows 8.1
I recently acquired a new HP 110-120 desktop for one of my customers. Prior to delivery I wanted to install the printer software and drivers so that everything worked upon delivery. I downloaded the most recent drivers for windows 8.1 64 bit OS (PS_
-
Hello, My CS3 programs have stopped working since I loaded them back on from a time machine backup. Upon opening any of the programs, the following dialog appears: I have tried uninstalling and reinstalling from the discs, but cannot get past this.
-
Network Error - Sync Using LAN with TCPIP 100mbps
Hi im facing an unknown issue while sync. 1. Using a gprs line i can sync the db (Fairly a large db 400mb) But when use the LAN it says Network Disconnected during async -Check for network Active. username/password and server url are correctly entere
-
Airport Extreme and WD My Book World Edition
Hello, I am considering buying an airport extreme, but I am fairly new to the product and had some questions about it. I have a WD My Book World Edition wireless hard drive, is it possible to plug the hard drive into the airport extreme via ethernet
-
I bought a PowerMac G5 and it came with OS X 10.5.8 pre-installed. I used that for a while till I got my hands on a retail Leopard disc. I wiped the HDD and did a clean install, and now iTunes won't start. It says there's not enough memory available,