Oracle - PL/SQL - 使用 SELECT 作为 VALUES 插入所有

Posted

技术标签:

【中文标题】Oracle - PL/SQL - 使用 SELECT 作为 VALUES 插入所有【英文标题】:Oracle - PL/SQL - INSERT ALL with SELECT as VALUES 【发布时间】:2015-09-21 13:15:16 【问题描述】:

我有一个关于用于将 3 个表关联起来的代码的问题,从 XML 文件开始。

这 3 个表是 Organisation、Publication 和 Fact 表,以便将它们联系起来。

表格结构下方:

PUBLICATION
------------
CD_PUB (primary key, progressive number automatically generated when a new record has been inserted, thus the value is not taken from the XML file)
CD_REC 

ORGANISATION
------------
CD_ORG (primary key, progressive number automatically generated when a new record has been inserted, thus the value is not taken from the XML file)

FT_PUB_ORG
------------
CD_FT (primary key, progressive number automatically generated when a new record has been inserted, thus the value is not taken from the XML file)
CD_ORG (I want to insert here the related code of the Organisation)

我使用的代码是:

BEGIN
  FOR i IN 1..10 LOOP



 INSERT INTO WOS_DM_PUBLICATION (
                                CD_UID 
                              , DT_COVERDATE 
                              , FL_HAS_ABSTRACT 
                              , QT_ISSUE 
                              , DT_PUBMONTH 
                              , CD_PUBTYPE 
                              , DT_PUBYEAR 
                              , DT_SORTDATE 
                              , QT_VOL 
                              , CD_PAGE_BEGIN 
                              , CD_PAGE_END 
                              , CD_PAGE 
                              , QT_PAGE_COUNT 
                              , QT_TITLE_COUNT 
                              , LB_TITLE_SOURCE 
                              , LB_TITLE_SERIES 
                              , LB_TITLE_SOURCE_ABBREV 
                              , LB_TITLE_ABBREV_ISO 
                              , LB_TITLE_ABBREV_11 
                              , LB_TITLE_ABBREV_29 
                              , LB_TITLE_ITEM                
                              , LB_TITLE_BOOK_SERIES 
                              , CD_ACCESSION_NO 
                              , CD_LANG_TYPE 
                              , CD_LANG_NORM 
                              , CD_IDS 
                              , FL_IDS_AVAIL 
                              , CD_BIB_ID 
                              , CD_BIB_PAGECOUNT                                 
                            )


                   select 
                             RecUid.cd_uid
                           , PubInfo.*
                           , Titles.*
                           , Title.*
                           , Acc_no.*
                           , Lang.*
                           , Lang2.*
                           , Items.*

                    from testtable2 t
                    cross join  xmltable(xmlnamespaces(default 'xxxxxxxxxxxxxxxx'),
                     'records/REC'
                      passing t.xml_file 
                      columns 
                              CD_UID varchar2(200) path 'UID',
                                     names xmltype path 'static_data/summary',
                                  identifi xmltype path 'dynamic_data/cluster_related',
                                      lang xmltype path 'static_data/fullrecord_metadata',
                                      item xmltype path 'static_data/item'
                        ) RecUid

                      cross join  xmltable(xmlnamespaces(default 'xxxxxxxxxxxxxxxx'),
                     'summary/pub_info'
                      passing RecUid.names
                      columns                           
                           DT_COVERDATE varchar2(20) path '@coverdate',
                        FL_HAS_ABSTRACT varchar2(20) path '@has_abstract',
                               QT_ISSUE varchar2(20) path '@issue',
                            DT_PUBMONTH varchar2(20) path '@pubmonth',
                             CD_PUBTYPE varchar2(20) path '@pubtype',
                             DT_PUBYEAR varchar2(20) path '@pubyear',
                            DT_SORTDATE varchar2(20) path '@sortdate',
                                 QT_VOL varchar2(20) path '@vol',
                          CD_PAGE_BEGIN varchar2(20) path 'page/@begin',
                            CD_PAGE_END varchar2(20) path 'page/@end',
                                CD_PAGE varchar2(20) path 'page',
                          CD_PAGE_COUNT varchar2(20) path 'page/@page_count'    
                        ) PubInfo

                    cross join  xmltable(xmlnamespaces(default 'xxxxxxxxxxxxxxxx'),
                     'summary/titles'
                       passing RecUid.names
                      columns     
                         QT_TITLE_COUNT varchar2(20) path '@count'
                       ) Titles

                    cross join  xmltable(xmlnamespaces(default 'xxxxxxxxxxxxxxxx'),
                     'summary/titles'
                       passing RecUid.names
                      columns                                 
                               LB_TITLE_SOURCE varchar2(200) path 'title[@type="source"]',
                               LB_TITLE_SERIES varchar2(200) path 'title[@type="series"]',
                        LB_TITLE_SOURCE_ABBREV varchar2(200) path 'title[@type="source_abbrev"]',
                           LB_TITLE_ABBREV_ISO varchar2(200) path 'title[@type="abbrev_iso"]',
                            LB_TITLE_ABBREV_11 varchar2(200) path 'title[@type="abbrev_11"]',
                            LB_TITLE_ABBREV_29 varchar2(200) path 'title[@type="abbrev_29"]',
                                 LB_TITLE_ITEM varchar2(200) path 'title[@type="item"]',
                          LB_TITLE_BOOK_SERIES varchar2(200) path 'title[@type="book_series"]'
                       ) Title

                    cross join  xmltable( xmlnamespaces(default 'xxxxxxxxxxxxxxxx'),
                     'cluster_related/identifiers/identifier[@type="accession_no"]'
                      passing RecUid.identifi
                      columns 
                         CD_ACCESSION_NO varchar2(200) path '@value'
                        )   Acc_no   

                    cross join  xmltable(xmlnamespaces(default 'xxxxxxxxxxxxxxxx'),
                     'fullrecord_metadata/languages/language[@type="primary"]'
                      passing RecUid.lang
                      columns 
                         CD_LANG_TYPE varchar2(200) path '.'
                         --CD_LANG_NORM varchar2(200) path 'normalized_languages/language'
                        )   Lang

                    cross join  xmltable(xmlnamespaces(default 'xxxxxxxxxxxxxxxx'),
                     'fullrecord_metadata/normalized_languages/language[@type="primary"]'
                      passing RecUid.lang
                      columns 
                         CD_LANG_NORM varchar2(200) path '.'
                        )   Lang2

                    cross join  xmltable(xmlnamespaces(default 'xxxxxxxxxxxxxxxx'),
                     'item'
                      passing RecUid.item
                      columns 
                              CD_IDS VARCHAR(200) path 'ids',
                        FL_IDS_AVAIL VARCHAR(2)   path 'ids/@avail',
                           CD_BIB_ID VARCHAR(50)  path 'bib_id',
                    CD_BIB_PAGECOUNT VARCHAR(25)  path 'bib_pagecount'

                      )   Items  ;

      INSERT     INTO  WOS_DM_ORGANISATION (
                                      LB_LEGAL_NAME 
                                    ,   CD_ADD_NO 
                                    ,   LB_FULL_ADDRESS 
                                    ,   CD_CITY 
                                    ,   CD_STATE 
                                    ,   CD_COUNTRY_NAME 
                         --           , CD_POSTAL_CODE 
                                    ,   CD_ZIP_LOCATION 
                                    , FL_PARENT
                                       )

                          select                              
                                    Organis.* 
                                 ,  'Y' FL_PARENT 

                          from testtable2 t
                          cross join  xmltable(xmlnamespaces(default 'xxxxxxxxxxxxxxxx'),
                           'records/REC/static_data/fullrecord_metadata/addresses/address_name'
                            passing t.xml_file 
                            columns 

                               LB_LEGAL_NAME varchar2(200) path 'address_spec/organizations/organization',
                              CD_ADD_NO varchar2(200) path 'address_spec/@addr_no',
                               LB_FULL_ADDRESS varchar2(200) path 'address_spec/full_address',
                               CD_CITY varchar2(200) path 'address_spec/city',
                               CD_STATE varchar2(200) path 'address_spec/state',  
                               CD_COUNTRY_NAME varchar2(200) path 'address_spec/country',
                              -- CD_POSTAL_CODE varchar2(200) path 'address_spec/zip/.'--,
                               CD_ZIP_LOCAZION varchar2(200) path 'address_spec/@location'
                               )   Organis ;
  INSERT INTO FT_PUB_ORG_TEST

                (  
                   CD_PUB_ID ,
                   CD_ORG_ID )
                VALUES
                (

                WOS_DM_PUBLICATION_id_seq.currval,
                WOS_DM_ORGANISATION_id_seq.currval);

  x := x + 1;
END LOOP;
   COMMIT;
     END;                  

问题在于,使用此代码,我只有 FT 表中的最终值,但我想要所有关系。 我已经尝试过使用 INSERT ALL 语句,但它似乎不适用于 SELECT 作为值。

请考虑循环只是一次尝试,但它不能按我的需要工作。

【问题讨论】:

我相信您是说insertWOS_DM_PUBLICATION 使用触发器和WOS_DM_PUBLICATION_id_seq 序列插入n 行,主键为CD_PUB_ID。类似地,insertWOS_DM_ORGANISATION 使用触发器和 WOS_DM_ORGANISATION_id_seq 序列准确插入 n 行,主键为 CD_ORG_ID。在两个插入运行后,对于 i 介于 1 和 n 之间,您希望将一行插入到 FT_PUB_ORG_TEST 中,主键来自前两个 insert 语句中的每一个的第 i 个键。对吗? 您似乎不太可能真的希望根据仓位顺序匹配两个 insert 操作中的行。当然,在您的两个语句中都添加 order by 子句。我希望您真的想通过其他键匹配数据。但我不知道那把钥匙是什么。 @JustinCave 我确认你的猜测。你知道代码的正确性吗? 好的。所以你是说你真的想依靠两个select 语句返回的行的任意顺序来确定要加入哪些行?没有order by,那是非常危险的。除非出于某种原因,只要每一行都被映射,您不关心映射表中哪些行被相互映射? @JustinCave 我明白你的意思......我们可以使用 CD_UID 作为两个表之间的链接。考虑到 FT_PUB_ORG_TEST 表中的 CD_UID 字段,您是否知道如何更正我的陈述? 【参考方案1】:

听起来你想创建一个集合类型

CREATE TYPE num_tbl
    AS TABLE OF NUMBER;

在您的 PL/SQL 块中,您需要声明几个集合

DECLARE
  l_publication_keys num_tbl;
  l_organization_keys num_tbl;
BEGIN
  ...

然后,您的 INSERT 语句将使用 RETURNING 子句将生成的主键加载到集合中

INSERT INTO WOS_DM_PUBLICATION( <<columns>> )
  SELECT <<whatever>>
    FROM <<whatever>>
   ORDER BY <<something>>
  RETURNING cd_pub_id 
       BULK COLLECT INTO l_publication_keys;

您可以使用WOS_DM_ORGANISATION insert 语句做同样的事情。然后,您可以通过遍历集合插入到映射表中

FOR j in 1 .. l_publication_keys.count
LOOP
  INSERT INTO FT_PUB_ORG_TEST (  
                   CD_PUB_ID ,
                   CD_ORG_ID )
                VALUES (
                   l_publication_keys(j),
                   l_organization_keys(j)
                );
END LOOP;

这假定两个集合将始终具有完全相同数量的元素,并且您始终希望匹配每个 insert 语句中的第 j 行。除非您没有提及您的数据,否则这似乎很冒险。

如果您想使用cd_uid 列来关联两个集合中的数据,您可能需要为您的集合创建一个对象类型。

CREATE TYPE uid_obj
  AS OBJECT (
    cd_uid integer, -- I'm guessing at the data type
    key    integer
);

CREATE TYPE uid_tbl
  AS TABLE OF uid_obj;

您的局部变量将成为新类型的集合

DECLARE
  l_publication_keys uid_tbl;
  l_organization_keys uid_tbl;
BEGIN

您的 RETURNING 子句将更改为

RETURNING uid_obj( cd_uid, cd_pub_id )
     BULK COLLECT INTO l_publication_keys;

然后最后一个INSERT 可能是这样的

INSERT INTO FT_PUB_ORG_TEST (  
                       CD_PUB_ID ,
                       CD_ORG_ID )
  SELECT p.key,
         o.key
    FROM TABLE( l_publication_keys ) p
         JOIN TABLE( l_organization_keys ) o
           ON( p.uid_cd = o.uid_cd );

【讨论】:

感谢您的详细回答。可能我之前误解了,但是当你说每个 Pub 和每个 Org 有一行很奇怪时,你是对的,实际上每个 Pub 都有更多的 Org。因此,一个出版物只能有一个 CD_UID,但可以有多个组织。你的代码能正常工作吗? @newinIT - 使用uid_obj 对象的第二种方法应该可以工作。假设我已经理解了您的所有要求,这是一个相当大的假设。将来,发布一个可重现的测试用例(一个简化的问题,包括(简化的)表定义、数据和我们需要复制问题的代码以及所需的输出将非常有助于澄清需求。否则,我们将留下猜测或者我们必须在cmets中来回走动(这是有限的) 非常感谢您的耐心和解释,我一直在尝试让代码工作但没有成功...我有以下错误:PL/SQL: ORA-00933: SQL command not properly ended - PLS-00103: Encountered the symbol "UID_OBJ" when expecting one of the following: := . ( @ % ; The symbol ":=" was substituted for "UID_OBJ" to continue. PLS-00103: Encountered the symbol "BULK" when expecting one of the following:我真的不明白在哪里问题是。

以上是关于Oracle - PL/SQL - 使用 SELECT 作为 VALUES 插入所有的主要内容,如果未能解决你的问题,请参考以下文章

oracle pl/sql 基础

Oracle PL/SQL语句基础学习笔记(上)

oracle pl/sql 简介

Oracle——PL/SQL

oracle pl/sql基本语法

PL/SQl,oracle 9i,使用sql删除重复行