First I would like to say, that I'm quite amazed. You even guessed table names right! 
:) I did not expect such an in-depth analysis in such a short time. Thanks, Tomasz!

> -----Original Message-----
> From: Tomasz Myrta [mailto:[EMAIL PROTECTED]]
> Sent: Tuesday, January 14, 2003 11:51 AM
> To: Tambet Matiisen
> Cc: [EMAIL PROTECTED]
> Subject: Re: [SQL] full join in view
> 
> 
> Tambet Matiisen wrote:
> 
> > You are right. After disabling seq_scan, it uses indexes 
> just as you 
> > described. Unfortunately my view happens to use subquery:
> 
> Don't disable seq_scan - sometimes it is better than indexscan.

I just did it for testing and for one session. I never disable it on production server.

...
> 
> There is one more problem in your query - coalesce, which possibly 
> disables any indexing in your view. Try to rewrite your view 
> - subquery 
> shouldn't return dor_kst_id and dor_mat_id null.
> 
Coalesce did not pose any problems. Unless I tried to filter using one of the 
coalesce-fields, which does not use indeces of course.

> Is dor_kst_id the same as kdt_kst_id and as mat_id? After 
> some database 
> practicing I found, that using the same name in all tables is 
> much more 
> comfortably
> 
This way I can refer most columns without prefixing them with table alias. But it's 
anyway good habit to use table aliases, so this is not that important. I think in next 
project I try it in your way.

> For each material (materjalid) and koostud (koostud) you want to find 
> some current value (koostude_detaild) and compare it to some sum 
> (documentid...)?
> I'm not sure if I understand well your view, but here is my 
> version of 
> this view - without subquery:
> 

I tried to save few scans by not including "koostud" and "materjalid" in my original 
query. Based on yours, I created a new version:

CREATE OR REPLACE VIEW v_tegelikud_kulud2 AS
SELECT
    kst.kst_id,
    mat.mat_id,
    max(kdt.detaili_nr) AS detaili_nr,
    max(kdt.arv) AS arv,
    max(kdt.kulu) AS kulu,
    max(kdt.yhik) AS yhik,
    max(kdt.koefitsent) AS koefitsent,
    max(kdt.eeldatav_hind) AS eeldatav_hind,
    sum(dor.kogus * dor.koefitsent::numeric) AS kogus,
    sum(dor.kokku) AS kokku
FROM koostud kst
CROSS JOIN materjalid mat
LEFT JOIN koostude_detailid kdt
    ON kst.kst_id = kdt.kdt_kst_id AND mat.mat_id = kdt.kdt_mat_id
LEFT JOIN dokumentide_read dor
    ON kst.kst_id = dor.dor_kst_id AND mat.mat_id = dor.dor_mat_id AND EXISTS
        (
        SELECT 1
        FROM dokumendid dok 
        WHERE dor.dor_dok_id = dok.dok_id AND dok.tyyp = 30 AND dok.kinnitaja IS NOT 
NULL
        )
WHERE kdt.kdt_id IS NOT NULL AND dor.dor_id IS NOT NULL
GROUP BY kst.kst_id, mat.mat_id;

I do a cross join between "koostud" and "materjalid", because "FROM koostud kst, 
materjalid mat" gave me syntax errors. Also I had to move "dokumendid" table to EXISTS 
subquery, to get equivalent results with the original query. Just LEFT JOIN-ing it is 
not enough and subquery troubled optimizer. There is also a WHERE condition to show 
only those materials, that appear in one of the tables "koostude_detailid" or 
"dokumentide_read". Here is the execution plan:

explain select count(1) from v_tegelikud_kulud2 where kst_id = 1125;
                                                          QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------
 Aggregate  (cost=92.60..92.60 rows=1 width=107)
   ->  Subquery Scan v_tegelikud_kulud2  (cost=69.58..92.58 rows=8 width=107)
         ->  Aggregate  (cost=69.58..92.58 rows=8 width=107)
               ->  Group  (cost=69.58..91.00 rows=79 width=107)
                     ->  Merge Join  (cost=69.58..90.61 rows=79 width=107)
                           Merge Cond: (("outer".kst_id = "inner".dor_kst_id) AND 
("outer".mat_id = "inner".dor_mat_id))
                           Join Filter: (subplan)
                           Filter: ("inner".dor_id IS NOT NULL)
                           ->  Merge Join  (cost=17.54..18.52 rows=79 width=66)
                                 Merge Cond: (("outer".kst_id = "inner".kdt_kst_id) 
AND ("outer".mat_id = "inner".kdt_mat_id))
                                 Filter: ("inner".kdt_id IS NOT NULL)
                                 ->  Sort  (cost=10.56..10.76 rows=79 width=8)
                                       Sort Key: kst.kst_id, mat.mat_id
                                       ->  Nested Loop  (cost=0.00..8.07 rows=79 
width=8)
                                             ->  Index Scan using kst_pk on koostud 
kst  (cost=0.00..4.49 rows=1 width=4)
                                                   Index Cond: (kst_id = 1125)
                                             ->  Seq Scan on materjalid mat  
(cost=0.00..2.79 rows=79 width=4)
                                 ->  Sort  (cost=6.98..7.27 rows=113 width=58)
                                       Sort Key: kdt.kdt_kst_id, kdt.kdt_mat_id
                                       ->  Seq Scan on koostude_detailid kdt  
(cost=0.00..3.13 rows=113 width=58)
                           ->  Sort  (cost=52.03..53.93 rows=761 width=41)
                                 Sort Key: dor.dor_kst_id, dor.dor_mat_id
                                 ->  Seq Scan on dokumentide_read dor  
(cost=0.00..15.61 rows=761 width=41)
                           SubPlan
                             ->  Index Scan using dok_pk on dokumendid dok  
(cost=0.00..3.47 rows=1 width=0)
                                   Index Cond: ($0 = dok_id)
                                   Filter: ((tyyp = 30) AND (kinnitaja IS NOT NULL))
(27 rows)

But there are still few things that worry me:
1. Cross join between koostud and materjalid. Table "materjalid" may have up to 10000 
rows and only 20-30 of them are actually needed.
2. Indeces on "koostude_detailid" and "dokumentide_read" are not used. Probably my 
tables do not contain enough rows. Maybe I should generate more test data first.
3. The cost of this query is twice as big, as my original query. It seems to me, that 
SubPlan is causing this. I tried to move it to subquery, but then the optimizer chose 
a totally different execution plan and seemingly was not able to use indeces of 
"dokumentide_read" table. The version with subquery:

CREATE OR REPLACE VIEW v_tegelikud_kulud3 AS
SELECT
    kst.kst_id,
    mat.mat_id,
    max(kdt.detaili_nr) AS detaili_nr,
    max(kdt.arv) AS arv,
    max(kdt.kulu) AS kulu,
    max(kdt.yhik) AS yhik,
    max(kdt.koefitsent) AS koefitsent,
    max(kdt.eeldatav_hind) AS eeldatav_hind,
    sum(dor.kogus * dor.koefitsent::numeric) AS kogus,
    sum(dor.kokku) AS kokku
FROM koostud kst
CROSS JOIN materjalid mat
LEFT JOIN koostude_detailid kdt
    ON kst.kst_id = kdt.kdt_kst_id AND mat.mat_id = kdt.kdt_mat_id
LEFT JOIN 
    (
    SELECT dor.*
    FROM dokumentide_read dor
    JOIN dokumendid dok ON dor.dor_dok_id = dok.dok_id 
    WHERE dok.tyyp = 30 AND dok.kinnitaja IS NOT NULL
    ) dor
    ON kst.kst_id = dor.dor_kst_id AND mat.mat_id = dor.dor_mat_id
WHERE kdt.kdt_id IS NOT NULL AND dor.dor_id IS NOT NULL
GROUP BY kst.kst_id, mat.mat_id;

And execution plan:

hekotek=# explain select count(1) from v_tegelikud_kulud3 where kst_id = 1125;
                                                              QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------------------
 Aggregate  (cost=56.52..56.52 rows=1 width=111)
   ->  Subquery Scan v_tegelikud_kulud3  (cost=53.31..56.50 rows=8 width=111)
         ->  Aggregate  (cost=53.31..56.50 rows=8 width=111)
               ->  Group  (cost=53.31..54.92 rows=79 width=111)
                     ->  Merge Join  (cost=53.31..54.53 rows=79 width=111)
                           Merge Cond: (("outer".kst_id = "inner".dor_kst_id) AND 
("outer".mat_id = "inner".dor_mat_id))
                           Filter: ("inner".dor_id IS NOT NULL)
                           ->  Merge Join  (cost=17.53..18.50 rows=79 width=66)
                                 Merge Cond: (("outer".kst_id = "inner".kdt_kst_id) 
AND ("outer".mat_id = "inner".kdt_mat_id))
                                 Filter: ("inner".kdt_id IS NOT NULL)
                                 ->  Sort  (cost=10.54..10.74 rows=79 width=8)
                                       Sort Key: kst.kst_id, mat.mat_id
                                       ->  Nested Loop  (cost=0.00..8.05 rows=79 
width=8)
                                             ->  Index Scan using kst_pk on koostud 
kst  (cost=0.00..4.47 rows=1 width=4)
                                                   Index Cond: (kst_id = 1125)
                                             ->  Seq Scan on materjalid mat  
(cost=0.00..2.79 rows=79 width=4)
                                 ->  Sort  (cost=6.98..7.27 rows=113 width=58)
                                       Sort Key: kdt.kdt_kst_id, kdt.kdt_mat_id
                                       ->  Seq Scan on koostude_detailid kdt  
(cost=0.00..3.13 rows=113 width=58)
                           ->  Sort  (cost=35.78..35.99 rows=84 width=45)
                                 Sort Key: dor.dor_kst_id, dor.dor_mat_id
                                 ->  Merge Join  (cost=9.04..33.09 rows=84 width=45)
                                       Merge Cond: ("outer".dor_dok_id = 
"inner".dok_id)
                                       ->  Index Scan using dor_dok_fk_i on 
dokumentide_read dor  (cost=0.00..20.91 rows=761 width=41)
                                       ->  Sort  (cost=9.04..9.13 rows=36 width=4)
                                             Sort Key: dok.dok_id
                                             ->  Seq Scan on dokumendid dok  
(cost=0.00..8.10 rows=36 width=4)
                                                   Filter: ((tyyp = 30) AND (kinnitaja 
IS NOT NULL))
(28 rows)

Uh.. Mails are getting very lengthy.

  Tambet

---------------------------(end of broadcast)---------------------------
TIP 2: you can get off all lists at once with the unregister command
    (send "unregister YourEmailAddressHere" to [EMAIL PROTECTED])

Reply via email to