查询结果不一致,具体取决于Neo4j中查询的顺序

时间:2019-06-12 08:24:44

标签: neo4j cypher match with-statement

[图片1 ][1]

根据查询2个关系的顺序,尽管查询相同(据我了解),我还是会得到2个不同的答案。该查询显然是不同的,但我不知道为什么。

MATCH p1=(:Barrier {code: 'B2'})-[:REL1]->()
WITH count(DISTINCT p1) AS failed_B2
MATCH p2=(:Barrier {code: 'B2'})-[:REL2]->()
RETURN count(DISTINCT p2) AS worked_B2, failed_B2

返回1和0-正确

但反过来:

MATCH p1=(:Barrier {code: 'B2'})-[:REL2]->()
WITH count(DISTINCT p1) AS failed_B2
MATCH p2=(:Barrier {code: 'B2'})-[:REL1]->()
RETURN count(DISTINCT p2) AS worked_B2, failed_B2

返回0和0-这是不正确的

我想合并多个查询的结果,但是UNION无法正常工作,因为它需要将结果分组在同一列下,在我看来这是不正确的。我需要将结果放在不同的列中。

1 个答案:

答案 0 :(得分:0)

所以这是一件有趣的事情,它围绕着当行被过滤掉(例如当MATCH失败或WHERE条件过滤行)时发生的事情。

但是首先我们需要解决您在第二种情况下观察到的问题:Returns 0 and 0。我不认为这是真的,我想知道您在这里使用的是什么版本的Neo4j。在这种情况下,我本来希望没有返回任何行,而这与返回两者都具有0值的行完全不同。

执行Cypher查询时,它们会建立数据记录(或行)。 Cypher操作每行执行一次。因此,当您在查询中的某个点执行MATCH时,将按行执行,而当MATCH失败时,如果不存在这种模式(该模式遵循WHERE子句,如果存在的话),则会将该行过滤掉。这很重要,因为这意味着该记录中的所有其他数据都已消失并且不再可寻址。

要记住的第二件事是,即使没有行存在,我们也允许某些聚合,例如count()collect()执行 ,因为可以想到您可能有一个查询,没有任何匹配项,并且将该计数设为0(或在收集时为空集合)是完全有效的情况,应该允许。在这些情况下,在MATCH或过滤器之后可能根本没有剩余的行(并且由于没有行,所以将无法执行其他任何操作,因为Cypher操作每行执行一次,因此如果有,则没有操作)没有行),则count()collect()会导致出现一个新的行,其计数为0或为空集合。并且由于现在存在一行,因此查询中其余的运算符可以执行一些操作,并且查询可以继续执行。

这是在您的第一种情况下发生的情况,其中模式p1不存在,而模式p2存在(一次)。这是发生的情况的细分:

  1. 第一个匹配项找不到任何内容。行数变为0。没有任何执行后续操作的对象。
  2. 您执行独立的count()聚合(作用域中没有其他变量,这很重要)。这会发出一个单行,计数为0,这是正确的:在图形中没有该模式的出现。
  3. 您执行了第二个匹配,并执行了一条记录/行(值为{failed_B2:0}),它找到了单个匹配项,并获得了计数(1),并且能够输出期望的答案(1、0),其中1是查询末尾的模式匹配数p2,而0是查询的前两行的模式匹配数p1 。

现在让我们看看当我们扭转这种情况时会发生什么。

在第二个查询中,现在模式p1在图中存在一次,而模式p2不存在。这是发生的故障:

  1. 第一个MATCH成功并找到了模式。
  2. 您将获得找到的模式的数量:1.现在,您有一个记录/行,其值为{failed_B2:1}
  3. 您执行第二个MATCH,但找不到该模式。记录/行被过滤掉。您现在没有记录/行,因此不仅没有任何要操作的内容,而且以前在记录/行中的所有内容也都消失了。没有failed_B2值可供参考。
  4. 您尝试获取p2failed_B2的计数。但这是Cypher所不允许的,当它是独立的count()或collect()时,我们只允许跨0行聚合,没有failed_B2引用,当包含的记录/行被擦除时它被过滤掉了。无法合理地进行处理,因为以前不存在现有数据(这是正确的行为)。查询应该不返回任何行...与0, 0不同,因为这意味着您返回了一行(这就是为什么我有兴趣用您)。

关于应该如何正确执行此操作,当您必须像这样进行聚合并且知道某些模式可能不存在时,请改用OPTIONAL MATCH。

当您选择匹配时,如果找不到匹配项,则不会过滤掉该行。相反,模式中新引入的变量将变为null,并且当您对null进行count()或collect()时,它将忽略它们,从而使您的计数正确为0,但不会清除包含{{1的记录/行}}值,您也想在最后返回。