1

I have a feeling this must have been asked multiple times, but I just cannot find a solution, or even a questions probably because of difficulty in explaining.

We all know that inner join basically gives an "intersection" of two tables. And hence multiple inner joins give the intersection of all tables. What I -however- wish is to get an inner join that gets an intersection of table B with A or table C with A.

Like for a request that gets all organizations I'm either moderator or admininistrator.

SELECT *
FROM organization
INNER JOIN moderator_organization
  ON organization.id = moderator_organization.organization AND moderator_organization.user = 10
INNER JOIN admin_organization
  ON organization.id = admin_organization.organization AND admin_organization.user = 10

Above however would only select the organizations from which the user (with id 10) is both admin and organizer, not one where either is true. To visualize in a diagram I'd like:

enter image description here

Where the red region is what I'd like to request.

0
5

Venn diagrams are not ideal to visualize join operations like a_horse commented:

Venn diagrams do not visualize joins, but set operations like union, intersect or except.

And his link to illustrate:

The twist, I think, is this: the Venn diagram is the right tool to visualize your objective, but the SQL JOIN is the wrong approach. You need a UNION (or equivalent) at the core of your query.

Title and the example query are misleading like that. What's more, you start with:

SELECT * FROM ...

That gets all columns from all joined tables. Also not the expressed objective:

all organizations I'm either moderator or administrator.

UNION

The core query is:

SELECT organization FROM admin_organization     WHERE user = 10
UNION
SELECT organization FROM moderator_organization WHERE user = 10

UNION. Not JOIN. And not UNION ALL - we don't want duplicate organizations in the result.

The column name organization is also misleading (IMHO). Should really be something like organization_id for clarity.

The resulting set of unique IDs may already be all that's needed. To flesh it out with more (or all) attributes of the organization (columns of table organization), now we JOIN to the table:

SELECT *
FROM  (
   SELECT organization AS id FROM admin_organization WHERE user = 10
   UNION
   SELECT organization FROM moderator_organization WHERE user = 10
   ) x
JOIN   organization USING (id);

It's typically (substantially) cheaper to apply UNION on just the ID column, and then join. It may even be a necessity, if some of the columns have types have data types with no equality operator. More common than one might think. See:

Assuming referential integrity, enforced with FK constraints, nothing is lost in the join.

For convenience, it added a column alias in the subquery (organization AS id), so that we can use the simplified join condition with USING (id) which, in turn, allows us to use the simple (and now correct) SELECT * in the outer query to get all columns of the table organization without a duplicate ID column.

Alternative with EXISTS

Shorter equivalent that also avoids undue duplication or elimination of rows:

SELECT *
FROM   organization o
WHERE  EXISTS (SELECT FROM admin_organization     WHERE user = 10 AND organization = o.id)
   OR  EXISTS (SELECT FROM moderator_organization WHERE user = 10 AND organization = o.id);

Naming convention

A naming convention with descriptive names can avoid some of the confusion and noise. Use organization_id (or org_id if you prefer short names and there is no ambiguity) for all three: admin_organization.organization, moderator_organization.organization, and organization.id.

3
  • I wonder about the naming convention: coming from programming language this is considered terrible advice. An object has "legs" not "table_legs" and/or "dog_legs", "cat_legs". Naming like that destroys reusability, as the fullname is object.field anyways. – paul23 Jan 13 at 16:49
  • @paul23: The reality in SQL is that you join multiple tables a lot, and often with more than one query level (so that original table names are not visible in the outer level). If every table has "legs", you end up with multiple "legs" and have to attach column aliases, which is cumbersome and error prone. Other programming languages don't join objects as much. So in a relational design it makes more sense to have (mostly) unique names across a whole schema, not just a single table. It's not required, but useful in practice - except for simple schemas where you do not join a lot. – Erwin Brandstetter Jan 13 at 16:58
  • Of course, if multiple tables have a column "legs" and all are supposed to be the same "cat legs", then they might as well share the same name (whether that's "legs" or "cat_legs"). When joining tables, you would only ever have one of them in the result anyway, and it's simpler to have the same name for the same datum. – Erwin Brandstetter Jan 13 at 17:04
3

The Venn diagram you provide suggests that the expression you aim for is:

(organizations JOIN moderators)
UNION
(organizations JOIN admins)

Translated into - your - SQL

SELECT ...
FROM organization
JOIN moderator_organization
  ON organization.id = moderator_organization.organization 
  AND moderator_organization.user = 10
UNION
SELECT ...
FROM organization 
JOIN admin_organization
  ON organization.id = admin_organization.organization 
  AND admin_organization.user = 10

Note that the two legs in the UNION need to be union-compatible. That's why I left the columns as an ellipse to be filled out (since I don't know what the columns are).

1
  • Costs and complications arising from having to match long SELECT lists can be avoided by folding IDs before joining to organization- possibly even has to be avoided. Imagine a json column in table organization which has no equality/inequality operators. I elaborated in my answer. – Erwin Brandstetter Aug 29 '20 at 22:54
3

I want to answer just one facet of your initial question, the feeling you have that this question has been asked multiple times. The answer is that yes it has but the question has been phrased in a very different way.

What you are basically seeking is information about organizations and persons that are connected to each other (maybe the person works for the organization). You have the further requirement that you are only interested in persons who are admins or moderators, or both.

When it's phrased this way, the question has been asked in this place dozens of times. People are always interested in information about members of one or more subclasses of some more general superclass. In some cases, the superclass is vehicles and the subclasses are cars, motorcycles, and trucks (lorries). In some cases, the superclass is pets, and the subclasses are dogs, cats, and birds.
In the case of a college, the superclass might be persons, and the subclasses might be students, teachers, and staff. In general, people want to combine the information about subclasses with other information generally using relational JOIN to combine data.

Other answers have given you a way to phrase an SQL query that will get you this information given that you have stored the information in three tables: the organization table, the admin-organization table, and the moderator-organization table. I have no improvements to offer over those other answers.


What I would like to suggest is a possible alternative table design, based on five tables. The five tables are: organization, admin, moderator, person, and person-organization. Organization is an entity table with data about organizations. Admin is an entity table with data about admins. Moderator is an entity with data about moderators.

Person is yet another entity table with data about persons. Data like first name and last name can be removed from the admin and moderator table because these attributes are common to persons, and can be stored in the person table instead. Organization-person is a relationship table with data about the relationship between persons and organizations including (at least) personId and organizationId.

The relationship between the admin table and the person table is what is known as an IS-A relationship. The same can be said about the moderator table and the person table.

IS-A relationships are often overlooked in introductory database courses, even though they occur all the time in the real world. Unfortunately, relational modeling techniques don't make this kind of situation as simple as we would wish. In object modeling, the superclass-subclass model takes care of the whole case, and inheritance does a lot of work for the modeler.

I'm going to refer you to three buzzwords that will give you a handle on this design problem. They are Single Table Inheritance, Class Table Inheritance, and Shared Primary Key. Depending on your case, you might choose single table inheritance over class table inheritance. I have chosen class table inheritance in the answer given above.

If you choose class table inheritance, I recommend the additional technique of shared primary key. Here, individual rows in the admin table are identified by personId instead of inventing a new ID for the admin table. The same goes for the moderators table. This makes needed joins simple, easy, and fast.

If you do searches of those three buzzwords, you are going to come up with lots of good articles about dealing with this kind of case. Maybe some day in the future, the people who teach courses about databases will incorporate this stuff into their subject.

Here is a link to one of the earlier questions, with an answer.

Is it a bad practice to have several mutually exclusive one-to-one relationships?

2

Have you considered using outer joins and a where clause?

SELECT *
    FROM organization org
    LEFT OUTER JOIN moderator_organization mo
        ON ( org.id = mo.organization 
            AND mo.user = 10 )
    LEFT OUTER JOIN admin_organization ao
        ON ( org.id = ao.organization 
            AND ao.user = 10 )
    WHERE mo.user IS NOT NULL 
        OR ao.user IS NOT NULL ;

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.