[Dbix-class] JOINs gallery.

Emanuele Zeppieri ema_zep at libero.it
Sat Aug 6 13:09:32 CEST 2005


Sorry for my late.
Instead of posting JOINs involving dozens of tables, thus difficult to
read and to understand, I opted for the simplest db schema possible,
that is a single table, so that the JOINs should be easier to
understand.

The table (which lives under the "dbix_class" schema, in PostgreSQL
parlance) contains several family trees. Each record represents a
person, along with the pointers to his/her father and mother (and the
birth year):

CREATE TABLE dbix_class.person
(
  person_id int4,
  name varchar,
  father_id int4,
  mother_id int4,
  birth_year char(4),
)

The pointers to the parents point to other records in the same table
(that is, they contain other person_id values).

Attached to this message there is an (PostgreSQL) sql script to create
such table (and the enclosing dbix_class schema) and populate it with
some test values, so that you can run the queries and/or alter/extend
them if you like.
(Please remove any reference to the dbix_class schema both in the script
and in the queries if you don't want it).
The script and every query have been tested on PostgreSQL v.8.0.3.

The queries were deliberately made overly convoluted, of course.


Query 1
List the persons along with their parents.
Cascading JOINs version:
------------------------------------------
SELECT
	child.name, father.name, mother.name
FROM
	dbix_class.person AS child
	JOIN
	dbix_class.person AS father
	ON
		father.person_id = child.father_id
	JOIN
	dbix_class.person AS mother
	ON
		mother.person_id = child.mother_id
------------------------------------------


Query 2
The same as above but nesting one JOIN:
------------------------------------------
SELECT
	child.name, father.name, mother.name
FROM
	dbix_class.person AS mother
	JOIN (
		dbix_class.person AS child
		JOIN
		dbix_class.person AS father
		ON
		father.person_id = child.father_id
	)
	ON
	mother.person_id = child.mother_id
------------------------------------------


Query 3
Lists the persons along with their parents and their paternal
grandparents.
Shows nested JOINs followed by other cascading JOINs.
------------------------------------------
SELECT
	child.name, father.name, mother.name,
	paternal_grandfather.name, paternal_grandmother.name
FROM
	dbix_class.person AS mother
	JOIN (
		dbix_class.person AS child
		JOIN
		dbix_class.person AS father
		ON
		father.person_id = child.father_id
	)
	ON
	mother.person_id = child.mother_id

	JOIN

	dbix_class.person AS paternal_grandfather
	ON
	paternal_grandfather.person_id = father.father_id 
	

	JOIN

	dbix_class.person AS paternal_grandmother
	ON
	paternal_grandmother.person_id = father.mother_id
------------------------------------------


Query 4
The same as above, but nesting instead of cascading one of the JOINs (so
we have two nested levels).
------------------------------------------
SELECT
	child.name, father.name, mother.name,
	paternal_grandfather.name, paternal_grandmother.name
FROM
	dbix_class.person AS paternal_grandfather
	JOIN (
		dbix_class.person AS mother
		JOIN (
			dbix_class.person AS child
			JOIN
			dbix_class.person AS father
			ON
			father.person_id = child.father_id
		)
		ON
		mother.person_id = child.mother_id
	)
	ON
	paternal_grandfather.person_id = father.father_id 
	

	JOIN

	dbix_class.person AS paternal_grandmother
	ON
	paternal_grandmother.person_id = father.mother_id
------------------------------------------


Query 5
Lists all and only the persons who have children.
Shows a subquery inside the ON clause!
------------------------------------------
SELECT
	person_name.name
FROM
	dbix_class.person AS person_id
	JOIN
	dbix_class.person AS person_name
	ON
	person_id.person_id = person_name.person_id
		AND
	EXISTS (
		SELECT person_id
		FROM dbix_class.person
		WHERE
			father_id = person_id.person_id
			OR
			mother_id = person_id.person_id
	)
------------------------------------------


Query 6
Lists all and only the persons who do not have children.
The same as above plus a cascading RIGHT JOIN (to find the complement of
the table).
------------------------------------------
SELECT
	all_persons.name
FROM
	dbix_class.person AS person_id
	JOIN
	dbix_class.person AS person_name
	ON
	person_id.person_id = person_name.person_id
		AND
	EXISTS (
		SELECT person_id
		FROM dbix_class.person
		WHERE
			father_id = person_id.person_id
			OR
			mother_id = person_id.person_id
	)

	RIGHT JOIN

	dbix_class.person AS all_persons
	ON
	all_persons.person_id = person_id.person_id
WHERE
	person_name.name IS NULL
------------------------------------------


Query 7
Lists all the persons in the table by UNIONing those who have children
and those who don't have children (tertium non datur ;-)
Simply (so to say) a UNION of queries 5 and 6.
------------------------------------------
SELECT
	person_name.name
FROM
	dbix_class.person AS person_id
	JOIN
	dbix_class.person AS person_name
	ON
	person_id.person_id = person_name.person_id
		AND
	EXISTS (
		SELECT person_id
		FROM dbix_class.person
		WHERE
			father_id = person_id.person_id
			OR
			mother_id = person_id.person_id
	)

UNION

SELECT
	all_persons.name
FROM
	dbix_class.person AS person_id
	JOIN
	dbix_class.person AS person_name
	ON
	person_id.person_id = person_name.person_id
		AND
	EXISTS (
		SELECT person_id
		FROM dbix_class.person
		WHERE
			father_id = person_id.person_id
			OR
			mother_id = person_id.person_id
	)

	RIGHT JOIN

	dbix_class.person AS all_persons
	ON
	all_persons.person_id = person_id.person_id
WHERE
	person_name.name IS NULL
------------------------------------------


Query 8
Lists all the persons who don't have children and are born after 1982.
Shows a JOIN with a subquery inside the ON clause which in turn contains
another JOIN with another subquery inside its ON clause (shesh!)
------------------------------------------
SELECT young_person.name, young_person.birth_year
FROM

(
	SELECT *
	FROM dbix_class.person
	WHERE birth_year >= '1983'
) AS young_person

JOIN

dbix_class.person AS everyone

ON

young_person.person_id = everyone.person_id

	AND

young_person.person_id IN (
SELECT
	all_persons.person_id
FROM
	dbix_class.person AS person_id
	JOIN
	dbix_class.person AS person_name
	ON
	person_id.person_id = person_name.person_id
		AND
	EXISTS (
		SELECT person_id
		FROM dbix_class.person
		WHERE
			father_id = person_id.person_id
			OR
			mother_id = person_id.person_id
	)

	RIGHT JOIN

	dbix_class.person AS all_persons
	ON
	all_persons.person_id = person_id.person_id
WHERE
	person_name.name IS NULL
)
------------------------------------------

OK, my fantasy is over for now.

Ciao,
Emanuele.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: joins_test_db.sql
Type: application/octet-stream
Size: 2724 bytes
Desc: not available
Url : http://lists.rawmode.org/pipermail/dbix-class/attachments/20050806/72820746/joins_test_db.obj


More information about the Dbix-class mailing list