我想通过定义如何读取和拆分字符串来动态查询分段为长字符串的数据。
所以我可以使用以下元素定义数据
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)
此表中的示例数据
我可以利用该信息来创建一个类似于
的选择吗?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;
现在开始解决如何动态创建关联数组以将数据填充到数据中或使用另一种方式处理数据的问题。
答案 0 :(得分:0)
在您的示例中,您使用了CASE
语句。您的第一个表达式具有DATE
数据类型,第二个表达式具有NUMBER
,第三个表达式是VARCHAR2
。从文档中:
对于简单的CASE表达式,expr和所有compare_expr值 必须具有相同的数据类型,或者必须全部具有 数字数据类型。
基本上,您无法执行此操作,因为无法在编译时知道field_name
列的数据类型。
这不是要解决的简单问题,因为直到运行时您都不知道数据类型是什么。即使您获得了动态SQL语句,您仍将选择哪种变量将数据放入?
我认为您基本上必须:
column_definitions
,构造一个字符串,其中包含适用于所讨论数据类型的SQL语句。TYPE
,其中包含所有可能的结果数据类型的成员。EXECUTE IMMEDIATE
或DBMS_SQL
解析并执行该字符串,然后将结果提取到该类型的实例中。实际上最好不要完全通过SQL执行此操作。相反,我可能会执行以下操作:
column_definitions
获取感兴趣的数据类型。SUBSTR
从staged_data中的字符串中提取感兴趣的区域。。
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;
我不希望这种方法具有高性能。