0

A common model: lords have peons, and both lords and peons have things. things can be owned by one or more peons and lords. To display all things owned directly or indirectly by a lord:

SELECT lords.id AS lord_id,
       peons.id AS peon_id,
       things.id AS thing_id
  FROM lords
  LEFT JOIN lords_things ON
       lords.id = lords_things.lord_id
  LEFT JOIN peons ON
       lords.id = peons.lord_id
  LEFT JOIN peons_things ON
       peons.id = peons_things.peon_id
  JOIN things ON
       lords_things.thing_id = things.id OR
       peons_things.thing_id = things.id
 WHERE lords.id = 123

Now there are two problems:

  1. The code will have to look at which of lord_id and peon_id is non-NULL to determine which level the thing is connected to. There are lots of ways to solve this - For example, in Oracle you could say

    SELECT NVL2(things_lords.id, 'lord', 'peon') as level
    

    and in SQL Server you should be able to say

    SELECT CASE WHEN things_lords.id IS NULL THEN 'peon' ELSE 'lord' END AS level
    

    but I don't think either of these are portable (at least to PostgreSQL). A different approach would use a UNION:

    SELECT lords.id AS owner_id,
           'lord' AS level,
           things.id AS thing_id
      FROM lords
      JOIN lords_things ON
           lords.id = lords_things.lord_id
      JOIN things ON
           lords_things.thing_id = things.id
     WHERE lords.id = 123
    UNION
    SELECT peons.id AS owner_id,
           'peon' AS type,
           things.id AS thing_id
      FROM lords
      JOIN peons ON
           lords.id = peons.lord_id
      JOIN peons_things ON
           peons.id = peons_things.peon_id
      JOIN things ON
           peons_things.thing_id = things.id
     WHERE lords.id = 123
    

    this is quite ugly since it duplicates almost all the code. Is there a more elegant portable solution for this?

  2. Some things may show up more than once. This is not a problem in my application, but mentioned for completeness.
4

1 回答 1

1

case语句移植到所有数据库引擎(至少所有相对较新的引擎,MS Access 除外)。从网上看,Postgres 至少从版本 7 开始就支持 case 语句。

因此,请使用案例版本。

至于完整性。你想要这样的东西:

select things.id,
       (case when max(lords.id) is not null and max(peons.id) is not null then 'BOTH'
             when max(lords.id) is not null then 'LORDS'
             when max(peons.id) is not null then 'PEONS'
             else 'Ooops!'
        end) as wherefrom
. . . 
group by things.id

也就是说,做一个聚合并使用聚合函数来总结你想要的。

于 2012-12-17T16:37:28.883 回答