SubSonic 2.2 で Oracle top-n (ページング) クエリを実行するにはどうすればよいでしょうか?

StackOverflow https://stackoverflow.com/questions/3824483

質問

(免責事項: セキュリティ上の理由から、ここでは変数/テーブル/列名の一部を変更/難読化しました。多少の違和感はご容赦ください。)

Oracle 10g データベースのフロントエンドを構築しており、ページングされたデータを取得しようとしています。ページングとは別に、次の SubSonic 2.2 コードは、必要なものを必要な順序で提供します。

var q = new Select()
  .From(AppDb.MyTable.Schema)
  .Where(AppDb.MyTable.DisabledDateColumn).IsNull()
  .OrderDesc(AppDb.MyTable.CreatedDateColumn.ColumnName)

System.Console.Out.Writeline(q.BuildStatement());

これにより、次の SQL が生成されます。

SELECT
    MYSCHEMA.MYTABLE.ID,
    MYSCHEMA.MYTABLE.DISABLED_DATE
FROM
    MYSCHEMA.MYTABLE
WHERE
    MYSCHEMA.MYTABLE.DISABLED_DATE IS NULL
ORDER BY
    CREATED_DATE DESC

次に、ページングを導入してみます。

var q = new Select()
  .From(AppDb.MyTable.Schema)
  .Where(AppDb.MyTable.DisabledDateColumn).IsNull()
  .OrderDesc(AppDb.MyTable.CreatedDateColumn.ColumnName)
  .Paged(0, 10);

そして、WHERE 句と ORDER BY 句を消去します。

SELECT * FROM (
    SELECT
        MYSCHEMA.MYTABLE.ID,
        MYSCHEMA.MYTABLE.DISABLED_DATE,
        ROWNUM as row_number
    FROM
        MYSCHEMA.MYTABLE
)
WHERE
    row_number BETWEEN 1 AND 10

私は SubSonic を初めて使用するので、それがバグなのか、それとも私が推奨する方法で実行していないだけなのか、あるいは Oracle がこれを別の、より Oracle 中心の方法で実行することを要求しているのかはわかりません。(Oracle はすべてにおいてそれを要求しているようです。)

明らかではない場合に備えて、後続の各ページに、次に新しく作成された無効になっていない 10 個のレコードを含めることを望みます。SubSonic 2.2 でこれを行うにはどうすればよいですか?

役に立ちましたか?

解決

クエリが現在の SQL と同じ SQL を生成していないようです。 SubSonic 用の Oracle データ プロバイダー すべき。関連するコードは次のとおりです (852 行目から)。

    if(qry.PageIndex < 0)
        query = String.Format("{0} {1} FROM {2}.{3} {4} {5}",select ,columns, table.SchemaName, table.Name, where, order);
    else
    {
        int start = qry.PageIndex * qry.PageSize;
        int end = (qry.PageIndex + 1) * qry.PageSize;

        const string cteFormat =
                    "WITH pagedtable AS (SELECT {0}, ROW_NUMBER () OVER ({1}) AS rowindex FROM {2}.{3} {4}) SELECT {5}, rowindex FROM pagedtable WHERE rowindex >= {6} AND rowindex < {7} ORDER BY rowindex";
        query = string.Format(cteFormat, columns, order,table.SchemaName, table.Name, where, columns.Replace(table.Name + ".", String.Empty), start, end);
    }
    return query;

おそらく、現在のソースを更新するとうまくいくでしょう。実際のプロバイダーに問題がある場合は、それを SqlDataProvider と比較して、問題の原因についてのヒントを得ることができます。

他のヒント

私は NET 2.0 と Oracle 9i R2 を使用している環境にいますが、全く同じ問題に遭遇しました。SubSonic2.2を使用しています。

GitHub からソースをダウンロードしたところ、次のものが見つかりました。

@ranomore によって引用されたコード (OracleDataProvider.GetSelectSql()) は、SubSonic.Query オブジェクトを使用する場合にのみ呼び出されます。OP と私自身は、より新しく強力な SubSonic.SqlQuery オブジェクトから派生した Select オブジェクトを使用しているため、OracleDataProvider.GetSelectSql() が呼び出されることはありません。その代わり、 OracleGenerator.BuildPaggedSelectStatement() が呼び出され、OP によって投稿された SQL が生成されます。このコードには、最終的に生成されるページネーション クエリに WHERE 句と ORDER BY 句が追加されないため、バグがあります。

BuildPaggedSelectStatement() の内容を ANSISqlGenerator.BuildSelectStatement() に基づくものに置き換えました。

public override string BuildPagedSelectStatement()
{
    int startnum = query.PageSize * query.CurrentPage + 1;
    int endnum = query.PageSize * query.CurrentPage + query.PageSize;
    string orderBy = String.Empty;

    if (this.query.OrderBys.Count > 0)
        orderBy = GenerateOrderBy();

    //The ROW_NUMBER() function in Oracle requires an ORDER BY clause.
    //In case one is not specified, we need to halt and inform the caller.
    if(orderBy.Equals(String.Empty))
        throw new ArgumentException("There is no column specified for the ORDER BY clause", "OrderBys");

    System.Text.StringBuilder sql = new System.Text.StringBuilder();

    //Build the command string
    sql.Append("WITH pagedtable AS (");
    sql.Append(GenerateCommandLine());

    //Since this class is for Oracle-specific SQL, we can add a hint
    //which should help pagination queries return rows more quickly.
    //AFAIK, this is only valid for Oracle 9i or newer.
    sql.Replace("SELECT", "SELECT /*+ first_rows('" + query.PageSize + "') */");

    sql.Append(", ROW_NUMBER () OVER (");
    sql.Append(orderBy);
    sql.Append(") AS rowindex ");
    sql.Append(Environment.NewLine);
    sql.Append(GenerateFromList());
    sql.Append(GenerateJoins());

    sql.Append(GenerateWhere());

    if (query.Aggregates.Count > 0)
    {
        sql.Append(GenerateGroupBy());
        sql.Append(Environment.NewLine);
        sql.Append(GenerateHaving());
    }

    sql.Append(") SELECT * FROM pagedtable WHERE rowindex >= ");
    sql.Append(startnum);
    sql.Append(" AND rowindex < ");
    sql.Append(endnum);
    sql.Append(" ORDER BY rowindex");

    return sql.ToString();
}

上記は私にとってはうまくいきました。これが他の人にも役立つことを願っています!

内部 SQL が最初のステートメント (where と order by を含む) で構成されている場合、Oracle は Top-N クエリを使用できます。

したがって、これらを省略するOracle固有の理由はないと思います。

亜音速を使用したことがないので、別の方法を使用する必要があるかどうかわかりません。

パフォーマンスの観点からは、DISABLED_DATE、CREATED_DATE のインデックスがうまく機能するはずです (以下を参照)。 http://blog.fatalmind.com/2010/07/30/analytic-top-n-queries/).

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top