Rails Postgres查询以排除包含连接中三个记录之一的任何结果

时间:2016-09-23 16:35:40

标签: ruby-on-rails postgresql activerecord

这是一个难以描述的问题,但我有Rails查询,我加入另一个表,我想排除任何结果,其中连接表包含三个条件之一。

我有一个与Device模型/记录相关的CarUserRole模型。在该CarUserRole记录中,它将包含三个:role - “所有者”,“监视器”,“驱动程序”中的一个。我想在没有相关的CarUserRole记录role: "owner"的情况下返回任何结果。我该怎么做?

这是我的第一次尝试 -

Device.joins(:car_user_roles).where('car_user_roles.role = ? OR car_user_roles.role = ? AND car_user_roles.role != ?', 'monitor', 'driver', 'owner') 

这是sql -

"SELECT \"cars\".* FROM \"cars\" INNER JOIN \"car_user_roles\" ON \"car_user_roles\".\"car_id\" = \"cars\".\"id\" WHERE (car_user_roles.role = 'monitor' OR car_user_roles.role = 'driver' AND car_user_roles.role != 'owner')"

更新

我应该提一下,设备有时会有多个CarUserRole记录。设备可以具有“所有者”和“驱动程序”CarUserRole。我还应该注意,他们只能拥有一个所有者。

Anwser

我最后通过我们的聊天来了解@ Reub的解决方案 -

where(CarUserRole.where("car_user_roles.car_id = cars.id").where(role: 'owner').exists.not)

2 个答案:

答案 0 :(得分:1)

由于car_user_roles表可以具有多个具有相同car_id的记录,因此内部联接可以导致连接表对于cars表中的每一行具有多行。因此,对于car_user_roles表(监视器,所有者和驱动程序)中有3条记录的汽车,连接表中将有3条记录(每条记录具有不同的角色)。您的查询将过滤掉该角色所有者所在的行,但它将与其他两个匹配,导致该查询返回该汽车,即使它有一个角色为“所有者”的记录。

让我们首先尝试为您想要的结果形成一个SQL查询。然后我们可以将其转换为Rails查询。

SELECT * FROM cars WHERE NOT EXISTS (SELECT id FROM car_user_roles WHERE role='owner' AND car_id = cars.id);

如果您希望没有任何car_user_role且具有“所有者”角色的设备,则上述内容就足够了。但这也可以为您提供car_user_roles中没有相应记录的设备。如果要确保设备在car_user_roles中至少有一条记录,可以将以下内容添加到上述查询中。

AND EXISTS (SELECT id FROM car_user_roles WHERE role IN ('monitor', 'driver') AND car_id = cars.id);

现在,我们需要将其转换为Rails查询。

Device.where(
     CarUserRole.where("car_user_roles.car_id = cars.id").where(role: 'owner').exists.not
).where(
     CarUserRole.where("car_user_roles.car_id = cars.id").where(role: ['monitor', 'driver']).exists
).all

如果您的Rails版本支持exists?

,您还可以尝试以下操作
Device.joins(:car_user_roles).exists?(role: ['monitor', 'driver']).exists?(role: 'owner').not.select('cars.*').distinct

答案 1 :(得分:1)

  • 选择不同的汽车

    SELECT DISTINCT (cars.*) FROM cars
    
  • 使用LEFT JOIN拉入car_user_roles

    LEFT JOIN car_user_roles ON cars.id = car_user_roles.car_id
    
  • 仅选择不包含'所有者的车辆。 car_user_role

    WHERE NOT EXISTS(SELECT NULL FROM car_user_roles WHERE cars.id = car_user_roles.car_id AND car_user_roles.role = 'owner')
    
  • 仅选择包含'驱动程序'或者'监控' car_user_role

    AND (car_user_roles.role IN ('driver','monitor'))
    

把它们放在一起:

SELECT DISTINCT (cars.*) FROM cars LEFT JOIN car_user_roles ON cars.id = car_user_roles.car_id WHERE NOT EXISTS(SELECT NULL FROM car_user_roles WHERE cars.id = car_user_roles.car_id AND car_user_roles.role = 'owner') AND (car_user_roles.role IN ('driver','monitor'));

编辑:

  • 直接从Rails执行查询并仅返回找到的对象ID

    ActiveRecord::Base.connection.execute(sql).collect { |x| x['id'] }