SQL连接具有空值的多个字段

时间:2019-01-28 22:47:12

标签: sql sql-server join

我有一张维护要求表和要执行的相关每月频率

维护

+----------+------+
| maint_id | freq |
+----------+------+
|        1 |    6 |
|        2 |   12 |
|        3 |   24 |
|        4 |    3 |
+----------+------+

我还有一张设备表,其中包含有关其制造商,型号,设备类型和构造的数据。

装备

+----------+--------+--------+--------+---------+
| equip_id | mfg_id | mod_id | dev_id | bldg_id |
+----------+--------+--------+--------+---------+
|        1 |      1 |      1 |      3 |       1 |
|        2 |      1 |      2 |      3 |       1 |
|        3 |      2 |      3 |      1 |       2 |
|        4 |      2 |      3 |      1 |       3 |
+----------+--------+--------+--------+---------+

我正在尝试使每个维护要求与其相关设备匹配。每个要求在其适用范围内都适用于特定的制造商,型号,设备,设施或它们的任意组合。 我创建了一个表来管理这些关系,如下所示:

maint_equip

+----------------+----------+--------+--------+--------+---------+
| maint_equip_id | maint_id | mfg_id | mod_id | dev_id | bldg_id |
+----------------+----------+--------+--------+--------+---------+
|              1 |        1 | NULL   | NULL   | 1      | NULL    |
|              2 |        2 | 2      | NULL   | NULL   | 2       |
|              3 |        3 | NULL   | NULL   | NULL   | 1       |
|              4 |        3 | NULL   | NULL   | NULL   | 3       |
|              5 |        4 | 1      | NULL   | 3      | 1       |
+----------------+----------+--------+--------+--------+---------+

根据上表,要求1仅适用于设备类型为“ 1”的任何设备。 要求2适用于制造商为“ 2”且建筑物为“ 2”的所有设备。 要求3将适用于所有建筑物为“ 1”或建筑物为“ 3”的设备 要求4适用于所有mfg_id为“ 1”且dev_id为“ 3”且建筑物为“ 1”的设备。

我正在尝试编写一个查询,以便根据maint_equip中定义的关系为我提供所有设备ID和所有相关频率要求的列表。我遇到的问题是处理多个联接。我已经尝试过:

SELECT  equip.equip_id, maint.freq
FROM    equip INNER JOIN
    maint_equip ON equip.mfg_id = maint_equip.mfg_id 
        OR equip.mod_id = maint_equip.mod_id 
        OR equip.dev_id = maint_equip.dev_id 
        OR equip.bldg_id = maint_equip.bldg_id INNER JOIN
    maint ON maint_equip.maint_id = maint.maint_id

但是使用OR分隔多个联接意味着它不考虑每一行的AND偶然性。例如,maint_id 2应该仅适用于equip_id 3,但同时返回ID 3和4。如果使用AND,则不会返回任何行,因为所有列都没有值。

是否可以通过这种方式联接表来完成此操作,或者是否有另一种结构化数据的方式?

4 个答案:

答案 0 :(得分:1)

如果我没看错,当maint_equip中与设备相关的ID为空时,这应该算作匹配项。仅当它不为null时,它才必须与equip中的相应ID相匹配。也就是说,您要检查maint_equip中的ID是否为空或等于equip中的ID。

SELECT e.equip_id,
       m.freq
       FROM equip e
            INNER JOIN maint_equip me
                       ON (me.mfg_id IS NULL
                            OR me.mfg_id = e.mfg_id)
                          AND (me.mod_id IS NULL
                                OR me.mod_id = e.mod_id)
                          AND (me.dev_id IS NULL
                                OR me.dev_id = e.dev_id)
                          AND (me.bldg_id IS NULL
                                OR me.bldg_id = e.bldg_id)
            INNER JOIN maint m
                       ON m.maint_id = me.main_id;

答案 1 :(得分:0)

尝试一下:

   ( equip.mfg_id  = maint_equip.mfg_id  OR maint_equip.mfg_id  is null )
AND( equip.mod_id  = maint_equip.mod_id  OR maint_equip.mod_id  is null )
AND( equip.dev_id  = maint_equip.dev_id  OR maint_equip.dev_id  is null )
AND( equip.bldg_id = maint_equip.bldg_id OR maint_equip.bldg_id is null )

答案 2 :(得分:0)

请注意,您的mod_id始终为null。否则,下面的查询将涉及您的所有情况。

  SELECT  maint_equip.maint_id, equip.equip_id, maint.freq
FROM    equip INNER JOIN
    maint_equip ON (
    (equip.mfg_id = maint_equip.mfg_id AND 
        equip.dev_id = maint_equip.dev_id AND
        equip.bldg_id = maint_equip.bldg_id
     ) OR
     (equip.mfg_id = maint_equip.mfg_id AND 
        maint_equip.dev_id is NULL AND
        equip.bldg_id = maint_equip.bldg_id
     ) OR
     (maint_equip.mfg_id is NULL AND 
        equip.dev_id = maint_equip.dev_id AND
        maint_equip.bldg_id is NULL
     ) OR 
     (maint_equip.mfg_id is NULL AND
        maint_equip.dev_id is NULL AND
        equip.bldg_id = maint_equip.bldg_id
     ) )


      INNER JOIN
    maint ON maint_equip.maint_id = maint.maint_id
   ; 

答案 3 :(得分:0)

在我看来,您实际上要寻找的是具有最高匹配数量的维护时间表。您可以通过将SUM与一系列CASE表达式一起使用来获取匹配列的数量。

然后,您必须考虑多个具有相同次数的maint_id值的关系。在下面的示例中,我选择使用维护频率作为决定性因素,与不那么频繁的维护相比,倾向于更频繁的维护。

具有数据设置的话机链接:https://rextester.com/VISR88105

ROW_NUMBER子句中的ORDER BY对结果进行排序以列匹配项的数目(螺母SUM/CASE组合)降序排列,以首先获得最大的匹配项,然后是维护频率以升序排列以支持更频繁的维护。如果愿意,可以轻松地使用DESC来解决。然后TOP (1) WITH TIES将结果集限制为ROW_NUMBER求值为1的所有行。

代码:

SELECT TOP (1) WITH TIES
  e.equip_id, 
  m.maint_id, 
  m.freq
FROM 
  #maint as m
JOIN
  #maint_equip as me
    ON
      m.maint_id = me.maint_id
JOIN
  #equip as e
    ON 
      e.mfg_id = COALESCE(me.mfg_id, e.mfg_id)
      AND
      e.mod_id = COALESCE(me.mod_id, e.mod_id)
      AND
      e.dev_id = COALESCE(me.dev_id, e.dev_id)
      AND
      e.bldg_id = COALESCE(me.bldg_id, e.bldg_id)
GROUP BY 
  e.equip_id, 
  m.maint_id, 
  m.freq
ORDER BY 
  ROW_NUMBER() OVER (PARTITION BY e.equip_id ORDER BY (
    SUM( 
    (CASE WHEN e.mfg_id = me.mfg_id THEN 1 ELSE 0 END) +
    (CASE WHEN e.mod_id = me.mod_id THEN 1 ELSE 0 END) +
    (CASE WHEN e.dev_id = me.dev_id THEN 1 ELSE 0 END) +
    (CASE WHEN e.bldg_id = me.bldg_id THEN 1 ELSE 0 END)) ) DESC, m.freq )

结果:

+----------+----------+------+
| equip_id | maint_id | freq |
+----------+----------+------+
|        1 |        4 |    3 |
|        2 |        4 |    3 |
|        3 |        2 |   12 |
|        4 |        1 |    6 |
+----------+----------+------+