문제

(Disclaimer: I changed/obfuscated some of the variable/table/column names here for security reasons. Please forgive me if something looks a little off.)

I am building a front-end to an Oracle 10g database, and I'm trying to get paged data. Aside from paging, the following SubSonic 2.2 code gives me what I want, in the order I want it:

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

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

This yields the following SQL:

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

Then I try to introduce paging:

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

And it wipes out my WHERE and ORDER BY clauses:

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

I'm brand new to SubSonic, so I don't know if that's a bug, or I'm just not doing it the preferred way, or if Oracle demands that this be done in a different, more Oracle-centric way. (Oracle seems to demand that of everything.)

What I want, in case it isn't obvious, is each succeeding page to include the 10 next most-recently-created, non-disabled records. How can I do this in SubSonic 2.2?

도움이 되었습니까?

해결책

It looks like your query is not generating the same SQL as the current Oracle Data Provider for SubSonic should. Here is the relevant code (starting at line 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;

Perhaps an update to current source would do the trick. If there is something wrong with the actual provider, you can compare it to the SqlDataProvider for hints as to what might be the problem.

다른 팁

I'm in an environment using NET 2.0 and Oracle 9i R2 and I ran into the same exact problem. I'm using SubSonic 2.2.

I downloaded the source from GitHub and this is what I found:

The code quoted by @ranomore (OracleDataProvider.GetSelectSql()) is only called when using the SubSonic.Query object. Since the OP and myself use the Select object which is derived from the newer and more powerful SubSonic.SqlQuery object, OracleDataProvider.GetSelectSql() never gets called. Instead, OracleGenerator.BuildPagedSelectStatement() gets called and generates the SQL you see posted by the OP. This code is buggy as it never adds the WHERE and ORDER BY clauses to the pagination query it ultimately generates.

I replaced the contents of BuildPagedSelectStatement() with something based on 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();
}

The above worked for me. Hope this helps others!

Oracle would be fine with the Top-N query if the inner SQL would consist of the first statement (including where and order by).

So, i'd say there is no Oracle specific reason to omit them.

Never used subsonic, don't know if you need to do it different there.

Performance wise, an index on DISABLED_DATE, CREATED_DATE should do the trick (see: http://blog.fatalmind.com/2010/07/30/analytic-top-n-queries/).

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top