执行条件连接的有效方法

问题描述 投票:0回答:1

有什么方法可以提供条件连接而不是

case when ... then ... end

我有 3 张桌子:

vehicles
sport_cars
motorcycles

  • vehicles
    - 存储
    sport_cars
    motorcycles
  • 的通用数据的基表
  • sprot_cars
    - 包含跑车特定数据的表格
  • motorcycles
    - 包含摩托车特定数据的表格

此外,

vehicles
有一个鉴别器列
type
,其中包含varchar值
sport_car
/
motorcycle

示例模型:

车辆:

  • id bigint,
  • 类型 varchar(20)
  • 成本数(7,2)
  • sport_car_id bigint,
  • motorcycle_id bigint,

运动车:

  • id bigint
  • is_cabriolet 布尔值
  • 座位整数

摩托车:

  • id bigint
  • is_motocross:布尔值
  • 处理程序:整数

我需要按类型检索所有车辆,并能够使用限制和偏移,使用单个查询,但无法找出它的方法。

select 
  r.id, 
  r.cost,
  case 
    when :type = 'sport_car' then s.is_cabriolet
    else m.is_motocross
  end as flag
from (select 
    v.id,
    v.cost,
    v.sport_car_id,
    v.motorcycle_id
  from vehicles as v
  where
    v.type = :type
  limit :limit
  offset :offset) as r
left join sport_cars s on r.sport_car_id = s.id
left join motorcycles m on r.motorcycle_id = m.id

也许这个解决方案可行,但效果相当低,因为我们至少提供了一个奇数连接。另外,如果可以有多个表而不是 2 个,那么加入所有这些表将成为一场噩梦。

另外,我尝试使用

union all
检索它,但问题是为此类查询提供限制和偏移量会很棘手。

看起来,这可能是设计不好的问题,但这个模型已经存在,看起来不需要更新。

有什么办法可以为加入提供某种条件吗?或者也许有另一种方法来选择必要的数据?

sql postgresql left-join
1个回答
0
投票

您的数据库建模不正确。有几个裁员...

  1. 外键 sport_car_id 和 Motorola_id 不感兴趣。车辆表的主键必须作为子表的外键,同时作为主键

  2. 类型列不重要,因为它可以从连接中推导出来

正确的型号是:

CREATE TABLE vehicles
(   id bigint PRIMARY KEY,
    cost decimal(7,2) -- instead of number
);

CREATE TABLE sport_cars
(   id bigint PRIMARY KEY REFERENCES vehicles (id),
    is_cabriolet boolean,
    seats integer
);

CREATE TABLE motorcycles
(   id bigint PRIMARY KEY REFERENCES vehicles (id),
    is_motocross boolean,
    handlers integer
);

这在建模术语中称为继承...

这个模型必须通过触发器来完成,以确保排他性约束:车辆要么是摩托车,要么是汽车,换句话说,两个子表中永远不能有相同的 id 值....

这是这两个触发器之一:

CREATE FUNCTION sport_cars_id_exists() 
   RETURNS trigger
   LANGUAGE plpgsql
AS $$
  DECLARE
    id_exists boolean;
  BEGIN
    SELECT (id IS NOT NULL) FROM sport_cars WHEREid = NEW.id INTO id_exists;
    IF id_exists THEN
      RAISE EXCEPTION 'Exclude exception between motorcycle and car (id : %)', NEW.id;
      RETURN NULL;
    END IF;
    RETURN NEW;
  END; $$;


CREATE TRIGGER EVT_IU_motorcycles
   BEFORE INSERT OR UPDATE 
   ON motorcycles
   FOR EACH STATEMENT EXECUTE PROCEDURE sport_cars_id_exists();

现在要知道车辆是摩托车还是汽车,您只需要知道连接是否存在于一个或另一个表中,如下所示:

SELECT *, CASE WHEN EXISTS(SELECT * FROM sport_cars AS c WHERE m.id = v.id)
                       THEN 'Sport Car'
                    EXISTS(SELECT * FROM motorcycles AS m WHERE m.id = v.id)
                       THEN 'Sport Car'
       END AS vehicle_type
FROM   vehicles AS v

这个查询应该是开发中系统使用的视图...

© www.soinside.com 2019 - 2024. All rights reserved.