{
    "componentChunkName": "component---src-templates-post-js",
    "path": "/whatisspaeql/",
    "result": {"data":{"ghostPost":{"id":"Ghost__Post__6a8a99a7781e37000184ef29","title":"SPARQLって結局何なの？実際にRDFデータに問い合わせしてみて勉強しよう！","slug":"whatisspaeql","featured":false,"feature_image":null,"excerpt":"\n0. この記事の目的\n「SPARQL」に関して書いてある記事等には、こういうコードが、特に説明もなく登場したりしますよね。\n\nPREFIX saas: <https://example.com/ontology/saas#>\n\nSELECT ?workspace ?workspaceName\nWHERE {\n    <https://example.com/data/tenant-abc>\n        saas:containsWorkspace ?workspace .\n\n    ?workspace saas:name ?workspaceName .\n}\n\n\nPREFIX、SELECT、WHERE、?workspace。見た感じSQLに似ている気もしますが、確信が持てないまま読み進めるのは、なかなかつらいものです。\n\nこの記事は、その手前の部分を埋めるための記事です。SPARQLだけに絞って、それが何であり、どうやって実際に手元で動かすのかを、説明します！\n\n※この記事は、RDF・RDFS・OWL・Turtleについて説明済みの前提で書きます。まだの方は、先に\nRDF・RD","custom_excerpt":null,"visibility":"public","created_at_pretty":"23 August, 2026","published_at_pretty":"24 August, 2026","updated_at_pretty":"24 August, 2026","created_at":"2026-08-23T15:56:39.000+09:00","published_at":"2026-08-24T17:29:11.000+09:00","updated_at":"2026-08-24T17:34:16.000+09:00","meta_title":null,"meta_description":null,"og_description":null,"og_image":null,"og_title":null,"twitter_description":null,"twitter_image":null,"twitter_title":null,"authors":[{"name":"Akihiro YAGASAKI","slug":"akihiro","bio":null,"profile_image":"https://ghost.tech.anti-pattern.co.jp/content/images/2022/04/yagasaki--2-.jpeg","twitter":null,"facebook":null,"website":null}],"primary_author":{"name":"Akihiro YAGASAKI","slug":"akihiro","bio":null,"profile_image":"https://ghost.tech.anti-pattern.co.jp/content/images/2022/04/yagasaki--2-.jpeg","twitter":null,"facebook":null,"website":null},"primary_tag":{"name":"Ontology","slug":"ontology","description":null,"feature_image":null,"meta_description":null,"meta_title":null,"visibility":"public"},"tags":[{"name":"Ontology","slug":"ontology","description":null,"feature_image":null,"meta_description":null,"meta_title":null,"visibility":"public"}],"plaintext":"\n0. この記事の目的\n「SPARQL」に関して書いてある記事等には、こういうコードが、特に説明もなく登場したりしますよね。\n\nPREFIX saas: <https://example.com/ontology/saas#>\n\nSELECT ?workspace ?workspaceName\nWHERE {\n    <https://example.com/data/tenant-abc>\n        saas:containsWorkspace ?workspace .\n\n    ?workspace saas:name ?workspaceName .\n}\n\n\nPREFIX、SELECT、WHERE、?workspace。見た感じSQLに似ている気もしますが、確信が持てないまま読み進めるのは、なかなかつらいものです。\n\nこの記事は、その手前の部分を埋めるための記事です。SPARQLだけに絞って、それが何であり、どうやって実際に手元で動かすのかを、説明します！\n\n※この記事は、RDF・RDFS・OWL・Turtleについて説明済みの前提で書きます。まだの方は、先に\nRDF・RDFS・OWL・Turtleの違いを整理して、ファイルで理解するオントロジー入門\n[https://tech.anti-pattern.co.jp/rdfrdfsowlturtlenowei-iwozheng-li-site-huairudeli-jie-suruontoroziru-men/]\nを読んでおいてください。\n\n\n--------------------------------------------------------------------------------\n\n1. SPARQLとは何か\n一言でいうと、SPARQLは次のようなものです。\n\nSPARQL：RDFグラフに問い合わせるための検索言語\n\n\n正式名称はSPARQL Protocol and RDF Query Languageです。読み方は「スパークル」です。\n\nプログラミングやデータベースに触れたことがある方には、次のたとえが分かりやすいと思います。\n\nSQL    ：テーブルに対して問い合わせる\nSPARQL ：RDFグラフに対して問い合わせる\n\n\nRDF・RDFS・OWL・Turtleの違いを整理して、ファイルで理解するオントロジー入門\n[https://tech.anti-pattern.co.jp/rdfrdfsowlturtlenowei-iwozheng-li-site-huairudeli-jie-suruontoroziru-men/]\nで説明した技術を、もう一度並べてみます。\n\nRDF\n    情報をトリプルとして表す\n\nRDFS・OWL\n    データが何を意味するかを定義する\n\nSHACL\n    データが期待する形になっているかを検証する\n\nSPARQL\n    RDFグラフから、必要な情報を取り出す\n\n\nRDFで情報を表し、RDFS・OWLで意味を定義し、SHACLで形を検証し、SPARQLで情報を取り出す。この記事は、最後のSPARQLの部分を担当します。\n\n\n--------------------------------------------------------------------------------\n\n2. SPARQLも、Turtleに似た書き方をする\nSPARQLのクエリは、Turtleのトリプルパターンとよく似た見た目をしています。これは偶然ではなく、意図的にそう設計されています。\n\nex:yamada ex:worksFor ex:abc .\n\n\nこれは、Turtleで書いた、確定した事実です。\n\n?employee ex:worksFor ex:abc .\n\n\nこれは、SPARQLで書いた、問い合わせの条件です。\n\n違いは、主語の部分がex:yamadaという具体的な値ではなく、?employeeという変数になっている点だけです。\n\n実際のデータ\n    ex:yamada ex:worksFor ex:abc .\n\n問い合わせの条件\n    ?employee ex:worksFor ex:abc .\n\n\n?で始まる名前は変数です。「ここに当てはまる値を教えてほしい」という、いわば穴あき問題です。\n\n\n--------------------------------------------------------------------------------\n\n3. 最初のクエリを書く\nSPARQLのSELECTクエリは、次の3つの部分でできています。\n\nPREFIX ex: <https://example.com/company#>\n\nSELECT ?employee\nWHERE {\n    ?employee a ex:Employee .\n}\n\n\n順番に見ていきます。\n\nPREFIX ex: <https://example.com/company#>\n\n\nこれは、Turtleの@prefixと同じ役割です。ex:を、このIRIの省略形として使う、という宣言です。書き方が少し違うだけで(@が無い、末尾に.\nが無い)、意味はまったく同じです。\n\nSELECT ?employee\n\n\n「?employeeという変数の値を、結果として返してください」という指定です。\n\nWHERE {\n    ?employee a ex:Employee .\n}\n\n\n「?employeeが、ex:Employeeという型を持つ、というパターンに一致するものを探してください」という条件です。\n\naは、rdf:typeの省略記法です。これもTurtleと同じ書き方です。\n\nこのクエリは、日本語にすると次のような問い合わせです。\n\nEmployeeという型を持つものを、すべて教えてください\n\n\n\n--------------------------------------------------------------------------------\n\n4. データを用意する\n具体的な例で確認するために、次のデータを用意します。RDF・RDFS・OWL・Turtleの違いを整理して、ファイルで理解するオントロジー入門\n[https://tech.anti-pattern.co.jp/rdfrdfsowlturtlenowei-iwozheng-li-site-huairudeli-jie-suruontoroziru-men/]\nの山田・鈴木・ABC社に、もう1人、田中を加えます。\n\n# data.ttl\n@prefix ex: <https://example.com/company#> .\n\nex:yamada a ex:Employee ;\n    ex:employeeId \"E001\" ;\n    ex:worksFor ex:abc ;\n    ex:manages ex:suzuki .\n\nex:suzuki a ex:Employee ;\n    ex:employeeId \"E002\" ;\n    ex:worksFor ex:abc .\n\nex:tanaka a ex:Employee ;\n    ex:employeeId \"E003\" ;\n    ex:worksFor ex:abc .\n\nex:abc a ex:Organization .\n\n\n山田はABC社に勤務し、鈴木を管理しています。鈴木と田中も、同じくABC社に勤務しています。\n\n\n--------------------------------------------------------------------------------\n\n5. 具体例でクエリを書く\n5.1 山田の勤務先を取得する\nPREFIX ex: <https://example.com/company#>\n\nSELECT ?org\nWHERE {\n    ex:yamada ex:worksFor ?org .\n}\n\n\n主語(ex:yamada)と述語(ex:worksFor)を固定し、目的語だけを変数にしています。結果は、ex:abcです。\n\n5.2 ABC社に勤務する全員を取得する\n今度は逆に、主語を変数にします。\n\nPREFIX ex: <https://example.com/company#>\n\nSELECT ?employee\nWHERE {\n    ?employee ex:worksFor ex:abc .\n}\n\n\nこのクエリは、「ex:worksFor ex:abc\nという関係の、主語側に来るものをすべて教えてください」という意味です。データを逆方向にたどっていることになります。\n\nRDFのトリプルには、あらかじめ決まった「たどってよい方向」はありません。主語を固定して目的語を探すこともできますし、目的語を固定して主語を探すこともできます。\n\n5.3 複数の条件をつなげる\n山田が管理している人の、社員番号を取得したいとします。1つのトリプルパターンだけでは表せません。\n\nPREFIX ex: <https://example.com/company#>\n\nSELECT ?subordinateId\nWHERE {\n    ex:yamada ex:manages ?subordinate .\n    ?subordinate ex:employeeId ?subordinateId .\n}\n\n\nWHEREの中に、2つのトリプルパターンが並んでいます。\n\n1つ目：ex:yamada ex:manages ?subordinate .\n2つ目：?subordinate     ex:employeeId ?subordinateId .\n\n\n1つ目のパターンで?subordinate\nに当てはまった値が、2つ目のパターンの主語としてそのまま使われます。同じ変数名を使うことで、2つのパターンが連動します。\n\nSQLに慣れている方には、これがJOINに相当する、と考えると分かりやすいと思います。1つ目のパターンで?subordinateがex:suzuki\nに決まり、2つ目のパターンによってex:suzukiのemployeeIdである\"E002\"が引き出されます。\n\n\n--------------------------------------------------------------------------------\n\n6. 実際に問い合わせを実行してみる\nここまでは、クエリの書き方の説明でした。ここからは、実際にこのクエリを動かしてみます。\n\n6.1 準備\npip install rdflib 'pyparsing>=3.1'\n\n\nrdflibは、RDFをPythonで扱うための、最も基本的なライブラリです。これをインストールすると、sparqlquery\nというコマンドラインツールも一緒に使えるようになります。\n\n6.2 データとクエリをファイルに保存する\n4.のデータをdata.ttlに、5.1のクエリをquery.rqに保存します。\n\n# query.rq\nPREFIX ex: <https://example.com/company#>\n\nSELECT ?org\nWHERE {\n    ex:yamada ex:worksFor ?org .\n}\n\n\n6.3 実行する\n同じフォルダにdata.ttlとquery.rqを置いた状態で、次のコマンドを実行します。\n\nsparqlquery data.ttl -qf query.rq -f csv\n\n\n-qfのあとにクエリファイル、-f csvで結果をCSV形式(見やすい表形式)で出力するよう指定しています。\n\n結果は、次のように表示されます。\n\norg\nhttps://example.com/company#abc\n\n\n1行目が列名(SELECTで指定した変数名)、2行目以降が実際の値です。この出力は、実際にこの記事のとおりに実行して得られたものです。\n\n6.4 もう1つ試してみる\nquery.rqの内容を、5.2のクエリに書き換えて、もう一度実行してみます。\n\n# query.rq\nPREFIX ex: <https://example.com/company#>\n\nSELECT ?employee\nWHERE {\n    ?employee ex:worksFor ex:abc .\n}\nORDER BY ?employee\n\n\nsparqlquery data.ttl -qf query.rq -f csv\n\n\nemployee\nhttps://example.com/company#suzuki\nhttps://example.com/company#tanaka\nhttps://example.com/company#yamada\n\n\n鈴木・田中・山田の3人が、ABC社に勤務している全員として返ってきました。\n\n\n--------------------------------------------------------------------------------\n\n7. DISTINCTとORDER BY\nSPARQLのクエリには、DISTINCTやORDER BYもよく登場します。どちらも、SQLと同じ意味です。\n\nORDER BY ?変数\n    結果を、その変数の値で並べ替える\n\nDISTINCT\n    同じ組み合わせの結果が複数出たとき、重複を1つにまとめる\n\n\nORDER BYは、先ほどの6.4のクエリでも使いました。?employeeのIRI文字列の順番(アルファベット順)で並んでいたのは、ORDER BY\n?employeeを指定していたためです。\n\nDISTINCTは、次のように使います。\n\nPREFIX ex: <https://example.com/company#>\n\nSELECT DISTINCT ?org\nWHERE {\n    ?employee ex:worksFor ?org .\n}\n\n\nSELECTのすぐ後ろにDISTINCTを書きます。3人の社員が同じex:abcに勤務しているため、DISTINCTが無ければex:abc\nが3回、あれば1回だけ返ります。手元で両方を実行して、違いを確認してみてください。\n\n\n--------------------------------------------------------------------------------\n\n8. もう少し複雑な問い合わせについて\nここまでで説明したのは、SELECT・WHERE・トリプルパターンの組み合わせ・ORDER BY・DISTINCTという、SPARQLの中でも基本的な部分です。\n\nさらに発展的なクエリには、次のような機能も登場します。\n\nOPTIONAL\n    無くてもエラーにせず、あれば結果に含める\n\nFILTER\n    値を比較して、条件に合わないものを除外する\n\n\nたとえば、OPTIONALは次のように使います。\n\nSELECT ?employee ?org\nWHERE {\n    ?employee a ex:Employee .\n    OPTIONAL { ?employee ex:worksFor ?org . }\n}\n\n\nこれは、「Employeeはすべて返すが、worksForが書かれていない場合は、?orgを空(未束縛)のままにする」という意味です。OPTIONALを使わずに\n?employee ex:worksFor ?org .を普通に書くと、worksForが無いEmployeeはそもそも結果に出てきません。\n\nFILTERは、次のように使います。\n\nSELECT ?employee ?id\nWHERE {\n    ?employee ex:employeeId ?id .\n    FILTER (?id != \"E001\")\n}\n\n\nこれは、「社員番号が\"E001\"ではない人だけを返す」という意味です。\n\nこの記事では、OPTIONALとFILTERの説明までは踏み込みません。ここで大事なのは、次の1点だけです。\n\n基本のSELECT/WHEREだけでは、\n「無い場合の扱い」や「値による絞り込み」を\nうまく表現できない場面がある。\n\nそのために OPTIONAL と FILTER がある。\n\n\nこれだけ知っておけば、OPTIONALやFILTERが出てきても、「ああ、無い場合の扱いや絞り込みをしているんだな」と読めるはずです。\n\n\n--------------------------------------------------------------------------------\n\nまとめ\n最後に、この記事の内容を整理します。\n\nSPARQLとは\n    RDFグラフに問い合わせるための検索言語\n\nPREFIX\n    TurtleのPREFIXと同じ、IRIの省略形の宣言\n\nSELECT ?変数\n    結果として返す変数を指定する\n\nWHERE { トリプルパターン }\n    ?変数を含むトリプルパターンで、条件を書く\n\n同じ変数名でつなげる\n    複数のトリプルパターンを連動させる(SQLのJOINに近い)\n\nORDER BY / DISTINCT\n    結果の並べ替え / 重複の除去\n\nOPTIONAL / FILTER\n    より複雑な問い合わせのための機能(この記事では扱わない)\n\n\n実際に問い合わせを動かす手順は、次の3ステップでした。\n\n1. pip install rdflib 'pyparsing>=3.1'\n2. データを data.ttl に、クエリを query.rq に保存する\n3. sparqlquery data.ttl -qf query.rq -f csv を実行する\n\n\nここまで読めば、突然出てくるSPARQLの例も、初見のコードではなくなっているはずです。ぜひ、ハマってしまった記事に戻って読んでみてください！なんとなくわかるようになってる気がします！\n\nこちらからは以上です。","html":"<h1></h1><h2 id=\"0-%E3%81%93%E3%81%AE%E8%A8%98%E4%BA%8B%E3%81%AE%E7%9B%AE%E7%9A%84\">0. この記事の目的</h2><p>「SPARQL」に関して書いてある記事等には、こういうコードが、特に説明もなく登場したりしますよね。</p><pre><code class=\"language-sparql\">PREFIX saas: &lt;https://example.com/ontology/saas#&gt;\n\nSELECT ?workspace ?workspaceName\nWHERE {\n    &lt;https://example.com/data/tenant-abc&gt;\n        saas:containsWorkspace ?workspace .\n\n    ?workspace saas:name ?workspaceName .\n}\n</code></pre><p><code>PREFIX</code>、<code>SELECT</code>、<code>WHERE</code>、<code>?workspace</code>。見た感じSQLに似ている気もしますが、確信が持てないまま読み進めるのは、なかなかつらいものです。</p><p>この記事は、その手前の部分を埋めるための記事です。SPARQLだけに絞って、それが何であり、どうやって実際に手元で動かすのかを、説明します！</p><p>※この記事は、RDF・RDFS・OWL・Turtleについて説明済みの前提で書きます。まだの方は、先に<a href=\"https://tech.anti-pattern.co.jp/rdfrdfsowlturtlenowei-iwozheng-li-site-huairudeli-jie-suruontoroziru-men/\">RDF・RDFS・OWL・Turtleの違いを整理して、ファイルで理解するオントロジー入門</a>を読んでおいてください。</p><hr><h2 id=\"1-sparql%E3%81%A8%E3%81%AF%E4%BD%95%E3%81%8B\">1. SPARQLとは何か</h2><p>一言でいうと、SPARQLは次のようなものです。</p><pre><code class=\"language-text\">SPARQL：RDFグラフに問い合わせるための検索言語\n</code></pre><p>正式名称はSPARQL Protocol and RDF Query Languageです。読み方は「スパークル」です。</p><p>プログラミングやデータベースに触れたことがある方には、次のたとえが分かりやすいと思います。</p><pre><code class=\"language-text\">SQL    ：テーブルに対して問い合わせる\nSPARQL ：RDFグラフに対して問い合わせる\n</code></pre><p><a href=\"https://tech.anti-pattern.co.jp/rdfrdfsowlturtlenowei-iwozheng-li-site-huairudeli-jie-suruontoroziru-men/\">RDF・RDFS・OWL・Turtleの違いを整理して、ファイルで理解するオントロジー入門</a>で説明した技術を、もう一度並べてみます。</p><pre><code class=\"language-text\">RDF\n    情報をトリプルとして表す\n\nRDFS・OWL\n    データが何を意味するかを定義する\n\nSHACL\n    データが期待する形になっているかを検証する\n\nSPARQL\n    RDFグラフから、必要な情報を取り出す\n</code></pre><p>RDFで情報を表し、RDFS・OWLで意味を定義し、SHACLで形を検証し、SPARQLで情報を取り出す。この記事は、最後のSPARQLの部分を担当します。</p><hr><h2 id=\"2-sparql%E3%82%82%E3%80%81turtle%E3%81%AB%E4%BC%BC%E3%81%9F%E6%9B%B8%E3%81%8D%E6%96%B9%E3%82%92%E3%81%99%E3%82%8B\">2. SPARQLも、Turtleに似た書き方をする</h2><p>SPARQLのクエリは、Turtleのトリプルパターンとよく似た見た目をしています。これは偶然ではなく、意図的にそう設計されています。</p><pre><code class=\"language-turtle\">ex:yamada ex:worksFor ex:abc .\n</code></pre><p>これは、Turtleで書いた、確定した事実です。</p><pre><code class=\"language-sparql\">?employee ex:worksFor ex:abc .\n</code></pre><p>これは、SPARQLで書いた、問い合わせの条件です。</p><p>違いは、主語の部分が<code>ex:yamada</code>という具体的な値ではなく、<code>?employee</code>という変数になっている点だけです。</p><pre><code class=\"language-text\">実際のデータ\n    ex:yamada ex:worksFor ex:abc .\n\n問い合わせの条件\n    ?employee ex:worksFor ex:abc .\n</code></pre><p><code>?</code>で始まる名前は変数です。「ここに当てはまる値を教えてほしい」という、いわば穴あき問題です。</p><hr><h2 id=\"3-%E6%9C%80%E5%88%9D%E3%81%AE%E3%82%AF%E3%82%A8%E3%83%AA%E3%82%92%E6%9B%B8%E3%81%8F\">3. 最初のクエリを書く</h2><p>SPARQLの<code>SELECT</code>クエリは、次の3つの部分でできています。</p><pre><code class=\"language-sparql\">PREFIX ex: &lt;https://example.com/company#&gt;\n\nSELECT ?employee\nWHERE {\n    ?employee a ex:Employee .\n}\n</code></pre><p>順番に見ていきます。</p><pre><code class=\"language-sparql\">PREFIX ex: &lt;https://example.com/company#&gt;\n</code></pre><p>これは、Turtleの<code>@prefix</code>と同じ役割です。<code>ex:</code>を、このIRIの省略形として使う、という宣言です。書き方が少し違うだけで(<code>@</code>が無い、末尾に<code>.</code>が無い)、意味はまったく同じです。</p><pre><code class=\"language-sparql\">SELECT ?employee\n</code></pre><p>「<code>?employee</code>という変数の値を、結果として返してください」という指定です。</p><pre><code class=\"language-sparql\">WHERE {\n    ?employee a ex:Employee .\n}\n</code></pre><p>「<code>?employee</code>が、<code>ex:Employee</code>という型を持つ、というパターンに一致するものを探してください」という条件です。</p><p><code>a</code>は、<code>rdf:type</code>の省略記法です。これもTurtleと同じ書き方です。</p><p>このクエリは、日本語にすると次のような問い合わせです。</p><pre><code class=\"language-text\">Employeeという型を持つものを、すべて教えてください\n</code></pre><hr><h2 id=\"4-%E3%83%87%E3%83%BC%E3%82%BF%E3%82%92%E7%94%A8%E6%84%8F%E3%81%99%E3%82%8B\">4. データを用意する</h2><p>具体的な例で確認するために、次のデータを用意します。<a href=\"https://tech.anti-pattern.co.jp/rdfrdfsowlturtlenowei-iwozheng-li-site-huairudeli-jie-suruontoroziru-men/\">RDF・RDFS・OWL・Turtleの違いを整理して、ファイルで理解するオントロジー入門</a>の山田・鈴木・ABC社に、もう1人、田中を加えます。</p><pre><code class=\"language-turtle\"># data.ttl\n@prefix ex: &lt;https://example.com/company#&gt; .\n\nex:yamada a ex:Employee ;\n    ex:employeeId \"E001\" ;\n    ex:worksFor ex:abc ;\n    ex:manages ex:suzuki .\n\nex:suzuki a ex:Employee ;\n    ex:employeeId \"E002\" ;\n    ex:worksFor ex:abc .\n\nex:tanaka a ex:Employee ;\n    ex:employeeId \"E003\" ;\n    ex:worksFor ex:abc .\n\nex:abc a ex:Organization .\n</code></pre><p>山田はABC社に勤務し、鈴木を管理しています。鈴木と田中も、同じくABC社に勤務しています。</p><!--kg-card-begin: html-->\n  <script type=\"text/turtle\" class=\"ontologyviewer\" data-height=\"600px\" data-layout=\"dagre\">\n@prefix ex: <https://example.com/company#> .\n\nex:yamada a ex:Employee ;\n    ex:employeeId \"E001\" ;\n    ex:worksFor ex:abc ;\n    ex:manages ex:suzuki .\n\nex:suzuki a ex:Employee ;\n    ex:employeeId \"E002\" ;\n    ex:worksFor ex:abc .\n\nex:tanaka a ex:Employee ;\n    ex:employeeId \"E003\" ;\n    ex:worksFor ex:abc .\n\nex:abc a ex:Organization .\n\n    </script><!--kg-card-end: html--><hr><h2 id=\"5-%E5%85%B7%E4%BD%93%E4%BE%8B%E3%81%A7%E3%82%AF%E3%82%A8%E3%83%AA%E3%82%92%E6%9B%B8%E3%81%8F\">5. 具体例でクエリを書く</h2><h3 id=\"51-%E5%B1%B1%E7%94%B0%E3%81%AE%E5%8B%A4%E5%8B%99%E5%85%88%E3%82%92%E5%8F%96%E5%BE%97%E3%81%99%E3%82%8B\">5.1 山田の勤務先を取得する</h3><pre><code class=\"language-sparql\">PREFIX ex: &lt;https://example.com/company#&gt;\n\nSELECT ?org\nWHERE {\n    ex:yamada ex:worksFor ?org .\n}\n</code></pre><p>主語(<code>ex:yamada</code>)と述語(<code>ex:worksFor</code>)を固定し、目的語だけを変数にしています。結果は、<code>ex:abc</code>です。</p><h3 id=\"52-abc%E7%A4%BE%E3%81%AB%E5%8B%A4%E5%8B%99%E3%81%99%E3%82%8B%E5%85%A8%E5%93%A1%E3%82%92%E5%8F%96%E5%BE%97%E3%81%99%E3%82%8B\">5.2 ABC社に勤務する全員を取得する</h3><p>今度は逆に、主語を変数にします。</p><pre><code class=\"language-sparql\">PREFIX ex: &lt;https://example.com/company#&gt;\n\nSELECT ?employee\nWHERE {\n    ?employee ex:worksFor ex:abc .\n}\n</code></pre><p>このクエリは、「<code>ex:worksFor ex:abc</code>という関係の、主語側に来るものをすべて教えてください」という意味です。データを逆方向にたどっていることになります。</p><p>RDFのトリプルには、あらかじめ決まった「たどってよい方向」はありません。主語を固定して目的語を探すこともできますし、目的語を固定して主語を探すこともできます。</p><h3 id=\"53-%E8%A4%87%E6%95%B0%E3%81%AE%E6%9D%A1%E4%BB%B6%E3%82%92%E3%81%A4%E3%81%AA%E3%81%92%E3%82%8B\">5.3 複数の条件をつなげる</h3><p>山田が管理している人の、社員番号を取得したいとします。1つのトリプルパターンだけでは表せません。</p><pre><code class=\"language-sparql\">PREFIX ex: &lt;https://example.com/company#&gt;\n\nSELECT ?subordinateId\nWHERE {\n    ex:yamada ex:manages ?subordinate .\n    ?subordinate ex:employeeId ?subordinateId .\n}\n</code></pre><p><code>WHERE</code>の中に、2つのトリプルパターンが並んでいます。</p><pre><code class=\"language-text\">1つ目：ex:yamada ex:manages ?subordinate .\n2つ目：?subordinate     ex:employeeId ?subordinateId .\n</code></pre><p>1つ目のパターンで<code>?subordinate</code>に当てはまった値が、2つ目のパターンの主語としてそのまま使われます。同じ変数名を使うことで、2つのパターンが連動します。</p><p>SQLに慣れている方には、これがJOINに相当する、と考えると分かりやすいと思います。1つ目のパターンで<code>?subordinate</code>が<code>ex:suzuki</code>に決まり、2つ目のパターンによって<code>ex:suzuki</code>の<code>employeeId</code>である<code>\"E002\"</code>が引き出されます。</p><hr><h2 id=\"6-%E5%AE%9F%E9%9A%9B%E3%81%AB%E5%95%8F%E3%81%84%E5%90%88%E3%82%8F%E3%81%9B%E3%82%92%E5%AE%9F%E8%A1%8C%E3%81%97%E3%81%A6%E3%81%BF%E3%82%8B\">6. 実際に問い合わせを実行してみる</h2><p>ここまでは、クエリの書き方の説明でした。ここからは、実際にこのクエリを動かしてみます。</p><h3 id=\"61-%E6%BA%96%E5%82%99\">6.1 準備</h3><pre><code class=\"language-bash\">pip install rdflib 'pyparsing&gt;=3.1'\n</code></pre><p><code>rdflib</code>は、RDFをPythonで扱うための、最も基本的なライブラリです。これをインストールすると、<code>sparqlquery</code>というコマンドラインツールも一緒に使えるようになります。</p><h3 id=\"62-%E3%83%87%E3%83%BC%E3%82%BF%E3%81%A8%E3%82%AF%E3%82%A8%E3%83%AA%E3%82%92%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AB%E4%BF%9D%E5%AD%98%E3%81%99%E3%82%8B\">6.2 データとクエリをファイルに保存する</h3><p><code>4.</code>のデータを<code>data.ttl</code>に、<code>5.1</code>のクエリを<code>query.rq</code>に保存します。</p><pre><code class=\"language-sparql\"># query.rq\nPREFIX ex: &lt;https://example.com/company#&gt;\n\nSELECT ?org\nWHERE {\n    ex:yamada ex:worksFor ?org .\n}\n</code></pre><h3 id=\"63-%E5%AE%9F%E8%A1%8C%E3%81%99%E3%82%8B\">6.3 実行する</h3><p>同じフォルダに<code>data.ttl</code>と<code>query.rq</code>を置いた状態で、次のコマンドを実行します。</p><pre><code class=\"language-bash\">sparqlquery data.ttl -qf query.rq -f csv\n</code></pre><p><code>-qf</code>のあとにクエリファイル、<code>-f csv</code>で結果をCSV形式(見やすい表形式)で出力するよう指定しています。</p><p>結果は、次のように表示されます。</p><pre><code class=\"language-text\">org\nhttps://example.com/company#abc\n</code></pre><p>1行目が列名(<code>SELECT</code>で指定した変数名)、2行目以降が実際の値です。この出力は、実際にこの記事のとおりに実行して得られたものです。</p><h3 id=\"64-%E3%82%82%E3%81%861%E3%81%A4%E8%A9%A6%E3%81%97%E3%81%A6%E3%81%BF%E3%82%8B\">6.4 もう1つ試してみる</h3><p><code>query.rq</code>の内容を、<code>5.2</code>のクエリに書き換えて、もう一度実行してみます。</p><pre><code class=\"language-sparql\"># query.rq\nPREFIX ex: &lt;https://example.com/company#&gt;\n\nSELECT ?employee\nWHERE {\n    ?employee ex:worksFor ex:abc .\n}\nORDER BY ?employee\n</code></pre><pre><code class=\"language-bash\">sparqlquery data.ttl -qf query.rq -f csv\n</code></pre><pre><code class=\"language-text\">employee\nhttps://example.com/company#suzuki\nhttps://example.com/company#tanaka\nhttps://example.com/company#yamada\n</code></pre><p>鈴木・田中・山田の3人が、ABC社に勤務している全員として返ってきました。</p><hr><h2 id=\"7-distinct%E3%81%A8order-by\">7. DISTINCTとORDER BY</h2><p>SPARQLのクエリには、<code>DISTINCT</code>や<code>ORDER BY</code>もよく登場します。どちらも、SQLと同じ意味です。</p><pre><code class=\"language-text\">ORDER BY ?変数\n    結果を、その変数の値で並べ替える\n\nDISTINCT\n    同じ組み合わせの結果が複数出たとき、重複を1つにまとめる\n</code></pre><p><code>ORDER BY</code>は、先ほどの<code>6.4</code>のクエリでも使いました。<code>?employee</code>のIRI文字列の順番(アルファベット順)で並んでいたのは、<code>ORDER BY ?employee</code>を指定していたためです。</p><p><code>DISTINCT</code>は、次のように使います。</p><pre><code class=\"language-sparql\">PREFIX ex: &lt;https://example.com/company#&gt;\n\nSELECT DISTINCT ?org\nWHERE {\n    ?employee ex:worksFor ?org .\n}\n</code></pre><p><code>SELECT</code>のすぐ後ろに<code>DISTINCT</code>を書きます。3人の社員が同じ<code>ex:abc</code>に勤務しているため、<code>DISTINCT</code>が無ければ<code>ex:abc</code>が3回、あれば1回だけ返ります。手元で両方を実行して、違いを確認してみてください。</p><hr><h2 id=\"8-%E3%82%82%E3%81%86%E5%B0%91%E3%81%97%E8%A4%87%E9%9B%91%E3%81%AA%E5%95%8F%E3%81%84%E5%90%88%E3%82%8F%E3%81%9B%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6\">8. もう少し複雑な問い合わせについて</h2><p>ここまでで説明したのは、<code>SELECT</code>・<code>WHERE</code>・トリプルパターンの組み合わせ・<code>ORDER BY</code>・<code>DISTINCT</code>という、SPARQLの中でも基本的な部分です。</p><p>さらに発展的なクエリには、次のような機能も登場します。</p><pre><code class=\"language-text\">OPTIONAL\n    無くてもエラーにせず、あれば結果に含める\n\nFILTER\n    値を比較して、条件に合わないものを除外する\n</code></pre><p>たとえば、<code>OPTIONAL</code>は次のように使います。</p><pre><code class=\"language-sparql\">SELECT ?employee ?org\nWHERE {\n    ?employee a ex:Employee .\n    OPTIONAL { ?employee ex:worksFor ?org . }\n}\n</code></pre><p>これは、「Employeeはすべて返すが、<code>worksFor</code>が書かれていない場合は、<code>?org</code>を空(未束縛)のままにする」という意味です。<code>OPTIONAL</code>を使わずに<code>?employee ex:worksFor ?org .</code>を普通に書くと、<code>worksFor</code>が無いEmployeeはそもそも結果に出てきません。</p><p><code>FILTER</code>は、次のように使います。</p><pre><code class=\"language-sparql\">SELECT ?employee ?id\nWHERE {\n    ?employee ex:employeeId ?id .\n    FILTER (?id != \"E001\")\n}\n</code></pre><p>これは、「社員番号が<code>\"E001\"</code>ではない人だけを返す」という意味です。</p><p>この記事では、<code>OPTIONAL</code>と<code>FILTER</code>の説明までは踏み込みません。ここで大事なのは、次の1点だけです。</p><pre><code class=\"language-text\">基本のSELECT/WHEREだけでは、\n「無い場合の扱い」や「値による絞り込み」を\nうまく表現できない場面がある。\n\nそのために OPTIONAL と FILTER がある。\n</code></pre><p>これだけ知っておけば、<code>OPTIONAL</code>や<code>FILTER</code>が出てきても、「ああ、無い場合の扱いや絞り込みをしているんだな」と読めるはずです。</p><hr><h2 id=\"%E3%81%BE%E3%81%A8%E3%82%81\">まとめ</h2><p>最後に、この記事の内容を整理します。</p><pre><code class=\"language-text\">SPARQLとは\n    RDFグラフに問い合わせるための検索言語\n\nPREFIX\n    TurtleのPREFIXと同じ、IRIの省略形の宣言\n\nSELECT ?変数\n    結果として返す変数を指定する\n\nWHERE { トリプルパターン }\n    ?変数を含むトリプルパターンで、条件を書く\n\n同じ変数名でつなげる\n    複数のトリプルパターンを連動させる(SQLのJOINに近い)\n\nORDER BY / DISTINCT\n    結果の並べ替え / 重複の除去\n\nOPTIONAL / FILTER\n    より複雑な問い合わせのための機能(この記事では扱わない)\n</code></pre><p>実際に問い合わせを動かす手順は、次の3ステップでした。</p><pre><code class=\"language-text\">1. pip install rdflib 'pyparsing&gt;=3.1'\n2. データを data.ttl に、クエリを query.rq に保存する\n3. sparqlquery data.ttl -qf query.rq -f csv を実行する\n</code></pre><p>ここまで読めば、突然出てくるSPARQLの例も、初見のコードではなくなっているはずです。ぜひ、ハマってしまった記事に戻って読んでみてください！なんとなくわかるようになってる気がします！</p><p>こちらからは以上です。</p><p></p>","url":"https://ghost.tech.anti-pattern.co.jp/whatisspaeql/","canonical_url":null,"uuid":"21fe47bc-aaf9-47a6-bbaf-b2d1908eb599","page":null,"codeinjection_foot":null,"codeinjection_head":null,"codeinjection_styles":null,"comment_id":"6a8a99a7781e37000184ef29","reading_time":6}},"pageContext":{"slug":"whatisspaeql"}},
    "staticQueryHashes": ["176528973","2358152166","2561578252","2731221146","4145280475"]}