Wikidata SPARQL查询人物altNames因P69教育经历产生重复结果问题
问题根因
这是SPARQL查询执行时的**交叉连接(笛卡尔积)**逻辑导致的,和当前查询块结构直接相关:
- 你在WHERE子句的同一层级,并列写了三个独立的匹配块:匹配英文别名的块、匹配人物单值基础属性(性别/生日/出生地/头像)的块、匹配P69教育经历的块。
- SPARQL在匹配同一实体下的多组独立属性时,会对不同块返回的结果做笛卡尔积计算:如果某个人物有A个英文别名、B条教育经历记录,最终会生成A*B条中间结果行。
- 你给教育经历的
GROUP_CONCAT加了DISTINCT去重,所以教育经历字段不会出现重复;但别名的聚合没有加去重逻辑,所有中间行里的别名都会被直接拼接,自然就会出现「有几条教育经历,别名列表就重复几次」的现象——比如比尔·盖茨有3条P69教育经历记录,每个别名都会在中间结果里出现3次,最终拼接出的别名列表就会把每个别名重复3遍。
修复方法
两种方案按需选择即可:
- 快速修复:直接给别名的聚合加上DISTINCT去重,把
GROUP_CONCAT(?altNames; SEPARATOR = ";") AS ?aliases修改为GROUP_CONCAT(DISTINCT ?altNames; SEPARATOR = ";") AS ?aliases,聚合时自动过滤重复的别名值。 - 性能更优的规范写法:把所有多值属性的查询拆分为独立子查询,先完成单属性的聚合,再和主查询的单值属性做连接,从根源上避免无意义的笛卡尔积生成冗余中间结果。核心修改后的查询结构如下:
SELECT ?item ?itemLabel ?itemDesc ?genderLabel ?birthday ?placeOfBirthLabel ?image ?aliases ?edus WHERE { VALUES ?item { wd:Q5284 wd:Q28920824 wd:Q93592590 wd:Q109596276 } # 单值基础属性直接匹配 OPTIONAL { ?item wdt:P21 ?gender. } OPTIONAL { ?item wdt:P569 ?birthday. } OPTIONAL { ?item wdt:P19 ?placeOfBirth. } OPTIONAL { ?item wdt:P18 ?image. } SERVICE wikibase:label { bd:serviceParam wikibase:language "en". ?item schema:description ?itemDesc. } # 别名单独子查询聚合 OPTIONAL { SELECT ?item (GROUP_CONCAT(?altName; SEPARATOR=";") AS ?aliases) WHERE { ?item skos:altLabel ?altName. FILTER(LANG(?altName) = "en") } GROUP BY ?item } # 教育经历单独子查询聚合 OPTIONAL { SELECT ?item (GROUP_CONCAT(?ed; SEPARATOR="|") AS ?edus) WHERE { ?item p:P69 ?statement. ?statement (ps:P69/rdfs:label) ?eduLabel. FILTER(LANG(?eduLabel) = "en") OPTIONAL { ?statement pq:P580 ?edStart. } OPTIONAL { ?statement pq:P582 ?edEnd. } OPTIONAL { ?statement (pq:P512/rdfs:label) ?edDegrees. FILTER(LANG(?edDegrees) = "en") } OPTIONAL { ?statement (pq:P812/rdfs:label) ?edMajors. FILTER(LANG(?edMajors) = "en") } BIND(IF(BOUND(?edStart), CONCAT("::start:", STR(YEAR(?edStart))), "") AS ?edStartText) BIND(IF(BOUND(?edEnd), CONCAT("::end:", STR(YEAR(?edEnd))), "") AS ?edEndText) BIND(IF(BOUND(?edDegrees), CONCAT("::degrees:", STR(?edDegrees)), "") AS ?edDegreeText) BIND(IF(BOUND(?edMajors), CONCAT("::majors:", STR(?edMajors)), "") AS ?edMajorText) BIND(CONCAT(?eduLabel, ?edStartText, ?edEndText, ?edDegreeText, ?edMajorText) AS ?ed) } GROUP BY ?item } }
子查询写法在实体的多值属性数量较多时,性能提升会非常明显,也能避免后续新增其他多值属性查询时再次出现同类重复问题。
内容的提问来源于stack exchange,提问作者Korimako
相关产品推荐
相关产品推荐

