neo4j - Cypher 中的数据透视表类型的查询(一次通过)
问题描述
我试图一次性执行以下查询,但我得出结论,这是不可能的,而且会导致某种形式的“嵌套”结构,这在性能方面绝不是好消息。
但是,我可能在这里遗漏了一些东西,所以我想我可能会问。
底层数据结构是两个实体之间的多对多关系A<---0:*--->B
最终目标是获得在特定时间间隔内实体B
对象分配给实体对象的次数占总分配次数的百分比。A
正是问题的后半部分引起了头痛。
实体A
包含一个item_date
字段 实体B
包含一个item_category
字段。
结果的表示可以扩展到一个表,其列是不同的item_date
,行是不同的item_category
标准化计数。我只是为了清楚起见而提到这一点,查询不必以这种确切的形式返回结果。
我的尝试:
with 12*30*24*3600 as window_length, "1980-1-1" as start_date,
"1985-12-31" as end_date
unwind range(apoc.date.parse(start_date,"s","yyyy-MM-dd"),apoc.date.parse(end_date,"s","yyyy-MM-dd"),window_length) as date_step
match (a:A)<-[r:RELATOB]-(b:B)
where apoc.date.parse(a.item_date,"s","yyyy-MM-dd")>=date_step and apoc.date.parse(a.item_date,"s","yyyy-MM-dd")<(date_step+window_length)
with window_length, date_step, count(r) as total_count unwind ["code_A", "code_B", "code_C"] as the_code [MATCH THE PATTERN AGAIN TO COUNT SPECIFIC `item_code` this time.
我发现很难一次性表达这一点,因为它需要在GROUP BY
图形模式的定义之后相当于两个独立的类似子句。你不能同时表达这两个,所以你必须解开它们。我担心这会导致两种评估:一种用于总计数,另一种用于部分计数。我试图优化的一点是重写查询的某种方式,这样它就不必计算它之前“捕获”的节点,但这对于聚合函数应用于集合的隐含方式非常困难。
基本上,任何不是聚合函数的属性都会成为分层变量。我必须在这里说一个简单的双重分层(“抓住所有东西,通过item_date
产生另一个级别的计数来产生一个级别的计数item_code
)对我不起作用,因为没有办法控制.的宽度window_length
。这意味着我无法在具有不同分配率的item_code
s 的两个时间段之间进行比较,因为时间段不相等:(
请注意,在一段时间内(在密码外部)检索这些item_code
特定代码的总和然后对其进行归一化不会导致准确的百分比,因为归一化将针对特定子集而不是总数.item_code
有没有办法r
在一个时间段内执行同时计数,然后(以某种方式)重新使用已经匹配a
的b
节点子集来评估那些特定的部分b
计数(b:{item_code:the_code})-[r2:RELATOB]-(a) where a.item_date...
?
如果不是,那么我将转到下一个最快的事情,即执行两个独立的查询(一个用于总计数,一个用于部分),然后在外部进行除法:/。
解决方案
Tomaz Bratanic 在评论中提出的解决方案(我认为)是这样的:
with 1*30*24*3600 as window_length,
"1980-01-01" as start_date,
"1985-12-31" as end_date
unwind range(apoc.date.parse(start_date,"s","yyyy-MM-dd"),apoc.date.parse(end_date,"s","yyyy-MM-dd"),window_length) as date_step
unwind ["code_A","code_B","code_c"] as the_code
match (a:A)<-[r:RELATOB]-(b:B)
where apoc.date.parse(a.item_date,"s","yyyy-MM-dd")>=date_step and apoc.date.parse(a.item_category,"s","yyyy-MM-dd")<(date_step+window_length)
return the_code, date_step, tofloat(sum(case when b.item_category=code then 1 else 0 end)/count(r)) as perc_count order by date_step asc
这个:
- 正在工作中
- 它完全符合我的要求(经过一些小的修改)
- 它甚至添加了用零填充缺失值,因为
ELSE 0
即使不存在计数数据也会有效地强制为零。
但在现实条件下,它至少比我目前使用的重新匹配慢 30 秒(不,不是,请参阅编辑)。(不,这不是因为在填充缺失数据时现在返回的额外数据,这是原始查询时间)。
我认为可能值得在此处附加查询计划:
这是两次应用相同模式但快速的方法的计划:
这是一次执行计数的计划,但执行速度很慢:
稍后我可能会看到时间如何随输入中的数据缩放,也许两者以不同的速率缩放,但在这一点上,“单程”似乎已经比“双程”慢,坦率地说,我无法看到它如何通过更多数据变得更快。这已经是分布在 18k 个项目(大约)中的 3 个类别的 12 个月的简单计数。
希望这对其他人也有帮助。
编辑:
虽然我最初是这样做的,但还有另一个修改,我没有包括比赛后第二次放松的位置。这将“双重匹配”以下的时间缩短了 20 秒,因为展开会影响返回,而不是同一查询的多次执行,现在变成:
with 1*30*24*3600 as window_length,
"1980-01-01" as start_date,
"1985-12-31" as end_date
unwind range(apoc.date.parse(start_date,"s","yyyy-MM-dd"),apoc.date.parse(end_date,"s","yyyy-MM-dd"),window_length) as date_step
match (a:A)<-[r:RELATOB]-(b:B)
where apoc.date.parse(a.item_date,"s","yyyy-MM-dd")>=date_step and apoc.date.parse(a.item_category,"s","yyyy-MM-dd")<(date_step+window_length)
unwind ["code_A","code_B","code_c"] as the_code
return the_code, date_step, tofloat(sum(case when b.item_category=code then 1 else 0 end)/count(r)) as perc_count order by date_step asc
这也是它的执行计划:
原始双人比赛大约 55790 毫秒,一次完成(比赛前都展开)82306 毫秒,一次完成(比赛后第二次展开)23461 毫秒。
推荐阅读
- php - 根据 Woocommerce 中的付款类型将新订单电子邮件发送到其他电子邮件
- android - Android 原生 - jni 编写的原生代码如何在 root 权限下运行?
- angular - ng 添加 @angular/pwa 时出错
- c# - 使用二维数据和 MVVM 绑定到 DataGrid
- python - 与 tensorflow-gpu 1.4 一起使用的 keras 版本
- c# - 如何在 ASP.NET Core 集成测试中覆盖来自其他容器的 DI 注册
- ios - Swift - CollectionViewCell 边框错误或不完整
- azure-devops - VSTS Windows 机器文件复制 robocopy 未找到
- sql - 如何获取列值的总和作为 SQL 结果集中的新列
- javascript - 如何更改猫头鹰轮播中的视频背景图像?