自我加入并仅忽略一个 table / 侧的空值
self join and ignore nulls on one table / side only
我有 2 个 table。
一个 table 定义客户连接:
CREATE TABLE IF NOT EXISTS `cust_connections` (
`id` int(11) NOT NULL,
`short_name` char(15) COLLATE utf8_unicode_ci NOT NULL,
`source_fnn` char(10) COLLATE utf8_unicode_ci NOT NULL,
`dest_fnn` char(10) COLLATE utf8_unicode_ci NOT NULL,
`service_type` char(32) COLLATE utf8_unicode_ci NOT NULL,
`ladder_side` char(10) COLLATE utf8_unicode_ci NOT NULL
) ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
INSERT INTO `cust_connections` (`id`, `short_name`, `source_fnn`, `dest_fnn`, `service_type`, `ladder_side`) VALUES
(1, 'cust1', 'N2843453A', '', 'HD_300_Connect', 'src only'),
(2, 'cust2', '', 'N2843600A', 'HD_300_Connect', 'dest only'),
(3, 'cust3', 'N2720257O', 'N2731164O', 'DVB25_188byte', 'both'),
(4, 'cust4', 'N27xxx7O', 'N2731164O', 'DVB25_188byte', 'src ukn'),
(5, 'cust4', 'N27xxx7O', '', 'DVB25_188byte', 'ukn +blk');
ALTER TABLE `cust_connections`
ADD PRIMARY KEY (`id`);
ALTER TABLE `cust_connections`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=18;
其他table定义装备:
CREATE TABLE IF NOT EXISTS `cust_port` (
`id` smallint(11) NOT NULL,
`system_name` char(32) COLLATE utf8_unicode_ci NOT NULL,
`slot_no` char(2) COLLATE utf8_unicode_ci NOT NULL,
`port_no` char(2) COLLATE utf8_unicode_ci NOT NULL,
`port_fnn` char(9) COLLATE utf8_unicode_ci NOT NULL
) ENGINE=InnoDB AUTO_INCREMENT=29 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
INSERT INTO `cust_port` (`id`, `system_name`, `slot_no`, `port_no`, `port_fnn`) VALUES
(1, '01-06C2:source', '7', '1', 'N2843453A'),
(2, '01-27B4:dest', '1', '2', 'N2843600A'),
(3, '01-27B6:source+dst', '17', '3', 'N2720257O'),
(4, '01-27B6:dst+src', '17', '3', 'N2731164O'),
(5, '01-32C6:dup_fnn1', '1', '2', 'N2845070O'),
(26, '01-32C6:dup_fnn2', '1', '3', 'N2845070O'),
(27, '01-32D6:no_fnn', '1', '4', ''),
(28, '01-32D6:diff_fnn', '1', '4', 'x123456');
ALTER TABLE `cust_port`
ADD PRIMARY KEY (`id`);
ALTER TABLE `cust_port`
MODIFY `id` smallint(11) NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=29;
SQl 结果是:
cc_id short_name source_fnn dest_fnn service_type ladder_side src_system_name src_slot_no src_port_no src_port_fnn dst_system_name dst_slot_no dst_port_no dst_port_fnn
1 cust1 N2843453A HD_300_Connect src only 01-06C2:source 7 1 N2843453A 01-32D6:no_fnn 1 4
2 cust2 N2843600A HD_300_Connect dest only 01-32D6:no_fnn 1 4 01-27B4:dest 1 2 N2843600A
3 cust3 N2720257O N2731164O DVB25_188byte both 01-27B6:source+dst 17 3 N2720257O 01-27B6:dst+src 17 3 N2731164O
4 cust4 N27xxx7O N2731164O DVB25_188byte src ukn NULL NULL NULL NULL 01-27B6:dst+src 17 3 N2731164O
5 cust4 N27xxx7O DVB25_188byte ukn +blk NULL NULL NULL NULL 01-32D6:no_fnn 1 4
我正在对两个 table 进行连接。
问题是,如果 port_fnn 为空,我想排除该行,但如果任何客户来源或目标 fnn 为空,我想显示该行。
我正在进行左(自)连接以匹配源和目标 fnn 与设备 fnn。不幸的是,我的客户 fnns 必须能够有空值。
如果设备 table 中没有空值,我的查询会很好用。
我的查询是:
SELECT
cc.id AS cc_id, short_name,source_fnn, dest_fnn, service_type,ladder_side,
src.system_name AS src_system_name,
src.slot_no AS src_slot_no,
src.port_no AS src_port_no,
src.port_fnn AS src_port_fnn,
dst.system_name AS dst_system_name,
dst.slot_no AS dst_slot_no,
dst.port_no AS dst_port_no,
dst.port_fnn AS dst_port_fnn
FROM cust_connections cc
LEFT JOIN cust_port src on cc.source_fnn=src.port_fnn
LEFT JOIN cust_port dst on cc.dest_fnn=dst.port_fnn
在我的结果集中:
第 1 行 - 只有源 fnn。我希望目标字段的结果为空,即:
cc_id short_name source_fnn dest_fnn service_type ladder_side src_system_name src_slot_no src_port_no src_port_fnn dst_system_name dst_slot_no dst_port_no dst_port_fnn
1 cust1 N2843453A HD_300_Connect src only 01-06C2:source 7 1 N2843453A NULL NULL NULL NULL
查询检测到空 fnn 并填充没有关联 fnn 的设备。即:01-32D6:no_fnn.
第 2 行的 source_system_name 和第 5 行的 dst_system_name 出现同样的问题。
[这个答案是在早期版本的 qeustion 的上下文中,其中示例输入不清楚地将空字符串 ie '' 描述为 NULL/< NULL >/null 所以它看起来像 "" 值是 NULL .如果他们为 NULL,则应用此答案。 Sqlfiddle]
错误"desired output"
你的 "desired output" 中的 equip4 1/2 是一个错误,你想要 equip1 1/2 来自端口 id 2 通过 4444。在你更正之后,所需的输出就是你在别处描述的(不清楚) .
NULL port_fnn不是问题
null port_fnn 没有将任何内容放入查询中的 table。 LEFT JOIN 永远不会匹配它。
你想要什么行?
你不清楚。唯一没有处理的行是非 NULL source_fnn 或 dest_fnn 没有匹配的 port_fn。然后在 LEFT JOIN 输出中它与 NULL port_fnn info.
配对
如果您不想要这些行,那就太奇怪了。您不仅没有 cust_connection 的行,而且扩展信息的另一半也会被丢弃。也许您只会丢弃 source 和 dest 都为非 NULL 且匹配的行。还是很奇怪。您必须告诉我们您是否需要这些行。想必您需要它们,因为它们在您的查询中。
如果每个source_fnn和dest_fnn都有一个匹配的port_fnn,即有从source_fnn和dest_fnn到port_fnn的外键,那么这永远不会发生,您的查询是正确的。
所以你的查询似乎没问题。
而且您似乎错误地猜测 NULL port_fnn 无法解释为什么它与您错误的预期输出不同。
您可以添加一个 where
子句,例如:
where (cc.source_fnn is null or cc.dest_fnn is null)
or (src.port_fnn is not null or dst.port_fnn is not null)
现在它将始终显示带有空 source_fnn
或 dest_fnn
的行。当两者都被填充时,它将过滤掉匹配的 port_fnn
列为空的行。
因此您将获得缺少外键的行,但会抑制外键引用具有空列的行的行。至少那是我认为你正在寻找的。如果不是,请澄清您的问题。
我认为您可以通过对每个 source/destination 列使用 IF() 来屏蔽名称和端口来完成您正在尝试做的事情。大多数时候人们会尝试做一些事情来防止空值并显示类似空字符串的东西......相反,你想要相反......如果 "port_fnn" 为空,你想要隐藏这些元素。
所以我为每一列都做了一个 IF(表达式,结果为真,结果为假)。因此,如果 port_fnn 为 NULL,则结果显示为 null,否则 return 无论列是什么(系统名称、插槽、端口等)
SELECT
cc.id AS cc_id,
short_name,
source_fnn,
dest_fnn,
service_type,ladder_side,
if( src.port_fnn = '', NULL, src.system_name ) AS src_system_name,
if( src.port_fnn = '', NULL, src.slot_no ) AS src_slot_no,
if( src.port_fnn = '', NULL, src.port_no ) AS src_port_no,
if( src.port_fnn = '', NULL, src.port_fnn ) AS src_port_fnn,
if( dst.port_fnn = '', NULL, dst.system_name ) AS dst_system_name,
if( dst.port_fnn = '', NULL, dst.slot_no ) AS dst_slot_no,
if( dst.port_fnn = '', NULL, dst.port_no ) AS dst_port_no,
if( dst.port_fnn = '', NULL, dst.port_fnn ) AS dst_port_fnn
FROM
cust_connections cc
LEFT JOIN cust_port src
on cc.source_fnn = src.port_fnn
LEFT JOIN cust_port dst
on cc.dest_fnn = dst.port_fnn
我调整了上面的查询以处理您的数据...NULL 不同于空字符串。我将您的表和示例数据复制到 SQL Fiddle,然后是上述查询。它似乎分别为源和目标显示 NULLS。
''(空字符串)不为 NULL。 (哪个 sqlfiddle 输出为“(null)”。)
在文本中,不要像您最初在问题中那样写 "NULL" 或“”或 "null" 或“(null)”来指代空字符串。清楚什么是 '' 什么是 NULL.
'' = '' 但 NULL <> NULL。因此,当相等性测试涉及 '' 时,您的 LEFT JOIN 会在列 cc.source_fnn 和 src.port_fnn 之间以及列 cc.dest_fnn 和 cust_port port_fnn 之间找到匹配项。但是您不希望 LEFT JOIN 匹配这些行。
你可以这样说:
声明所有 _fnn 列可为空,即声明为 NULL(默认)而不是 NOT NULL,并在您现在使用 ''(空字符串)的表中使用 NULL。那么您的查询将给出正确答案!
要求 port_fnn <> '':
FROM cust_connections cc
LEFT JOIN cust_port src
ON cc.source_fnn=src.port_fnn AND cc.source_fnn <> ''
LEFT JOIN cust_port dst
ON cc.dest_fnn=dst.port_fnn AND cc.dest_fnn <> '';
在 LEFT JOIN 之前从 cust_port 中删除这些行:
FROM cust_connections cc
LEFT JOIN
(SELECT * FROM cust_port WHERE port_fnn <> ''
) src
ON cc.source_fnn=src.port_fnn
LEFT JOIN
(SELECT * FROM cust_port WHERE port_fnn <> ''
) dst
ON cc.dest_fnn=dst.port_fnn;
SQLfiddle for 1 using NULL and for 2 & 3 using ''。这些添加了第二个 cust_port 行,缺少 port_fnn 以表明以上给出了正确的结果。您与 '' 一起使用的查询错误地从中生成了其他虚假行。
如果您希望 LEFT JOIN 中的 NULL 在输出中显示为空字符串,那么您可以对这些列使用 IFNULL:
IFNULL(dst.port_fnn,'') AS dst_port_fnn
我有 2 个 table。
一个 table 定义客户连接:
CREATE TABLE IF NOT EXISTS `cust_connections` (
`id` int(11) NOT NULL,
`short_name` char(15) COLLATE utf8_unicode_ci NOT NULL,
`source_fnn` char(10) COLLATE utf8_unicode_ci NOT NULL,
`dest_fnn` char(10) COLLATE utf8_unicode_ci NOT NULL,
`service_type` char(32) COLLATE utf8_unicode_ci NOT NULL,
`ladder_side` char(10) COLLATE utf8_unicode_ci NOT NULL
) ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
INSERT INTO `cust_connections` (`id`, `short_name`, `source_fnn`, `dest_fnn`, `service_type`, `ladder_side`) VALUES
(1, 'cust1', 'N2843453A', '', 'HD_300_Connect', 'src only'),
(2, 'cust2', '', 'N2843600A', 'HD_300_Connect', 'dest only'),
(3, 'cust3', 'N2720257O', 'N2731164O', 'DVB25_188byte', 'both'),
(4, 'cust4', 'N27xxx7O', 'N2731164O', 'DVB25_188byte', 'src ukn'),
(5, 'cust4', 'N27xxx7O', '', 'DVB25_188byte', 'ukn +blk');
ALTER TABLE `cust_connections`
ADD PRIMARY KEY (`id`);
ALTER TABLE `cust_connections`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=18;
其他table定义装备:
CREATE TABLE IF NOT EXISTS `cust_port` (
`id` smallint(11) NOT NULL,
`system_name` char(32) COLLATE utf8_unicode_ci NOT NULL,
`slot_no` char(2) COLLATE utf8_unicode_ci NOT NULL,
`port_no` char(2) COLLATE utf8_unicode_ci NOT NULL,
`port_fnn` char(9) COLLATE utf8_unicode_ci NOT NULL
) ENGINE=InnoDB AUTO_INCREMENT=29 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
INSERT INTO `cust_port` (`id`, `system_name`, `slot_no`, `port_no`, `port_fnn`) VALUES
(1, '01-06C2:source', '7', '1', 'N2843453A'),
(2, '01-27B4:dest', '1', '2', 'N2843600A'),
(3, '01-27B6:source+dst', '17', '3', 'N2720257O'),
(4, '01-27B6:dst+src', '17', '3', 'N2731164O'),
(5, '01-32C6:dup_fnn1', '1', '2', 'N2845070O'),
(26, '01-32C6:dup_fnn2', '1', '3', 'N2845070O'),
(27, '01-32D6:no_fnn', '1', '4', ''),
(28, '01-32D6:diff_fnn', '1', '4', 'x123456');
ALTER TABLE `cust_port`
ADD PRIMARY KEY (`id`);
ALTER TABLE `cust_port`
MODIFY `id` smallint(11) NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=29;
SQl 结果是:
cc_id short_name source_fnn dest_fnn service_type ladder_side src_system_name src_slot_no src_port_no src_port_fnn dst_system_name dst_slot_no dst_port_no dst_port_fnn
1 cust1 N2843453A HD_300_Connect src only 01-06C2:source 7 1 N2843453A 01-32D6:no_fnn 1 4
2 cust2 N2843600A HD_300_Connect dest only 01-32D6:no_fnn 1 4 01-27B4:dest 1 2 N2843600A
3 cust3 N2720257O N2731164O DVB25_188byte both 01-27B6:source+dst 17 3 N2720257O 01-27B6:dst+src 17 3 N2731164O
4 cust4 N27xxx7O N2731164O DVB25_188byte src ukn NULL NULL NULL NULL 01-27B6:dst+src 17 3 N2731164O
5 cust4 N27xxx7O DVB25_188byte ukn +blk NULL NULL NULL NULL 01-32D6:no_fnn 1 4
我正在对两个 table 进行连接。
问题是,如果 port_fnn 为空,我想排除该行,但如果任何客户来源或目标 fnn 为空,我想显示该行。
我正在进行左(自)连接以匹配源和目标 fnn 与设备 fnn。不幸的是,我的客户 fnns 必须能够有空值。
如果设备 table 中没有空值,我的查询会很好用。 我的查询是:
SELECT
cc.id AS cc_id, short_name,source_fnn, dest_fnn, service_type,ladder_side,
src.system_name AS src_system_name,
src.slot_no AS src_slot_no,
src.port_no AS src_port_no,
src.port_fnn AS src_port_fnn,
dst.system_name AS dst_system_name,
dst.slot_no AS dst_slot_no,
dst.port_no AS dst_port_no,
dst.port_fnn AS dst_port_fnn
FROM cust_connections cc
LEFT JOIN cust_port src on cc.source_fnn=src.port_fnn
LEFT JOIN cust_port dst on cc.dest_fnn=dst.port_fnn
在我的结果集中: 第 1 行 - 只有源 fnn。我希望目标字段的结果为空,即:
cc_id short_name source_fnn dest_fnn service_type ladder_side src_system_name src_slot_no src_port_no src_port_fnn dst_system_name dst_slot_no dst_port_no dst_port_fnn
1 cust1 N2843453A HD_300_Connect src only 01-06C2:source 7 1 N2843453A NULL NULL NULL NULL
查询检测到空 fnn 并填充没有关联 fnn 的设备。即:01-32D6:no_fnn.
第 2 行的 source_system_name 和第 5 行的 dst_system_name 出现同样的问题。
[这个答案是在早期版本的 qeustion 的上下文中,其中示例输入不清楚地将空字符串 ie '' 描述为 NULL/< NULL >/null 所以它看起来像 "" 值是 NULL .如果他们为 NULL,则应用此答案。 Sqlfiddle]
错误"desired output"
你的 "desired output" 中的 equip4 1/2 是一个错误,你想要 equip1 1/2 来自端口 id 2 通过 4444。在你更正之后,所需的输出就是你在别处描述的(不清楚) .
NULL port_fnn不是问题
null port_fnn 没有将任何内容放入查询中的 table。 LEFT JOIN 永远不会匹配它。
你想要什么行?
你不清楚。唯一没有处理的行是非 NULL source_fnn 或 dest_fnn 没有匹配的 port_fn。然后在 LEFT JOIN 输出中它与 NULL port_fnn info.
配对如果您不想要这些行,那就太奇怪了。您不仅没有 cust_connection 的行,而且扩展信息的另一半也会被丢弃。也许您只会丢弃 source 和 dest 都为非 NULL 且匹配的行。还是很奇怪。您必须告诉我们您是否需要这些行。想必您需要它们,因为它们在您的查询中。
如果每个source_fnn和dest_fnn都有一个匹配的port_fnn,即有从source_fnn和dest_fnn到port_fnn的外键,那么这永远不会发生,您的查询是正确的。
所以你的查询似乎没问题。
而且您似乎错误地猜测 NULL port_fnn 无法解释为什么它与您错误的预期输出不同。
您可以添加一个 where
子句,例如:
where (cc.source_fnn is null or cc.dest_fnn is null)
or (src.port_fnn is not null or dst.port_fnn is not null)
现在它将始终显示带有空 source_fnn
或 dest_fnn
的行。当两者都被填充时,它将过滤掉匹配的 port_fnn
列为空的行。
因此您将获得缺少外键的行,但会抑制外键引用具有空列的行的行。至少那是我认为你正在寻找的。如果不是,请澄清您的问题。
我认为您可以通过对每个 source/destination 列使用 IF() 来屏蔽名称和端口来完成您正在尝试做的事情。大多数时候人们会尝试做一些事情来防止空值并显示类似空字符串的东西......相反,你想要相反......如果 "port_fnn" 为空,你想要隐藏这些元素。
所以我为每一列都做了一个 IF(表达式,结果为真,结果为假)。因此,如果 port_fnn 为 NULL,则结果显示为 null,否则 return 无论列是什么(系统名称、插槽、端口等)
SELECT
cc.id AS cc_id,
short_name,
source_fnn,
dest_fnn,
service_type,ladder_side,
if( src.port_fnn = '', NULL, src.system_name ) AS src_system_name,
if( src.port_fnn = '', NULL, src.slot_no ) AS src_slot_no,
if( src.port_fnn = '', NULL, src.port_no ) AS src_port_no,
if( src.port_fnn = '', NULL, src.port_fnn ) AS src_port_fnn,
if( dst.port_fnn = '', NULL, dst.system_name ) AS dst_system_name,
if( dst.port_fnn = '', NULL, dst.slot_no ) AS dst_slot_no,
if( dst.port_fnn = '', NULL, dst.port_no ) AS dst_port_no,
if( dst.port_fnn = '', NULL, dst.port_fnn ) AS dst_port_fnn
FROM
cust_connections cc
LEFT JOIN cust_port src
on cc.source_fnn = src.port_fnn
LEFT JOIN cust_port dst
on cc.dest_fnn = dst.port_fnn
我调整了上面的查询以处理您的数据...NULL 不同于空字符串。我将您的表和示例数据复制到 SQL Fiddle,然后是上述查询。它似乎分别为源和目标显示 NULLS。
''(空字符串)不为 NULL。 (哪个 sqlfiddle 输出为“(null)”。)
在文本中,不要像您最初在问题中那样写 "NULL" 或“
'' = '' 但 NULL <> NULL。因此,当相等性测试涉及 '' 时,您的 LEFT JOIN 会在列 cc.source_fnn 和 src.port_fnn 之间以及列 cc.dest_fnn 和 cust_port port_fnn 之间找到匹配项。但是您不希望 LEFT JOIN 匹配这些行。
你可以这样说:
声明所有 _fnn 列可为空,即声明为 NULL(默认)而不是 NOT NULL,并在您现在使用 ''(空字符串)的表中使用 NULL。那么您的查询将给出正确答案!
要求 port_fnn <> '':
FROM cust_connections cc LEFT JOIN cust_port src ON cc.source_fnn=src.port_fnn AND cc.source_fnn <> '' LEFT JOIN cust_port dst ON cc.dest_fnn=dst.port_fnn AND cc.dest_fnn <> '';
在 LEFT JOIN 之前从 cust_port 中删除这些行:
FROM cust_connections cc LEFT JOIN (SELECT * FROM cust_port WHERE port_fnn <> '' ) src ON cc.source_fnn=src.port_fnn LEFT JOIN (SELECT * FROM cust_port WHERE port_fnn <> '' ) dst ON cc.dest_fnn=dst.port_fnn;
SQLfiddle for 1 using NULL and for 2 & 3 using ''。这些添加了第二个 cust_port 行,缺少 port_fnn 以表明以上给出了正确的结果。您与 '' 一起使用的查询错误地从中生成了其他虚假行。
如果您希望 LEFT JOIN 中的 NULL 在输出中显示为空字符串,那么您可以对这些列使用 IFNULL:
IFNULL(dst.port_fnn,'') AS dst_port_fnn