2013-10-15 3 views
1

У меня возникли трудности с созданием блока PL/SQL. Цель моего скрипта - вывести отчет на основе итогов последнего месяца в таблице (которая уже была создана как имя таблицы «countpull»). Я хочу использовать имя месяца в моем сценарии для создания таблицы, но Oracle возвращает ошибку «ORA-00900: неверный оператор SQL», указывая на команду CREATE TABLE ниже.Использование переменной в скрипте создания таблицы

DECLARE 
    curMonthChar NVARCHAR2(25); 
    curTableName NVARCHAR2(50); 
    tableexists NUMBER := 0; 

BEGIN 
    SELECT TO_CHAR(ADD_MONTHS(sysdate,-1),'fmMONTH') INTO curMonthChar FROM DUAL; 
    SELECT ('QP17414_'||curMonthChar) INTO curTableName FROM DUAL; 

    --Check to see if current month's count table exists 
    SELECT COUNT(1) INTO tableexists FROM all_tab_columns WHERE OWNER = (SELECT USER FROM DUAL) AND table_name = curTableName; 

    --If current month's count table exists, drop it 
    IF tableexists > 0 THEN 
    EXECUTE IMMEDIATE 'DROP TABLE '||curTableName; 
    END IF; 

    --Create current month's count table 
    EXECUTE IMMEDIATE ' 
    CREATE TABLE '||curTableName||' AS (
    SELECT * FROM (
     SELECT movetype, phone_flag, state FROM countpull 
    ) 
    PIVOT (
     COUNT(state) 
     FOR state IN ('''||'AA'||''','''||'AE'||''','''||'AK'||''','''||'AL'||''' 
     ,'''||'AP'||''','''||'AR'||''','''||'AS'||''','''||'AZ'||''','''||'CA'||''' 
     ,'''||'CO'||''','''||'CT'||''','''||'DC'||''','''||'DE'||''','''||'FL'||''' 
     ,'''||'FM'||''','''||'GA'||''','''||'GU'||''','''||'HI'||''','''||'IA'||''' 
     ,'''||'ID'||''','''||'IL'||''','''||'IN'||''','''||'KS'||''','''||'KY'||''' 
     ,'''||'LA'||''','''||'MA'||''','''||'MD'||''','''||'ME'||''','''||'MH'||''' 
     ,'''||'MI'||''','''||'MN'||''','''||'MO'||''','''||'MP'||''','''||'MS'||''' 
     ,'''||'MT'||''','''||'NC'||''','''||'ND'||''','''||'NE'||''','''||'NH'||''' 
     ,'''||'NJ'||''','''||'NM'||''','''||'NV'||''','''||'NY'||''','''||'OH'||''' 
     ,'''||'OK'||''','''||'OR'||''','''||'PA'||''','''||'PR'||''','''||'PW'||''' 
     ,'''||'RI'||''','''||'SC'||''','''||'SD'||''','''||'TN'||''','''||'TX'||''' 
     ,'''||'UT'||''','''||'VA'||''','''||'VI'||''','''||'VT'||''','''||'WA'||''' 
     ,'''||'WI'||''','''||'WV'||''','''||'WY'||''') 
    ))'; 

END; 

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

Почему это недействительно для создания таблицы с именем динамической таблицы в блоке PL/SQL?

Дополнительная информация:

Схема таблицы для countpull является

state NVARCHAR2(2), 
movetype NVARCHAR2(1), 
phone_flag NVARCHAR2(1) 

ответ

2

По причинам, которые не сразу ясно мне, проблема в том, что переменная вы используете nvarchar2. Это работает, если вы объявите его как varchar2:

curTableName VARCHAR2(50); 

ПРОВЕРЯЕМЫЕ через SQL Fiddle; этот скрипт терпит неудачу, если вы просто измените объявление переменной имени таблицы на nvarchar2.

Как @jonearles указывает, documentation делает состояние, что команда струны "тип должен быть либо CHAR, VARCHAR2 или CLOB. И хотя документация для the concatenation operator не относится к ней, связанный с ней номер concat function указывает, что «если один из аргументов является национальным типом данных, тогда возвращаемое значение является национальным типом данных», поэтому ваша командная строка равна nvarchar2, поскольку переменная, которую вы используете в конкатенации, равна nvarchar2, в результате чего аргумент execute immediate недействителен.

Не стоит создавать таблицы на лету, хотя. Объекты схемы обычно должны создаваться один раз. У вас может быть таблица - возможно, глобальная таблица временных таблиц - с столбцом месяца или с помощью двенадцати таблиц push, которые вы опустошаете и заполняете по мере необходимости.

Кроме того, все ваши select from dual заявления могут быть упрощены:

curMonthChar := TO_CHAR(ADD_MONTHS(sysdate,-1),'fmMONTH'); 
    curTableName := 'QP17414_'||curMonthChar); 

и т.д., даже в вашем подзапрос:

... WHERE OWNER = user AND ... 

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

+1

[manual] (http://docs.oracle.com/cd/E11882_01/appdev.112/e25519/executeimmediate_statement.htm#LNPLS1201) не объясняет, почему он говорит только динамическую строку SQL «... должен быть CHAR, VARCHAR2 или CLOB. " –

+0

@jonearles - thanks - У меня был этот раздел, открытый на другой вкладке, и он все еще не мог его увидеть. Я добавил ссылки doc для конкатенации, создавая общую динамическую строку 'nvarchar2', которая также не была сразу очевидна. –

+0

Спасибо, Алекс, это была проблема. Это мой первый реальный набег на PL/SQL после переключения с SQL Server, поэтому мне стало немного поучиться.Конкатенация в моем стержне является результатом многих шагов, которые я предпринял, чтобы попытаться выяснить, что было «недействительным» в отношении моего SQL. Я думал, что я должен неправильно настроить поворот. Благодарим вас за упрощение предложений. Что касается использования nvarchar2, я должен это сделать, чтобы разработать утомленную дурную привычку не уделять достаточно пристального внимания использованию правильных типов данных для этой необходимости. – Emma

Смежные вопросы