0

我必须在 sql server 2008 r2 中加载一个非常大的 xml 文件。该文件如下所示:

<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?>
<listaCupons xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <cupomVenda>
        <codFilial>123456</codFilial>
        <dtVenda>2013-01-01T00:00:00</dtVenda>
        <numeroDePdv>0.000000000000000</numeroDePdv>
        <cupomFiscal>12345</cupomFiscal>
        <horaVenda xsi:nil="true"/>
        <tipoVenda>1</tipoVenda>
        <vendedorVip>FUlano de tal</vendedorVip>
        <cpfCnpjAdquirente xsi:nil="true"/>
        <item>
            <numeroDoItem>1</numeroDoItem>
            <codigoDoProduto>2134</codigoDoProduto>
            <qtde>1</qtde>
            <valorUnitario>399.000</valorUnitario>
            <ultimoCusto>216.150</ultimoCusto>
        </item>
    </cupomVenda>
</listaCupons>

我有以下表结构:

Table CupomVenda
idCupomVenda int auto increment
codFilial int
dtVenda date
numeroDePdv varchar (20)
cupomFiscal int
horaVenda smallint
tipoVenda bit
vendedorVip varchar(80)
cpfCnpjAdquirente char(14)

Table ItemCupomVenda (child from CupomVenda)

idItemCupomVenda int auto Increment
numeroDoItem int
codigoDoProduto int
qtde int
valorUnitario float
ultimoCusto float
idCupomVenda int (relation with primary key from CupomVenda)

我接受任何解决方案,因为我必须在.net 中使用它。我发现了一些使用 sqlbulkcopy 类的示例,但是我不能将它与 xmlReader 类一起使用,我想这对于这种情况来说是正确的。

4

1 回答 1

0

这个查询应该让你离开地面。

关键是粉碎你的 Xml。但是由于您要插入带有 IDENTITY 的表,因此您必须使用“自然键”来返回“代理 IDENTITY 键”。

因此,只要您的“codFilial”(我称之为“codFilialNaturalKey”)是一个唯一约束,您就可以背负它以将父表的 PK 与子表的 FK 匹配。

我为你做了几列数据(这也意味着我没有为你做每一列)。并添加了额外的“项目”元素,以显示它适用于多个“项目”。

还列出了一个 DataSet DateTime 转换器 (UDF)。您需要将您的值作为 varchar 提取,然后通过 UDF 运行它以转换为 DateTime。

IF OBJECT_ID('tempdb..#DestinationCupomVendaParentTable') IS NOT NULL
begin
        drop table #DestinationCupomVendaParentTable
end



IF OBJECT_ID('tempdb..#DestinationItemCupomVendaChildTable') IS NOT NULL
begin
        drop table #DestinationItemCupomVendaChildTable
end


CREATE TABLE #DestinationCupomVendaParentTable
(
CupomVendaParentSurrogateIdentityKey int not null identity (1001, 1), 
codFilialNaturalKey int, 
tipoVenda int
)



CREATE TABLE #DestinationItemCupomVendaChildTable
(
DestinationChildSurrogateIdentityKey int not null identity (3001, 1), 
CupomVendaParentSurrogateIdentityKeyFK int, 
numeroDoItemNaturalKey int,
codigoDoProduto int
)





-- Declare XML variable

DECLARE @data XML;

-- Element-centered XML

SET @data = N'
<listaCupons xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <cupomVenda>
        <codFilial>123456</codFilial>
        <dtVenda>2013-01-01T00:00:00</dtVenda>
        <numeroDePdv>0.000000000000000</numeroDePdv>
        <cupomFiscal>12345</cupomFiscal>
        <horaVenda xsi:nil="true"/>
        <tipoVenda>1</tipoVenda>
        <vendedorVip>FUlano de tal</vendedorVip>
        <cpfCnpjAdquirente xsi:nil="true"/>
        <item>
            <numeroDoItem>9001</numeroDoItem>
            <codigoDoProduto>2134</codigoDoProduto>
            <qtde>1</qtde>
            <valorUnitario>399.000</valorUnitario>
            <ultimoCusto>216.150</ultimoCusto>
        </item>

        <item>
            <numeroDoItem>9002</numeroDoItem>
            <codigoDoProduto>32134</codigoDoProduto>
            <qtde>1</qtde>
            <valorUnitario>399.000</valorUnitario>
            <ultimoCusto>216.150</ultimoCusto>
        </item>
    </cupomVenda>


    <cupomVenda>
        <codFilial>234567</codFilial>
        <dtVenda>2013-01-01T00:00:00</dtVenda>
        <numeroDePdv>0.000000000000000</numeroDePdv>
        <cupomFiscal>23456</cupomFiscal>
        <horaVenda xsi:nil="true"/>
        <tipoVenda>4</tipoVenda>
        <vendedorVip>FUlano de tal</vendedorVip>
        <cpfCnpjAdquirente xsi:nil="true"/>
        <item>
            <numeroDoItem>9011</numeroDoItem>
            <codigoDoProduto>3256</codigoDoProduto>
            <qtde>4</qtde>
            <valorUnitario>333.44</valorUnitario>
            <ultimoCusto>333.55</ultimoCusto>
        </item>

        <item>
            <numeroDoItem>9013</numeroDoItem>
            <codigoDoProduto>33256</codigoDoProduto>
            <qtde>4</qtde>
            <valorUnitario>333.44</valorUnitario>
            <ultimoCusto>333.55</ultimoCusto>
        </item>
    </cupomVenda>


</listaCupons>

';




INSERT INTO #DestinationCupomVendaParentTable ( codFilialNaturalKey ,  tipoVenda )
SELECT T.parentEntity.value('(codFilial)[1]', 'INT') AS codFilial,
       T.parentEntity.value('(tipoVenda)[1]', 'INT') AS tipoVenda
FROM @data.nodes('listaCupons/cupomVenda') AS T(parentEntity)
/* add a where not exists check on the natural key */
where not exists (
    select null from #DestinationCupomVendaParentTable innerRealTable where innerRealTable.codFilialNaturalKey = T.parentEntity.value('(codFilial)[1]', 'INT') )
;

/* Optional.  You could do a UPDATE here based on matching the #DestinationCupomVendaParentTablecodFilialNaturalKey = T.parentEntity.value('(codFilial)[1]', 'INT')
You could Combine INSERT and UPDATE using the MERGE function on 2008 or later.
 */



INSERT INTO #DestinationItemCupomVendaChildTable (  CupomVendaParentSurrogateIdentityKeyFK ,  numeroDoItemNaturalKey , codigoDoProduto )
SELECT  par.CupomVendaParentSurrogateIdentityKey , 
        T.childEntity.value('(numeroDoItem)[1]', 'INT') AS numeroDoItem,
        T.childEntity.value('(codigoDoProduto)[1]', 'INT') AS codigoDoProduto
FROM @data.nodes('listaCupons/cupomVenda/item') AS T(childEntity)
/* The next join is the "trick".  Join on the natural key (codFilial)....**BUT** insert the CupomVendaParentSurrogateIdentityKey into the table */
join #DestinationCupomVendaParentTable par on par.codFilialNaturalKey = T.childEntity.value('(../codFilial)[1]', 'INT')
where not exists (
    select null from #DestinationItemCupomVendaChildTable innerRealTable where innerRealTable.CupomVendaParentSurrogateIdentityKeyFK = par.CupomVendaParentSurrogateIdentityKey AND  innerRealTable.numeroDoItemNaturalKey = T.childEntity.value('(numeroDoItem)[1]', 'INT'))
;



print '/#DestinationCupomVendaParentTable/'
select * from #DestinationCupomVendaParentTable


print '/#DestinationItemCupomVendaChildTable/'
select * from #DestinationItemCupomVendaChildTable


select codFilialNaturalKey , tipoVenda , numeroDoItemNaturalKey , codigoDoProduto , par.CupomVendaParentSurrogateIdentityKey as ParentPK , child.CupomVendaParentSurrogateIdentityKeyFK as childFK from #DestinationCupomVendaParentTable par join #DestinationItemCupomVendaChildTable child
on par.CupomVendaParentSurrogateIdentityKey = child.CupomVendaParentSurrogateIdentityKeyFK



IF OBJECT_ID('tempdb..#DestinationCupomVendaParentTable') IS NOT NULL
begin
        drop table #DestinationCupomVendaParentTable
end


IF OBJECT_ID('tempdb..#DestinationItemCupomVendaChildTable') IS NOT NULL
begin
        drop table #DestinationItemCupomVendaChildTable
end

这是一个将 DataSet DateTime 转换为 SqlServer 值的 UDF:

if exists (select * from sysobjects where id = object_id('udfConvertXmlDateToTsqlDate') and xtype = 'FN')
    drop function udfConvertXmlDateToTsqlDate
GO


CREATE FUNCTION dbo.udfConvertXmlDateToTsqlDate (@input_xml_date varchar(64))

/*


Original Need : When adding a value to a DataSet/datetime column,
                the DataSet stores the date as an xml formatted date
                TSQL does not like xml formatted dates
                This procedure will transfer a xml formatted date,
                into a datetime tsql datatype.
Sample Usage :

        select dbo.udfConvertXmlDateToTsqlDate ('2002-06-20T00:00:00.0000000+05:30') as myConvertedDate
        will yield:
            myConvertedDate                                        
            ------------------------------------------------------ 
            2002-06-20 00:00:00.000


        DateTime.MinValue Test

        select dbo.udfConvertXmlDateToTsqlDate ('0001-01-01T00:00:00-05:00') as myConvertedDate



Notes : 
            The procedure strips out the time part of the datetime.

*/


RETURNS 


datetime

AS
BEGIN


        --This is a DotNet/Xml/DataSet and DateTime.MinValue work around
        if LEFT(@input_xml_date,16)  = '0001-01-01T00:00' --  :00-05:00'
            BEGIN
                return null 
            END



    RETURN 

        --CONVERT(datetime , LEFT(@input_xml_date , (CHARINDEX('T', @input_xml_date))-1))
        CONVERT(datetime , LEFT(CONVERT(nvarchar(4000), @input_xml_date, 126), 10))

        -- SEE "Data Type Coercions" (SQL Server 2000 Books Online)
        -- this has the code above as the translation mechanism
        -- for converted an xml formatted datestamp into a TSQL datetime

END
GO


GRANT REFERENCES ON udfConvertXmlDateToTsqlDate TO public


GO
于 2013-05-07T22:16:07.280 回答