具有空值的多个字段的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
按列匹配数(nuttySUM/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 |
+----------+----------+------+