1.9 SEM_APIS.SPARQL_TO_SQL関数を使用したRDFデータの問合せ

SEM_APIS.SPARQL_TO_SQL関数をSEM_MATCH表関数の代わりに使用してRDFデータを問い合せられます。

ノート:

SEM_APIS.SPARQL_TO_SQL関数は、Oracle Autonomous Databaseサーバーレス・デプロイメントでOracle JVMが有効になっている場合にのみサポートされます。Oracle JVMを有効にするには、Oracle Autonomous Databaseサーバーレスの使用Oracle Javaの使用で詳細を参照してください。

SEM_APIS.SPARQL_TO_SQL関数は、SEM_MATCH表関数の代替になるものとして用意されています。アプリケーション開発者は、これを利用してSPARQL問合せをSQLに翻訳することができます。これは、SEM_MATCHを実行した場合と同じSQL翻訳です。生成されたSQL翻訳は、その他のSQL文字列と同じ方法で実行可能です(たとえばPL/SQLのEXECUTE IMMEDIATEや、JavaアプリケーションのJDBC)。

SEM_APIS.SPARQL_TO_SQLに対する最初の(sparql_query)パラメータで、SPARQL問合せ文字列を指定します。これはSEM_MATCHの問合せ引数に相当します。ただしこの場合、sparql_queryは、CLOB型であり、4000バイト(VARCHARが有効になっている場合は32Kバイト)を超える長さの問合せ文字列を使用できます。その他の引数はすべて、SEM_MATCHの対応する引数とまったく同等です(「RDFデータに対する問合せでのSEM_MATCH表関数の使用」を参照してください)。SEM_APIS.SPARQL_TO_SQLで返される問合せ文字列によって、SEM_MATCHを同じ引数で実行したときと同じ結果の列が生成されます。

次に示すPL/SQLの一部分は、SEM_APIS.SPARQL_TO_SQL関数を使用した例です。

DECLARE
  c           sys_refcursor;
  sparql_stmt clob;
  sql_stmt    clob;
  x_value     varchar2(4000);
BEGIN
  sparql_stmt := 
    'PREFIX : <http://www.example.org/family/>
     SELECT ?x
     WHERE {
       ?x :grandParentOf ?y . 
       ?x rdf:type :Male
     }';

  sql_stmt := sem_apis.sparql_to_sql(
                sparql_stmt,
                sem_models('family'),
                SEM_Rulebases('RDFS','family_rb'),
                null,
                null,
                ' PLUS_RDFT=VC ', null, null,
                'RDFUSER', 'NET1');

  open c for 'select x$rdfterm from(' || sql_stmt || ')';
  loop
    fetch c into x_value;
    exit when c%NOTFOUND;
    
    dbms_output.put_line('x_value: ' || x_value);    
  end loop;
  close c;

END;
/

1.9.1 SEM_APIS.SPARQL_TO_SQLによるバインド変数の使用

SEM_APIS.SPARQL_TO_SQL関数を使用すると、PL/SQLおよびJDBCのバインド変数を使用できます。これは、SEM_APIS.SPARQL_TO_SQLから返されるSQL翻訳に、ANYTYPE表関数への呼出しが含まれていないためです。基本的な仕組みとして、USE_BIND_VAR=PLSQLまたはUSE_BIND_VAR=JDBC問合せオプションが指定されている場合に、単純なSPARQL BIND句が、JDBCかPL/SQLのバインド変数いずれかに変換されます。単純なSPARQL BIND句とは、BIND (<constant> AS ?var)の形式を持つものです。

バインド変数オプションが指定されている場合、SQL翻訳には、変換されたSPARQL問合せ変数各々に対して2つのバインド変数が含まれることになります。1つは値ID、もう1つはRDF語句文字列です。値ID (RDF_VALUE$表から)を1番目のバインド値として、またRDF語句文字列を2番目のバインド値として指定することで、RDF語句をSPARQL問合せ変数の代替として使用できます。パフォーマンス上の理由から、制限内のRDF語句に対する値IDが必要です。一般的なワークフローでは、RDF語句に対する値IDをRDF_VALUE$表から(またはSEM_APIS.RES2VIDで)検索し、得られたIDとRDF語句を、翻訳されたSQLにバインドします。

1つの問合せで複数の問合せ変数がバインド変数に変換されることがあります。そうした場合、バインド変数は、SPARQL問合せ文字列のSPARQL BIND句と同じ順序でSQL翻訳に現れます。つまり、1番目のBIND句に対応するIDと語句のペアが最初にバインドされ、2番目のBIND句に対応するIDと語句のペアが2番目にバインドされます。

次に示す例では、PL/SQLブロックから、SEM_APIS.SPARQL_TO_SQLに対するバインド変数が使用されています。ダミーのバインド変数?nが宣言されています。

DECLARE
  sparql_stmt clob;
  sql_stmt    clob;
  cur         sys_refcursor;
  vid         number;
  term        varchar2(4000);
  c_val       varchar2(4000);
BEGIN
  -- Add a dummy bind clause in the SPARQL statement
  sparql_stmt := 'PREFIX : <http://www.example.org/family/>
                  SELECT ?c WHERE { 
                  BIND("" as ?s)
                  ?s :parentOf ?c }';
  -- Get the SQL translation for SPARQL statement
  sql_stmt := sem_apis.sparql_to_sql(
                sparql_stmt,
                sem_models('family'),
                SEM_Rulebases('RDFS','family_rb'),
                null,
                null,' USE_BIND_VAR=PLSQL PLUS_RDFT=VC ', null, null,
                'RDFUSER', 'NET1');

  -- Execute with <http://www.example.org/family/Martha>
  term := '<http://www.example.org/family/Martha>';
  vid := sem_apis.res2vid('RDFUSER.NET1#RDF_VALUE$',term);

  dbms_output.put_line(chr(10)||'?s='||term);
  open cur for 'select c$rdfterm from('|| sql_stmt || ')' using vid,term;
  loop
    fetch cur into c_val;
    exit when cur%NOTFOUND;
    dbms_output.put_line('|-->?c='||c_val);
  end loop;
  close cur;

  -- Execute with <http://www.example.org/family/Sammy>
  term := '<http://www.example.org/family/Sammy>';
  vid := sem_apis.res2vid('RDFUSER.NET1#RDF_VALUE$',term);

  dbms_output.put_line(chr(10)||'?s='||term);
  open cur for 'select c$rdfterm from('|| sql_stmt || ')' using vid,term;
  loop
    fetch cur into c_val;
    exit when cur%NOTFOUND;
    dbms_output.put_line('|-->?c='||c_val);
  end loop;
  close cur;

END;
/

次に示す例では、Javaから、SEM_APIS.SPARQL_TO_SQLに対するバインド変数が使用されています。この例では、ヒントUSE_BIND_VAR=JDBCが使用されています。

public static void sparqlToSqlTest() {

    try {
        // Get connection
        Connection conn=DriverManager.getConnection( 
                "jdbc:oracle:thin:@localhost:1521:orcl","testuser","testuser");          

        String sparqlStmt =
            "PREFIX : http://www.example.org/family/ \n" +
            "SELECT ?c WHERE {  \n" +
            "  BIND(\"\" as ?s) \n" +
            "  ?s :parentOf ?c      \n" +
            "}";

        // Get SQL translation of SPARQL statement
        // through sem_apis.sparql_to_sql
        OracleCallableStatement ocs = (OracleCallableStatement)conn.prepareCall(
            "begin" +
            "  ? := " +
            "    sem_apis.sparql_to_sql('" +
            "      "+sparqlStmt+"'," +
            "      sem_models('family')," +
            "      SEM_Rulebases('RDFS','family_rb')," +
            "      null,null," +
            "  ' USE_BIND_VAR=JDBC PLUS_RDFT=VC " +
            " ',null,null,'RDFUSER','NET1');" +
            "end;");          
        ocs.registerOutParameter(1,Types.VARCHAR);
        ocs.execute();
        String sqlStmt = ocs.getString(1);
        ocs.close();

        // Set up statement to look up value ids
        OracleCallableStatement ocsVid = (OracleCallableStatement)conn.prepareCall(
            "begin" +
            "  ? := sem_apis.res2vid(?,?);" +
            "end;");          

        // Execute SQL setting values for a bind variable
        PreparedStatement stmt=conn.prepareStatement(sqlStmt);

        // Look up value id for first value
        long valueId = 0;
        String term = "<http://www.example.org/family/Martha>";
        ocsVid.registerOutParameter(1,Types.NUMERIC);
        ocsVid.setString(2,"RDFUSER.NET1#RDF_VALUE$");
        ocsVid.setString(3,term);
        ocsVid.execute();
        valueId = ocsVid.getLong(1);

        stmt.setLong(1, valueId);
        stmt.setString(2, term);
        ResultSet rs=stmt.executeQuery();

        // Print results
        System.out.println("\n?s="+term);
        while(rs.next()) {
            System.out.println("|-->?c=" + rs.getString("c$rdfterm"));
        }
        rs.close();

        // Execute the same query for a different URI
        // Look up value id for next value
        valueId = 0;
        term = "<http://www.example.org/family/Sammy>";
        ocsVid.registerOutParameter(1,Types.NUMERIC);
        ocsVid.setString(2,"RDFUSER.NET1#RDF_VALUE$");
        ocsVid.setString(3,term);
        ocsVid.execute();
        valueId = ocsVid.getLong(1);

        stmt.setLong(1, valueId);
        stmt.setString(2, term);
        rs=stmt.executeQuery();

        // Print results
        System.out.println("\n?s="+term);
        while(rs.next()) {
            System.out.println("|-->?c=" + rs.getString("c$rdfterm"));
        }
        rs.close();

        stmt.close();
        ocsVid.close();
        conn.close(); 

    } catch (SQLException e) {
        e.printStackTrace();
    }
}

1.9.2 SEM_MATCHとSEM_APIS.SPARQL_TO_SQLの比較

SEM_APIS.SPARQL_TO_SQL関数では、書き換え可能な表関数インタフェースを使用していることに起因するSEM_MATCH表関数に固有の制限が回避されています。具体的には、SEM_APIS.SPARQL_TO_SQLでは次のようなことが可能です。

  • 4000バイト(LONG VARCHARに対応している場合32Kバイト)を超えるSPARQL問合せ文字列の使用。

  • SEM_APIS.SPARQL_TO_SQLから返されたプレーンSQLの、読取り専用データベースに対する実行。

  • SEM_APIS.SPARQL_TO_SQLから返されたプレーンSQLの、PL/SQLおよびJDBCのバインド変数への対応。

ただし、SEM_MATCHには、SEM_APIS.SPARQL_TO_SQLにはない特有の機能があります。

  • 射影の最適化への対応。SEM_MATCHの呼び出しで、射影された変数のVAR$RDFVID列のみが選択されている場合に、この変数に対するRDF_VALUE$の結合が回避されます

  • 開始、フェッチ、クローズを行うプロシージャ型表関数の実行が必要な拡張機能への対応(SPARQL SERVICEのSERVICE_JPDWN=TおよびOVERLOADED_NL=Tオプション)。

  • SQL*Plusのようなツールを利用して対話型で問合せを実行する機能。