Как выполнить запрос Oracle top-n (постраничный) в SubSonic 2.2?

(Заявление об ограничении ответственности: я изменил / запутал некоторые имена переменных / таблиц / столбцов здесь из соображений безопасности. Простите меня, если что-то выглядит не так.)

Я создаю интерфейс для базы данных 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?


person Joel Wietelmann    schedule 29.09.2010    source источник


Ответы (3)


Похоже, ваш запрос не генерирует тот же SQL, что и текущий Поставщик данных Oracle для SubSonic должен. Вот соответствующий код (начиная со строки 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, чтобы узнать, в чем может быть проблема.

person Dave Neeley    schedule 03.10.2010

Я нахожусь в среде, использующей NET 2.0 и Oracle 9i R2, и я столкнулся с той же самой проблемой. Я использую SubSonic 2.2.

Я скачал исходный код с GitHub и вот что нашел:

Код, цитируемый @ranomore (OracleDataProvider.GetSelectSql ()), вызывается только при использовании объекта SubSonic.Query. Поскольку OP и я использую объект Select, который является производным от более нового и более мощного объекта SubSonic.SqlQuery, OracleDataProvider.GetSelectSql () никогда не вызывается. Вместо этого OracleGenerator.BuildPagedSelectStatement () вызывается и генерирует SQL, который вы видите опубликованным OP. Этот код содержит ошибки, поскольку он никогда не добавляет предложения WHERE и ORDER BY в запрос разбивки на страницы, который он в конечном итоге генерирует.

Я заменил содержимое BuildPagedSelectStatement () чем-то, основанным на 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();
}

Вышеупомянутое сработало для меня. Надеюсь, это поможет другим!

person FrostyZoob    schedule 04.02.2011

Oracle подойдет для запроса Top-N, если внутренний SQL будет состоять из первого оператора (включая where и order by).

Итак, я бы сказал, что у Oracle нет особой причины их опускать.

Никогда не использовал дозвуковой, не знаю, нужно ли вам там делать это по-другому.

С точки зрения производительности индекс на DISABLED_DATE, CREATED_DATE должен помочь (см .: http://blog.fatalmind.com/2010/07/30/analytic-top-n-queries/).

person Markus Winand    schedule 30.09.2010