我是使用3.0版的neo4j新手。我有一个庞大的事务数据集,我转换为图形模型。我需要将下面的SQL查询转换为cypher。
create table calc_base as
select a.ticket_id ticket_id, b.product_id, b.product_desc,
a.promotion_flag promo_flag,
sum(quantity) sum_units,
sum(sales) sum_sales
from fact a
inner join dimproduct b on a.product_id = b.product_id
where store_id in (select store_id from dimstore)
and b.product_id in (select product_id from fact group by 1 order by count(distinct ticket_id) desc limit 5000)
group by 1,2,3,4;
这是我的ER diagram和相应的graph model。我对此查询的关系是:
MATCH (a:PRODUCT)
MATCH (b:FACT {PRODUCT_ID: a.PRODUCT_ID})
CREATE (b)-[:HAS_PRODUCT]->(a);
MATCH (a:STORE)
MATCH (b:FACT {STORE_ID: a.STORE_ID})
CREATE (b)-[:HAS_STORE]->(a);
此查询的我的密码翻译是:
PROFILE
MATCH (b:PRODUCT)
MATCH (a:FACT)
MATCH (c:STORE)
CREATE (d:CALC_BASE {TICKET_ID: a.TICKET_ID, PRODUCT_ID: a.PRODUCT_ID, PRODUCT_DESC: b.PRODUCT_DESC,
PROMO_FLAG: a.PROMOTION_FLAG, KPI_UNITS: SUM(a.QUANTITY_ABS), KPI_SALES: SUM(a.SALES_ABS) })
Q = (MATCH (e:FACT)
WITH count(PRODUCT_ID) AS PRO_ID_NUM , COUNT(DISTINCT TICKET_ID) AS TICKET_ID_NUM
ORDER BY TICKET_ID_NUM DESC)
WHERE b.PRODUCT_ID = Q
ORDER BY TICKET_ID, PRODUCT_ID, PRODUCT_DESC, PROMO_FLAG
我的主要问题是在cypher中定义group by
和子查询。
如何以最佳方式将此查询写入cypher?
答案 0 :(得分:1)
例如,Cypher中没有GROUP BY,因为分组列隐含地是每行中的非聚合列。
我假设你有约束和索引设置?对于高性能查询,您需要正确设置这些设置。
我看到的一个主要的红旗是这些查询中根本没有关系,可能在整个数据模型中。图形数据库用于建模事物之间的关系,这些数据库倾向于取代关系数据库中外键的概念。我将详细介绍最后建立数据建模的方法。
那就是说,我会用你当前的数据模型进行翻译。
我的方法是从内到外。首先让我们获取允许的store_id和b.product_id值的集合。
// first collect allowed STORE_IDs
MATCH (s:STORE)
WITH COLLECT(s.STORE_ID) as STORE_IDs
MATCH (e:FACT)
// now get PRODUCT_IDs with the most associated TICKET_IDs
WITH STORE_IDs, e.PRODUCT_ID, COUNT(DISTICT e.TICKET_ID) as TICKET_ID_CNT
ORDER BY TICKET_ID_CNT DESC
LIMIT 5000
WITH STORE_IDs, COLLECT(e.PRODUCT_ID) as PRODUCT_IDs
// we now have 1 row with both collections, and will do membership checking with them later
// next get only PRODUCT nodes with PRODUCT_ID in the collection of allowed PRODUCT_IDs
MATCH (b:PRODUCT)
WHERE b.PRODUCT_ID in PRODUCT_IDs
WITH b, STORE_IDs
// now get FACT nodes with STORE_ID in the collection of allowed STORE_IDs
// and associated with PRODUCT nodes by PRODUCT_ID
MATCH (a:FACT)
WHERE a.STORE_ID in STORE_IDs
AND a.PRODUCT_ID = b.PRODUCT_ID
WITH a, b
// grouping is implicit, the non-aggregation columns are the grouping key
WITH a.TICKET_ID as TICKET_ID, b.PRODUCT_ID as PRODUCT_ID, b.PRODUCT_DESC as PRODUCT_DESC, a.PROMOTION_FLAG as PROMOTION_FLAG, SUM(a.QUANTITY) as SUM_UNITS, SUM(a.SALES) as SUM_SALES
CREATE (:CALC_BASE {TICKET_ID:TICKET_ID, PRODUCT_ID:PRODUCT_ID, PRODUCT_DESC:PRODUCT_DESC, PROMO_FLAG:PROMOTION_FLAG, SUM_UNITS:SUM_UNITS, SUM_SALES:SUM_SALES})
那应该能得到你想要的东西。
现在回到所有这一切的主要问题......你正在使用图形数据库来处理非图形数据和查询。您正在使用外键并尝试加入节点,而不是将这些建模为关系。您还使用缩写名称,这使得很难弄清楚数据的含义以及它们应该如何相互关联。
我的建议是重新考虑您的数据模型,尤其是数据如何连接在一起。查找您正在使用外键加入的位置,而是考虑如何用节点之间的关系替换它,以及这些关系的性质。
以更加面向图形的方式建模的数据与关系有助于更多面向图形和高性能的查询,以及更容易理解和与他人沟通的数据模型。
修改
既然你有不同类型的节点之间的关系,我们可以稍微简化一下。
方法类似,我们仍然会从内到外而不是一些内部子查询(尽管使用Neo4j 3.1,pattern comprehension可以在各种情况下像内部查询一样使用)。
// first get products with the most tickets (top 5k)
MATCH (f:FACT)
WITH f.PRODUCT_ID as productID, COUNT(DISTICT f.TICKET_ID) as ticketIDCnt
ORDER BY ticketIDCnt DESC
LIMIT 5000
MATCH (p:PRODUCT)
WHERE p.PRODUCT_ID = productID
WITH p
// with those products, get related facts (graph equivalent of a join)
MATCH (p)<-[:HAS_PRODUCT]-(f:FACT)
// ensure the fact has a related store.
// if ALL facts have a related store, you don't need this WHERE clause
WHERE (f)-[:HAS_STORE]->(:STORE)
WITH f.TICKET_ID as TICKET_ID, p.PRODUCT_ID as PRODUCT_ID, p.PRODUCT_DESC as PRODUCT_DESC, f.PROMOTION_FLAG as PROMOTION_FLAG, SUM(f.QUANTITY) as SUM_UNITS, SUM(f.SALES) as SUM_SALES
CREATE (:CALC_BASE {TICKET_ID:TICKET_ID, PRODUCT_ID:PRODUCT_ID, PRODUCT_DESC:PRODUCT_DESC, PROMO_FLAG:PROMOTION_FLAG, SUM_UNITS:SUM_UNITS, SUM_SALES:SUM_SALES})
同样,您需要确保数据模型中存在适当的索引和唯一约束,以加快匹配速度。
您可能还需要考虑几个方面来修改数据模型(当然,这是有意义的)。有一个票证ID的概念,但没有:票证节点。您已创建:CALC_BASE节点,但尚未将它们与以下内容相关联:产品或票证。一般来说,看看你还在使用外键的概念,并看看将它们建模为与其他节点的关系是否更好是有用的。
再次在GROUP BY上,这是在Cypher处理的。您的行由非聚合列和聚合列组成。 Cypher将非聚合列自动用作分组键(相当于按列分组)。由于SUM_UNITS和SUM_SALES是SUM()操作(聚合函数)的结果,因此所有其他列都将自动用作分组键。