Динамически генерировать SQL-оператор на основе метаданных о запрашиваемых данных - PullRequest
0 голосов
/ 23 мая 2019

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

Таким образом, я могу определить данные со следующими элементами

FIELD_NAME              VARCHAR2(30)          NOT NULL,
DATA_TYPE               VARCHAR2(20)          NOT NULL,
COLUMN_ID               NUMBER                NOT NULL,
FIELD_START_POS         NUMBER,
FIELD_END_POS           NUMBER,
FIELD_LEN               NUMBER,
ROW_TYPE                VARCHAR2(10),
DATE_MASK               VARCHAR2(12)

пример данных в этой таблице

enter image description here

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

SELECT CASE cd.data_type
           WHEN 'DATE'
           THEN
               TO_DATE (SUBSTR (sd.source_text, cd.field_start_pos, cd.field_len), cd.date_mask)
           WHEN 'NUMBER'
           THEN
               TO_NUMBER (SUBSTR (sd.source_text, cd.field_start_pos, cd.field_len))
           ELSE
               TRIM (SUBSTR (sd.source_text, cd.field_start_pos, cd.field_len))
       END
           AS cd.field_name
  FROM staged_data sd, column_definitions cd

У меня возникают трудности при попытке связать 2 вместе.

Я знаю, что мог бы повернуть имена столбцов в определении следующим образом:

SELECT *
  FROM column_definitions 
  PIVOT (max(field_name) FOR column_id IN (1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20))

, но это все равно приводит кво многих строках

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

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


Итак, я былв состоянии генерировать строку, которую я ищу, на основе метаданных о подготовленном файле, например:

DECLARE
    select_items   VARCHAR2 (4000);
BEGIN
    FOR c IN (  SELECT *
                  FROM column_definitions
                 WHERE file_pk = 1 AND row_type = 1
              ORDER BY column_id)
    LOOP
        IF c.data_type = 'NUMBER'
        THEN
            select_items :=
                   select_items
                || 'CASE WHEN is_number(SUBSTR(row_data,'
                || c.field_start_pos
                || ','
                || c.field_len
                || ')) = ''TRUE'' THEN TO_NUMBER(SUBSTR(row_data,'
                || c.field_start_pos
                || ','
                || c.field_len
                || ')) ELSE NULL END AS '
                || c.field_name
                || ',';
        ELSIF c.data_type = 'DATE'
        THEN
            select_items :=
                   select_items
                || 'CASE WHEN ISDATE(SUBSTR(row_data,'
                || c.field_start_pos
                || ','
                || c.field_len
                || '))=''true'' THEN TO_DATE(SUBSTR(row_data,'
                || c.field_start_pos
                || ','
                || c.field_len
                || '),'''
                || c.date_mask
                || ''') ELSE NULL END AS '
                || c.field_name
                || ',';
        ELSE
            select_items :=
                   select_items
                || 'TRIM(SUBSTR(row_data,'
                || c.field_start_pos
                || ','
                || c.field_len
                || ')) AS '
                || c.field_name
                || ',';
        END IF;
    END LOOP;

    select_items := SUBSTR (select_items, 1, LENGTH (select_items) - 1);

    select_items :=
           'SELECT '
        || select_items
        || ' FROM STAGED_FILE where row_type=1 AND rownum <= 1000;';

    DBMS_OUTPUT.PUT_LINE (select_items);
END;

, это выдает что-то вроде этого:

SELECT CASE
           WHEN is_number (SUBSTR (row_data, 1, 1)) = 'TRUE'
           THEN
               TO_NUMBER (SUBSTR (row_data, 1, 1))
           ELSE
               NULL
       END
           AS REC_TYPE_IND,
       SUBSTR (row_data, 11, 4)   AS SRVC_LOC,
       CASE
           WHEN ISDATE (SUBSTR (row_data, 15, 8)) = 'true'
           THEN
               TO_DATE (SUBSTR (row_data, 15, 8), 'YYYYMMDD')
           ELSE
               NULL
       END
           AS BEGIN_DT,
       CASE
           WHEN ISDATE (SUBSTR (row_data, 23, 8)) = 'true'
           THEN
               TO_DATE (SUBSTR (row_data, 23, 8), 'YYYYMMDD')
           ELSE
               NULL
       END
           AS END_DT,
       SUBSTR (row_data, 31, 50)  AS ID,
       SUBSTR (row_data, 101, 2)  AS COUNTY_CD,
       SUBSTR (row_data, 103, 30) AS ADDR_LN_1,
       SUBSTR (row_data, 133, 30) AS ADDR_LN_2,
       SUBSTR (row_data, 163, 18) AS CITY,
       SUBSTR (row_data, 181, 2)  AS STATE_CD,
       CASE
           WHEN is_number (SUBSTR (row_data, 183, 5)) = 'TRUE'
           THEN
               TO_NUMBER (SUBSTR (row_data, 183, 5))
           ELSE
               NULL
       END
           AS ZIP_CD,
       CASE
           WHEN is_number (SUBSTR (row_data, 188, 4)) = 'TRUE'
           THEN
               TO_NUMBER (SUBSTR (row_data, 188, 4))
           ELSE
               NULL
       END
           AS ZIP_CD4,
       CASE
           WHEN is_number (SUBSTR (row_data, 192, 10)) = 'TRUE'
           THEN
               TO_NUMBER (SUBSTR (row_data, 192, 10))
           ELSE
               NULL
       END
           AS PHONE_NUM
  FROM staged_FILE
 WHERE row_type = 1 AND ROWNUM <= 1000;

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

1 Ответ

0 голосов
/ 23 мая 2019

В вашем примере вы используете оператор CASE.Ваше первое выражение имеет тип данных DATE, второе - NUMBER, а третье - VARCHAR2.Из документации:

Для простого выражения CASE выражения expr и все значения сравнения_expr должны иметь одинаковый тип данных или все должны иметь числовой тип данных.

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

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

Я думаю, что вам, в основном, придется:

  1. Используя column_definitions, создайте строку , которая содержит инструкцию SQL, соответствующую данному типу данных.
  2. Создайте TYPE, который содержит члены всех возможных результатов.типы данных.
  3. Используйте либо EXECUTE IMMEDIATE, либо DBMS_SQL для анализа и выполнения этой строки, а затем извлеките результат в экземпляр этого типа.

Вы можете быть лучшене делать это через SQL на всех.Вместо этого я, вероятно, сделал бы следующее:

  1. Получить интересующий тип данных из column_definitions.
  2. Используйте SUBSTR, чтобы извлечь интересующую область из строки в staged_data.
  3. Сделайте что-то вроде:

.

l_token := SUBSTR (sd.source_text, cd.field_start_pos, cd.field_len);
IF l_datatype = 'DATE' THEN
    l_date := TO_DATE( l_token, 'yyyy-mm-dd' );
ELSIF l_datatype = 'NUMBER' THEN
    l_number := TO_NUMBER( l_token);
....
END IF;

Я бы не ожидал высокой производительности от такого подхода.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...