1

I'm using Postgres 9.4 and I have 3 tables: truck , container and container_metadata.
A truck can have many container and a container many container_metadata.

I'll add more description later with the tables below (I did my best creating them), so here goes:

enter image description here

A truck can contain many containers referred by key truck_id

enter image description here enter image description here

A container then is described by the container_metadata table, a container can also have many records of different types, but in this case, I will be focusing only in 'subcontainer' type.

The problem I'm trying to solve is to create a view for ease of querying, and model creation (to be used for databrowser). I'll add more details below.

enter image description here

In the last column, all good is true if a truck has:

  • At least one container.
  • A container should have at least one record of type subcontainer in the metadata.
  • Volume values of all subcontainers should be positive non zero.

More details:

  • If a truck has no container, then I should have a column to describe whether I need to create container for it.
  • If a truck has containers but no subcontainer, then I should also be able to tell I need to create subcontainer records for it.
  • If a truck has 10 containers, but only 6 of the containers have atleast one subcontainer, then it's still not good, and I should be able to tell that I still need to create a subcontainer.
  • If a truck's containers have sub containers, but one of the subcontainer has a volume of 0, again, it's not good, and I should also notify that we need to fill it .

I have only been able to count the containers, subcontainers I've been trying out aggregate functions, but I'm new to this problem, I hope the tables help. Please do ask if it needs more clarification.

Query

My attempt so far:

select 
t.id,t.name, count(c.id) as container_count, count(cm.id) as subcontainer_count
from
    public.truck t
left join
    public.container c
on
    c.truck_id = t.id
left join
    public.container_metadata cm
on
    cm.container_id = c.id and type = 'subcontainer'
group by t.id
;

Assuming schema is public. Above is the query I've tried but it outputs wrong number of subcontainers, and that's how far I've got.

Community
  • 1
  • 1
muffin
  • 2,034
  • 10
  • 43
  • 79
  • 2
    Can you supply the table definitions as `create table` statements and the sample data as `insert` statement? Or even create a http://sqlfiddle.com for this? Screenshots are really bad for sample data because you can't copy and paste from them. –  Apr 24 '15 at 06:16
  • And we really like to see what you have tried so far. This site is for solving *problems* not for letting other people do your work without pay. As such, there are way too many issues packed into one question. – Erwin Brandstetter Apr 24 '15 at 06:19
  • okay sure...just a minute. this isn't real data.. I just represented the similar table structure with trucks and containers. I just thought it's already too long to include my solutions. okay will update. – muffin Apr 24 '15 at 06:20
  • Sql fiddle here http://sqlfiddle.com/#!15/8c724/1 ...i'm not sure if I got it correctly, but yeah. This isn't a real work btw, sorry if you misunderstood it. But I really want to know the right sql statements to solve this kind of problem. Even if not exactly, 'this' problem. thanks. i'll update the question – muffin Apr 24 '15 at 06:36
  • i've updated the question with my initial query – muffin Apr 24 '15 at 06:43
  • I appreciate you are presenting your question well. Postgres 9.4 I assume (should *always* be in your question)? Demo data in `container_metadata.volume suggest `NOT NULL DEFAULT 0`, but table definition ìn the fiddle says otherwise? – Erwin Brandstetter Apr 24 '15 at 07:53
  • sorry about that.. could you just ignore the strict constraints. i'm sorry. :) it's really just about how to get the right joins. – muffin Apr 24 '15 at 08:04
  • Knowing those constraints is *essential* to get it right. – Erwin Brandstetter Apr 24 '15 at 08:31
  • container_metadata.volume suggest NOT NULL DEFAULT 0 then :) – muffin Apr 24 '15 at 08:33

1 Answers1

1

Assuming container_metadata.volume to be NOT NULL DEFAULT 0, this should do the complete job:

SELECT t.id, t.name
     , COALESCE(c.cont_ct, 0)      AS cont_ct
     , COALESCE(c.sub_ct, 0)       AS sub_ct
     , COALESCE(c.empty_ct, 0)     AS empty_ct
     , c.truck_id IS NULL          AS cont_missing
     , c.sub_missing IS NOT FALSE  AS sub_missing
     ,(c.empty_ct = 0) IS NOT TRUE AS sub_needfill
     , c.empty_ct = 0 AND NOT c.sub_missing AS all_good
FROM   truck t
LEFT   JOIN (
   SELECT truck_id
        , count(*) AS cont_ct
        , sum(cm.ct_sub) AS sub_ct
        , sum(cm.ct_empty) AS empty_ct
        , bool_or(cm.container_id IS NULL) AS sub_missing
   FROM   container c
   LEFT JOIN (
      SELECT container_id
           , count(*) AS ct_sub
           , count(*) FILTER (WHERE volume = 0) AS ct_empty
      FROM   container_metadata
      WHERE  type = 'subcontainer'  -- only those seem relevant
      GROUP  BY 1
      ) cm ON cm.container_id = c.id
   GROUP  BY 1
   ) c ON c.truck_id = t.id;

The major feature is to aggregate first and then join to the next upper level. Not only is it typically faster when processing most or all of the table anyway, it also allows to aggregate each level in one fell swoop:

You need to be very careful where NULL values can creep in. Those can be introduced by column values or by the LEFT JOIN (missing rows). So it is essential to observe which columns can be NULL. The query is only valid in combination with the fitting table definitions.

And of course you need to understand logical and comparison operators, in particular involving NULL values.

Aggregate FILTER requires Postgres 9.4:

SQL Fiddle without data since no data has been provided. So not tested either.

Community
  • 1
  • 1
Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228
  • thanks , these are really helpful, i'm begining to try it out with my real tables and data sets. :) and update what happened. – muffin Apr 24 '15 at 09:19