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

具有空值的多个字段的SQL联接,sql,sql-server,join,Sql,Sql Server,Join,我有一份维护要求表和相关的每月执行频率 维护 我还有一个设备表,其中有关于制造商、型号、设备类型和建筑的数据 装备 我正在尝试将每个维护要求与其相关设备相匹配。每项要求适用于其适用范围内的特定制造商、型号、装置、设施或其任何组合。 我创建了一个表来管理这些关系,如下所示: 维护设备 +----------------+----------+--------+--------+--------+---------+ | maint_equip_id | maint_id | mfg_id | mo

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

维护

我还有一个设备表,其中有关于制造商、型号、设备类型和建筑的数据

装备

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

维护设备

+----------------+----------+--------+--------+--------+---------+
| 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将适用于所有制造id为“1”和开发id为“3”以及建筑id为“1”的设备

我正在尝试编写一个查询,根据maint_equipment中定义的关系为我提供所有设备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分隔多个连接意味着它不考虑每行的大小和意外情况。例如,maint_id 2应仅适用于装备id 3,但id 3和4均返回。如果使用AND,则不会返回任何行,因为所有列都没有值


是否有可能以这种方式连接表以完成此操作,或者是否有其他方式来构造数据?

如果我做对了,当
maint\u equipment
中的设备相关ID为空时,应将其视为匹配项。仅当它不为空时,它必须与
设备中的相应ID匹配。也就是说,您要检查
maint\u-equipment
中的ID是否为空或是否等于
equipment
中的对应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;
试试这个:

   ( 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 )

请注意,您的mod_id始终为空。否则,下面的查询将遍历您的所有案例

  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
   ; 

在我看来,您实际上在寻找的是匹配次数最多的维护计划。您可以通过使用带有一系列
大小写
表达式的
和来获得匹配列的计数

然后,您必须考虑多个
maint\u id
值匹配相同次数的关系。对于下面的示例,我选择使用维护频率作为联系断路器,支持更频繁的维护而不是不频繁的维护

Rextester与数据设置的链接:

ORDER BY
子句中的
ROW_NUMBER
按列匹配数(nutty
SUM/CASE
组合)按降序对结果进行排序,以首先获得最多匹配,然后按维护频率按升序对结果进行排序,以利于更频繁的维护。如果您愿意,可以使用
DESC
轻松反转。然后,带有TIES的
TOP(1)
将结果集限制为
ROW\u 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 |
+----------+----------+------+

可能的复制品正是我想要的。非常感谢。
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 |
+----------+----------+------+