如何将SPARQL“ MINUS”用作SQL“ NOT IN”,以等同于让所有只在一个团队中比赛的球员

时间:2018-07-12 21:23:25

标签: sparql rdf

我正在对Lahman棒球数据库中的数据进行sparql查询。 这是一些示例数据,展示了我的查询需要做什么。

@prefix ma: <http://mydataset.com/ns/master#> .
ma:billybo01 ma:nameFirst "Billy" .
ma:billybo01 ma:nameLast "Bored" .
ma:chrisgow01 ma:nameFirst "Chris" .
ma:chrisgow01 ma:nameLast "Gowan" .
ma:bradlee01 ma:nameFirst "Brad" .
ma:bradlee01 ma:nameLast "Lee" .


@prefix teamQ2: <http://mydataset.com/ns/teamQ2#> .
@prefix yearQ2: <http://mydataset.com/ns/yearQ2#> .
@prefix ma: <http://mydataset.com/ns/master#> .
teamQ2:BS1 yearQ2:1871 ma:billybo01 .
teamQ2:BS1 yearQ2:1872 ma:billybo01 .
teamQ2:BS1 yearQ2:1873 ma:billybo01 .
teamQ2:LAN yearQ2:1874 ma:billybo01 .

teamQ2:LAN yearQ2:1871 ma:chrisgow01 .
teamQ2:LAN yearQ2:1872 ma:chrisgow01 .

teamQ2:BS1 yearQ2:1871 ma:bradlee01 .
teamQ2:BS1 yearQ2:1872 ma:bradlee01 .

我正在尝试获取仅为LAN团队效力而没有其他团队效力的所有球员的姓氏和名字。我的尝试如下所示。我希望查询任何一年在团队LAN上的所有球员,获取他们的masterID,然后从该团队中出现在LAN以外的团队中的所有球员中减去。然后,我将masterId与末尾的名字和姓氏进行匹配。现在,它正在返回数据,就像MINUSFILTER !EXISTS互相抵消一样,它只返回在LAN上玩过的所有玩家。除了MINUSFILTER !EXISTS以外,我还需要使用其他东西吗?

PREFIX ma: <http://mydataset.com/ns/master#>
PREFIX teamQ2: <http://mydataset.com/ns/teamQ2#> 
SELECT DISTINCT ?nameFirst ?nameLast
WHERE
{
  teamQ2:LAN ?yearID ?masterID .
  MINUS{FILTER (
      !EXISTS {
          teamQ2:LAN ?yearID ?nonLANmasterID .
      }
      )}
  ?masterID ma:nameLast ?nameLast .
  ?masterID ma:nameFirst ?nameFirst .
}
ORDER BY ?nameLast ?nameFirst

2 个答案:

答案 0 :(得分:1)

建模是非常不寻常的,但这是根据您的描述进行的查询的概要:

  1. 找到所有teamQ2:LAN ?p ?o .三元组。

  2. 在属性对象对(FILTER NOT EXISTS)的{​​{1}}主题不同的情况下拒绝(FILTER(?otherteam != teamQ2:LAN )

    (这实际上是“在同一年没有参加同一支球队,因为?p ?o?p,包括年份在内。)

PREFIX ma: 
PREFIX teamQ2:  
SELECT *
WHERE
{
  teamQ2:LAN ?p ?masterID .
  FILTER NOT EXISTS {
      ?otherteam ?p ?masterID .
      FILTER(?otherteam !=  teamQ2:LAN )
  }
  ?masterID ma:nameLast ?nameLast .
  ?masterID ma:nameFirst ?nameFirst .
}

在这种情况下,也可以通过以下方式完成:

  1. 找到yearQ2:1871的玩家。
  2. 查找不属于teamQ2:LAN的玩家。
  3. teamQ2:LAN在上述MINUS上。
PREFIX ma: 
PREFIX teamQ2:  
SELECT *
WHERE
{
  teamQ2:LAN ?yearID ?masterID .
  MINUS {
      ?otherteam ?yearID ?masterID .
      FILTER(?otherteam !=  teamQ2:LAN )
  }
  ?masterID ma:nameLast ?nameLast .
  ?masterID ma:nameFirst ?nameFirst .
}

我认为您应该考虑将数据记录为时间事件:

以“年”或三元组的形式“为团队效力”:

ma:bradlee01 :playedForInYear [ :team teamQ2:BS1 ; :year 1871 ].
ma:bradlee01 :playedForInYear [ :team teamQ2:BS1 ; :year 1872 ].

答案 1 :(得分:1)

在您的示例中,我假设您想要返回的答案是“克里斯·高万”,因为他是那里唯一为LAN效力的球员,而不是为其他任何团队效力的人,对吗?

解决它的方法是逐步构建它。首先,进行查询以获取参与LAN的所有人:

SELECT DISTINCT ?firstName ?lastName
WHERE { 
         teamQ2:LAN ?yearId ?player .
         ?player ma:nameLast ?lastName;
                 ma:nameFirst ?firstName .
}

结果将是:

Evaluating SPARQL query...
+-------------------------------------+-------------------------------------+
| firstName                           | lastName                            |
+-------------------------------------+-------------------------------------+
| "Billy"                             | "Bored"                             |
| "Chris"                             | "Gowan"                             |
+-------------------------------------+-------------------------------------+
2 result(s) (75 ms)

现在,我们要添加一个过滤器,以删除在另一年为另一支球队效力的球员。如果三人组的对象是局域网(LAN)以外的团队,则该玩家为另一支球队效力。

我将使用MINUS,因为这就是您的要求。我还经常发现FILTER NOT EXISTS可以更直观地使用,但是它们大致相同。因此,我们需要的MINUS条件应该看起来像这样:

 MINUS { ?otherTeam ?otherYearId ?player }

但这还不够,因为我们需要限制?otherTeam?otherYearId的允许值(否则此条件将与所有条件匹配)。

坦率地说,正确执行此操作有些棘手,因为数据模型太差了。例如,如果所有团队资源都是rdf:type teamQ2:Team,那就容易得多了,更不用说使用年份作为团队和球员之间关系的属性名称了这不是建模的好方法。但是我离题了。

我们将使用较差的版本来标识团队:我们假设以teamQ2前缀开头的每个资源都是团队标识符。因此,我们想减去玩家所参加的所有团队,其中主题从teamQ2开始,但不是LAN结尾:

 MINUS { ?otherTeam ?otherYearId ?player . 
         FILTER(STRSTARTS(STR(?otherTeam), STR(teamQ2:)) 
         FILTER(!STRENDS(STR(?otherTeam), "LAN"))
 }

这使它成为完整的查询:

SELECT DISTINCT ?firstName ?lastName
WHERE { 
         teamQ2:LAN ?yearId ?player .
         ?player ma:nameLast ?lastName;
                 ma:nameFirst ?firstName .
         MINUS { ?otherTeam ?otherYearId ?player . 
                  FILTER(STRSTARTS(STR(?otherTeam), STR(teamQ2:))) 
                  FILTER(!STRENDS(STR(?otherTeam), "LAN"))
         }
}

结果:

Evaluating SPARQL query...
+-------------------------------------+-------------------------------------+
| firstName                           | lastName                            |
+-------------------------------------+-------------------------------------+
| "Chris"                             | "Gowan"                             |
+-------------------------------------+-------------------------------------+
1 result(s) (2 ms)