按照MySQL中的条件顺序LEFT JOIN到单行

时间:2013-10-13 21:18:50

标签: mysql sql

好的,我试图通过抽象细节来简化我的问题,但我担心我不清楚并且不符合主持人的要求。因此,我将更详细地发布我的问题的完整查询和我正在努力的实际查询。如果问题仍然不充分,请您详细说明不清楚的内容,我会尽力澄清。

首先,这是当前查询,它返回每张床的所有分配行:

SELECT 
  beds.bed_id,
  beds.bedstatus,
  beds.position as bed_position,
  rooms.room_id,
  rooms.room,
  wings.wing_id,
  wings.name as wing_name,
  buildings.building_id,
  buildings.name as building_name,
  assignments.assignment_id,
  assignments.student_id,
  assignments.assign_dt,
  assignments.assigned_by,
  assignments.assignment_status,
  assignments.expected_arrival_dt as arrival_dt,
  assignments.room_charge_type,
  students.first_name,
  students.last_name,
  meal_plans.name as meal_plan_name,
  room_rates.rate_name
FROM
  beds
LEFT JOIN 
  rooms ON (beds.room_id = rooms.room_id)
LEFT JOIN
  wings ON (rooms.wing_id = wings.wing_id)
LEFT JOIN
  buildings ON (wings.building_id = buildings.buildings_id)
LEFT JOIN assignments ON 
  ((beds.bed_id=assignments.bed_id) AND (term_id = @term_id))
LEFT JOIN
  students ON (assignments.student_id = students.student_id)
LEFT JOIN
  meal_plans ON (assignments.meal_plan_id = meal_plans.meal_plan_id)
LEFT JOIN
  room_rates ON (room_rate_id = room_rates.room_rate_id)
WHERE
  (
    (rooms.room IS NOT NULL) AND
    (rooms.assignable = 1) AND
    (buildings.active = 1) AND
    (buildings.building_id = @building_id)
  )
ORDER BY BY rooms.room;

问题是每个房间的“assignments”表中可能有多行由“assignment_status”字段区分,我希望每个分配都有一行。我想根据assignment_status中的值确定要选择的分配行。也就是说,如果分配状态是“活动”,我想要那一行,否则,如果有一行状态为“等待批准”,那么我想要那行等等......

Barmar的建议在这里给出:

LEFT JOIN (SELECT *
       FROM OtherTable
       WHERE <criteria>
       ORDER BY CASE status
                    WHEN 'Active' THEN 1
                    WHEN 'Waiting Approval' THEN 2
                    WHEN 'Canceled' THEN 3
                    ...
                END
       LIMIT 1) other

这非常有帮助,我尝试了这种方法:

SELECT 
  beds.bed_id,
  beds.bedstatus,
  beds.position as bed_position,
  rooms.room_id,
  rooms.room,
  wings.wing_id,
  wings.name as wing_name,
  buildings.building_id,
  buildings.name as building_name,
  assign.assignment_id,
  assign.student_id,
  assign.assign_dt,
  assign.assigned_by,
  assign.assignment_status,
  assign.expected_arrival_dt as arrival_dt,
  assign.room_charge_type,
  students.first_name,
  students.last_name,
  meal_plans.name as meal_plan_name,
  room_rates.rate_name
FROM
  beds
LEFT JOIN 
  rooms ON (beds.room_id = rooms.room_id)
LEFT JOIN
  wings ON (rooms.wing_id = wings.wing_id)
LEFT JOIN
  buildings ON (wings.building_id = buildings.buildings_id)

LEFT JOIN (SELECT *
           FROM assignments
           WHERE ((assignments.bed_id==beds.bed_id) AND (term_id = @term_id))
           ORDER BY CASE assignment_status
                        WHEN 'Active' THEN 1
                        WHEN 'Waiting Approval' THEN 2
                        WHEN 'Canceled' THEN 3
                    END
           LIMIT 1) assign  

LEFT JOIN
  students ON (assign.student_id = students.student_id)
LEFT JOIN
  meal_plans ON (assign.meal_plan_id = meal_plans.meal_plan_id)
LEFT JOIN
  room_rates ON (room_rate_id = room_rates.room_rate_id)
WHERE
  (
    (rooms.room IS NOT NULL) AND
    (rooms.assignable = 1) AND
    (buildings.active = 1) AND
    (buildings.building_id = @building_id)
  )
ORDER BY rooms.room;

但我意识到,这里的问题是OtherTable(赋值)基于FK加入父查询:

  ((beds.bed_id=assignments.bed_id) AND (term_id = @term_id))

所以我不能做subselect,因为bed.bed_id不在subselect的范围内。因此,Barmar的评论表明连接标准需要在子选择之外 - 但我无法弄清楚如何将结果限制为每个房间的单行并将连接移动到子选择之外。我想知道travelboy建议使用GROUP BY可能会更有成效,但是无法确定如何进行分组。

如果我能提供额外的澄清,请告诉我。

原始问题:

我需要从表A在另一个表中的SINGLE行上执行LEFT JOIN,表B符合某些标准(表B中可能有多行或没有符合条件的行)。如果有多行,我想根据表B中字段的值选择B中要连接的哪一行。例如,如果B中有一行,状态列='活动',我想要该行,如果不是,如果有一行status ='Waiting Approval',我想要那一行,如果有一行status ='Cancelled',我想要那行等等......我可以不用子选择吗?使用子选择?

3 个答案:

答案 0 :(得分:4)

使用:

LEFT JOIN (SELECT *
           FROM OtherTable
           WHERE <criteria>
           ORDER BY CASE status
                        WHEN 'Active' THEN 1
                        WHEN 'Waiting Approval' THEN 2
                        WHEN 'Canceled' THEN 3
                        ...
                    END
           LIMIT 1) other

答案 1 :(得分:1)

在某些情况下(但并非在所有情况下),您可以在没有子选择的情况下执行此操作。您需要在表A中GROUP BY一个唯一字段,通常是ID。这可以确保您只从表B中获得一行(或没有)。但是,选择所需的行是棘手的部分。您需要一个聚合函数,如MAX()。如果B中的字段是数字,那很容易做到。如果没有,您可以在B中的字段上应用一些SQL函数来计算类似于要排序的分数。例如,Active可能对应的值高于Cancelled等。这将在没有子选择的情况下工作,并且可能在大数据集上更快。

通过子选择很容易做到。您可以使用Barmar的解决方案,或者,如果您只需要B中的一个特定字段,您也可以将子选择放在外部查询的SELECT子句中。

答案 2 :(得分:0)

我需要跟进一些额外的测试,以确保这是完成我的目标 - 但我认为我已经使用travelboy建议的一组查询结合barmar的案例逻辑(希望我可以分开答案) )。这是查询:

SELECT 
  beds.bed_id,
  beds.bedstatus,
  beds.position as bed_position,
  rooms.room_id,
  rooms.room,
  wings.wing_id,
  wings.name as wing_name,
  buildings.building_id,
  buildings.name as building_name,
  assignments.assignment_id,
  assignments.student_id,
  assignments.assign_dt,
  assignments.assigned_by,
  assignments.assignment_status,
  assignments.expected_arrival_dt as arrival_dt,
  assignments.room_charge_type,
  MIN(CASE assignments.assignment_status
         WHEN 'Active' THEN 1
         WHEN 'Waiting Approval' THEN 2
         WHEN 'Canceled' THEN 3
       END),
  students.first_name,
  students.last_name,
  meal_plans.name as meal_plan_name,
  room_rates.rate_name
FROM
  beds
LEFT JOIN 
  rooms ON (beds.room_id = rooms.room_id)
LEFT JOIN
  wings ON (rooms.wing_id = wings.wing_id)
LEFT JOIN
  buildings ON (wings.building_id = buildings.building_id)

LEFT JOIN assignments
  ON ((assignments.bed_id=beds.bed_id) AND (term_id = 28))
LEFT JOIN
  students ON (assignments.student_id = students.student_id)
LEFT JOIN
  meal_plans ON (assignments.meal_plan_id = meal_plans.meal_plan_id)
LEFT JOIN
  room_rates ON (assignments.room_rate_id = room_rates.room_rate_id)
WHERE
  (
    (rooms.room IS NOT NULL) AND
    (rooms.assignable = 1) AND
    (buildings.active = 1)
  )
GROUP BY
  bed_id
ORDER BY rooms.room;