Search code examples
postgresqljpaeclipselink

How retrieve a record but only when every linked sub entities column 'archived' is set to true?


I have entities named Record that are stored in a postgresql table.

Theses records can be archived on backup media.

The assocation is like that : Record <-> AssocationTable <-> Media

A record can be associated with more than 1 media. The association table provide an attribute : archived as boolean

I would like to write a query that return a list of records. But I want to find a record only when each association to a media have the archived attribute set to true.

Sample :

Media (id, name)
1, First media
2, Second Media

Record (id, name)
10, First record
20, Second record
30, Third record

Association (media id, record id, archived)
1, 10, true
2, 10, false
1, 20, false
1, 30, true
2, 30, true

So, here we have 3 records, record 10 is linked to media 1 and 2, but media 2 is not archived, so I don't want it. Record 20 is only linked to media 1 but not archived, again I don't want it. Then record 30 is linked to media 1 and 2, and are both marked as archived. That's the kind of record I want to find.

I tried something like that :

SELECT r FROM Record r, ArchiveMediasRecords ass, AchiveMedia a WHERE ass MEMBER OF r.medias AND ass MEMBER OF a.records AND ass.archived = true

But the ass.archived=true is not a criteria that match 'every' associations.

Thanks.

Note : Java 8u51, Eclipselink 2.6 and PostgreSQL 9.4

Edit, using pure SQL gives me this :

EXPLAIN SELECT * FROM recorder.records r WHERE true = ALL (
    SELECT archived FROM recorder.archive_medias_records WHERE record_id = r.id
)


Seq Scan on records r  (cost=0.00..6026.82 rows=190 width=182)
  Filter: (SubPlan 1)
  SubPlan 1
    ->  Seq Scan on archive_medias_records  (cost=0.00..31.63 rows=9 width=1)
          Filter: (record_id = r.id)

Real data :

Record
2001;.....
2002;.....
2003;.....

Media
1;"PLop";....
5;"plip";....

Assocation
1;2001;TRUE

1;2002;TRUE  
5;2002;FALSE  

1;2003;TRUE  
5;2003;TRUE  

Expected : Record 2001 and 2003


Solution

  • DDL and sample data:

    CREATE TABLE media (
      id SERIAL PRIMARY KEY,
      name TEXT
    );
    INSERT INTO media VALUES
      (1,'PLop'),
      (5,'plip');
    
    CREATE TABLE record (
      id SERIAL PRIMARY KEY,
      name TEXT
    );
    INSERT INTO record VALUES
      (2001,'First record'),
      (2002,'Second record'),
      (2003,'Third record');
    
    CREATE TABLE assosiation(
      mediaid INTEGER REFERENCES media(id),
      recordid INTEGER REFERENCES record(id),
      archived BOOLEAN
    );
    INSERT INTO assosiation VALUES
      (1,2001,true),
      (1,2002,true),
      (5,2002,false),
      (1,2003,true),
      (5,2003,true);
    

    psql below returns expected values:

    SELECT * FROM record r WHERE
    true = ALL (
      SELECT archived FROM assosiation WHERE recordid = r.id
    );
    

    OUTPUT :

      id  |     name     
    ------+--------------
     2001 | First record
     2003 | Third record
    (2 rows)