如何过滤结果

时间:2016-03-09 16:07:30

标签: mysql sql database greatest-n-per-group

我有一个查询,用于在特定时间点(从vehicle_sightings表中)获取有关车辆所有者的信息。我附上了以下查询部分的片段:

SELECT 
    sighting_id
FROM
    vehicle_sightings
        INNER JOIN
    vehicle_vrn ON vehicle_sightings.plate = vehicle_vrn.vrnno
        INNER JOIN
    vehicle_ownership ON vehicle_vrn.fk_sysno = vehicle_ownership.fk_sysno
WHERE
    vehicle_sightings.seenDate >= vehicle_ownership.ownership_start_date
        AND (vehicle_sightings.seenDate <= vehicle_ownership.ownership_end_date
        OR vehicle_ownership.ownership_end_date IS NULL
        OR vehicle_ownership.ownership_end_date = '0001-01-01 00:00:00')

这适用于车辆历史上只有一个车主的大多数情况。但是,在某些情况下ownership_end_date字段未填写(大多数情况下 填写,因为它表示车辆已转手,并且来自该阶段转发给新主人)。在未填写(或保留默认值)的情况下,将返回该所有权历史记录的所有条目,例如以下情况:

enter image description here

在上面的情况下,查询返回这两个记录,因为seenDate适合它们,因为未填写结束日期(在这种情况下具有默认值)。因此,在这些情况下,我需要修改我的查询以返回具有最高ownership_start_date的记录。

我尝试通过在最后添加以下内容来实现此目的:

GROUP BY sighting_id HAVING seenDate >= MAX(ownership_start_date)

然而这并不起作用,因为返回了更少的记录。有没有一种干净的方法可以实现,也许没有GROUP BY?

2 个答案:

答案 0 :(得分:3)

首先,使用默认日期是一个非常糟糕的主意。您应该已经知道了这一点,因为现在您在某些情况下会对硬编码日期进行编码。当另一个开发人员对数据库进行编码时(或者甚至是你以后的编码),他们现在必须知道并记住他们必须根据某些硬编码的日期对异常进行编码。

此外,您的ownership_end_date应始终位于ownership_start_date之后,此“默认”日期现在将违反。如果您不知道日期或日期尚不存在,那么它应该是NULL - 这正是NULL的用途 - 未知。

对于您的具体问题,您可以使用LEFT JOIN执行此操作,以检查符合条件的其他所有者,如果存在更好的行,则排除该行。你没有提供你所有的桌子结构,而且你是否只是想在最近看到的日期之前想要最新的所有者(我做了什么)或者所有拥有这辆车的车主在看见日期之后都不清楚了,所以我不知道这是否有效,但是这样的话:

SELECT 
    VS.sighting_id    -- ALWAYS use table aliases or prefixes for clarity
FROM
    vehicle_sightings VS
INNER JOIN vehicle_vrn VRN ON VRN.vrnno = VS.plate
INNER JOIN vehicle_ownership VO ON VO.fk_sysno = VRN.fk_sysno
LEFT OUTER JOIN vehicle_ownership VO2 ON
    VO2.fk_sysno = VRN.fk_sysno AND
    VO2.ownership_start_date <= VS.seenDate AND
    (
        VO2.ownership_end_date >= VS.seenDate OR
        VO2.ownership_end_date IS NULL OR
        VO2.ownership_end_date = '0001-01-01 00:00:00'
    ) AND
    VO2.ownership_start_date > VO.ownership_start_date
WHERE
    VS.seenDate >= VO.ownership_start_date AND
    (
        VS.seenDate <= VO.ownership_end_date OR
        VO.ownership_end_date IS NULL OR
        VO.ownership_end_date = '0001-01-01 00:00:00'
    ) AND
    VO2.id IS NULL    -- Or some other non-nullable column

最后一点需要注意:决定命名约定并坚持下去(例如seenDate vs ownership_end_date)并使用有意义的名称(什么是fk_sysno ??)< / p>

答案 1 :(得分:1)

这是一个不需要子查询的解决方案。它确保不存在大于返回记录的所有权记录。

SELECT 
    sighting_id
FROM
    vehicle_sightings
        INNER JOIN
    vehicle_vrn ON vehicle_sightings.plate = vehicle_vrn.vrnno
        INNER JOIN
    vehicle_ownership ON vehicle_vrn.fk_sysno = vehicle_ownership.fk_sysno
        LEFT OUTER JOIN
    vehicle_ownership vo2 ON vo2.fk_sysno = vehicle_ownership.fk_sysno
        AND vo2.ownership_start_date > vehicle_ownership.ownership_start_date
WHERE
    vehicle_sightings.seenDate >= vehicle_ownership.ownership_start_date
        AND (vehicle_sightings.seenDate <= vehicle_ownership.ownership_end_date
        OR vehicle_ownership.ownership_end_date IS NULL
        OR vehicle_ownership.ownership_end_date = '0001-01-01 00:00:00')
        AND vo2.fk_sysno IS NULL