Запрос на выборку на основе параметра в Oracle PL / SQL - PullRequest
2 голосов
/ 16 ноября 2010

Хорошо, скажите, что у меня есть запрос:

SELECT * FROM TABLE_AWESOME WHERE YEAR = :AMAZINGYEAR;

, который работает очень хорошо.Но скажем, я хочу иметь возможность вернуть либо только те результаты, либо все результаты, основанные на раскрывающемся списке.(например, выпадающий список будет иметь 2008, 2009, ВСЕ ГОДЫ)

Я решил заняться указанной проблемой с PL / SQL в следующем формате:

DECLARE
  the_year VARCHAR(20) := &AMAZINGYEAR;
BEGIN
  IF the_year = 'ALL' THEN
      SELECT * FROM TABLE_AWESOME;
  ELSE
      SELECT * FROM TABLE_AWESOME WHERE YEAR = the_year;
  END IF;
END;

К сожалению, это не удается.Я получаю сообщения об ошибках типа «предложение INTO ожидается в этом операторе SELECT».

Я совершенно новичок в PL / SQL, поэтому думаю, что просто слишком многого от него ожидаю.Я просмотрел документацию, но не нашел ни одной причины, почему это не сработало бы так, как у меня.На самом деле используемый мной запрос намного сложнее, но я хочу, чтобы он был простым, поэтому я быстро получу ответ.

Заранее спасибо:)

Ответы [ 5 ]

4 голосов
/ 17 ноября 2010

Существует реальная опасность в запросах, предложенных Джимом и Алексом.

Предположим, у вас есть данные за 20 лет, поэтому запрос по YEAR = возвращает 5% блоков.Я говорю блоки, а не строки, потому что я предполагаю, что данные добавляются в эту дату, поэтому коэффициент кластеризации высокий.

Если вы хотите 1 год, вы хотите, чтобы оптимизатор использовал индекс по годам, чтобы найти эти 5% строк.

Если вы хотите все годы, вы хотите, чтобы оптимизатор использовал полное сканирование таблицы для получения каждой строки.

Пока все хорошо?

Один развы запускаете его в производство, когда Oracle в первый раз загружает пиковый запрос в переменную связывания и на основе этого формулирует план.

Итак, скажем, первая загрузка - «Все».

Отлично, план представляет собой сканирование полной таблицы (FTS), и этот план кэшируется, и вы возвращаете все строки через 5 минут.Ничего страшного.

При следующем запуске вы говорите 1999 год. Но план кэшируется, и поэтому он использует FTS, чтобы получить только 5% строк, и это занимает 5 минут.«Хммм ... пользователь говорит, что было намного меньше строк и в то же время».Но это нормально ... это всего лишь 5-минутный отчет ... жизнь немного медленнее, когда этого не нужно, но никто не кричит.

Той ночью пакетные задания выбивают этот запрос изкеш, а утром первый пользователь запрашивает 2001 год. Oracle проверяет кеш, а не там, заглядывает в переменную 2001 года. Ах, лучший план для этого - сканирование индекса.и этот план кэшируется.Результаты возвращаются через 10 секунд и поражают пользователя.Следующий человек, обычно первый, делает утренний отчет «ВСЕ», и запрос никогда не возвращается.

ПОЧЕМУ?

Поскольку он просматривает каждую строку, просматривая индекс ...ужасные вложенные петли.5-минутный отчет теперь на 30 и считается.

Ваш оригинальный пост имеет лучший ответ.Два запроса, так что оба всегда ВСЕГДА получат наилучший план, просмотр переменной привязки не убьет вас.

Проблема, с которой вы столкнулись, является просто фундаментальной проблемой Oracle.Вы запускаете запрос из инструмента и получаете результаты обратно в инструмент.Если вы помещаете оператор select в блок pl / sql, вы должны что-то с ним сделать.Вы должны загрузить его в курсор, или массив, или переменную.Это не имеет ничего общего с тем, что вы не правы, а они правы ... это просто отсутствие навыков pl / sql.

3 голосов
/ 16 ноября 2010

В PL / SQL вам нужно SELECT ... INTO что-то, что вы должны иметь возможность вернуть клиенту;это может быть указатель ссылки, как показывает касание.Это может усложнить клиента.

Вместо этого вы можете сделать это в SQL с помощью чего-то вроде:

SELECT * FROM TABLE_AWESOME WHERE :AMAZING_YEAR = 'ALL' OR YEAR = :AMAZINGYEAR;

... хотя вам может потребоваться позаботиться об индексах;Я бы посмотрел на план выполнения с обоими типами аргументов, чтобы убедиться, что он не делает ничего неожиданного.

3 голосов
/ 16 ноября 2010

Вы можете сделать это одним запросом, например:

SELECT * FROM TABLE_AWESOME WHERE (? = 'ALL' OR YEAR = ?)

и передать ему аргумент дважды.

1 голос
/ 16 ноября 2010

Не уверен насчет использования SqlDataSource, но вы определенно можете сделать это через system.data.oracle или oracle-клиенты.

Это можно сделать через анонимный блок в asp.net

VAR SYS1 REFCURSOR;
VAR SYS2 REFCURSOR;

DECLARE
  FUNCTION CURSORCHOICE(ITEM IN VARCHAR2) RETURN SYS_REFCURSOR IS
      L_REFCUR SYS_REFCURSOR;
    returnNum VARCHAR2(50);
    BEGIN
        IF upper(item) = 'ALL' THEN
            OPEN L_REFCUR FOR
            SELECT  level  FROM DUAL 
            CONNECT BY LEVEL < 15 ;
        ELSE
            OPEN L_REFCUR FOR
            SELECT   'NONE'  FROM DUAL ;  
        END IF;
        RETURN L_REFCUR;
    END ;
BEGIN
:SYS1 := CURSORCHOICE('ALL');
:SYS2 := CURSORCHOICE('NOT ALL');
end ;
/
PRINT :SYS1 ;
PRINT :SYS2 ;

тогда как вы просто создали бы выходной параметр (типа refcursor) - вместо var sys # refcursors) и в значительной степени просто изменили приведенный выше код.

Я ответил на аналогичный вопрос о получениианонимный блок refcuror здесь Как вернуть RefCursor из функции Oracle?

0 голосов
/ 16 ноября 2010

Этот тип параметра должен обрабатываться из вашего кода, чтобы ваш OracleCommand объект выполнял только один из запросов.

using (var connection = new OracleConnection(connString)) {
    connection.Open();

    string sql = "select * from table_awesome";
    sql = string.Concat(sql, theYear.Equals(@"ALL") ? string.Empty : " where year = :pYear")

    using (var command = connection.CreateCommand()) {
        command.CommancText = sql;
        command.CommandType = CommandType.Text;
        var parameter = command.CreateParameter();
        parameter.Name = @":yearParam";
        parameter.Direction = ParameterDirection.Input;
        parameter.Value = theYear;

        var reader = command.ExecuteQuery();

        if (!reader.HasRows) return;

        while (reader.Read()) {
            // Extract your data from the OracleDataReader instance here.
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...