2013-05-07 5 views
0

Je dois charger un très gros fichier xml dans sql server 2008 r2. Le fichier ressemble à:Charger un gros fichier xml dans le serveur sql

<?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> 

je la structure de tableau suivant:

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) 

J'accepte toute solution, puisque je dois l'utiliser dans .net. J'ai trouvé quelques exemples qui utilisent la classe sqlbulkcopy, mais je ne peux pas l'utiliser avec une classe xmlReader, qui est la bonne pour ce cas.

+0

Dans quel genre de structure de la table vous allez stocker? – tucaz

+0

Avez-vous regardé l'objet [SQLXMLBulkLoad] (http://msdn.microsoft.com/en-us/library/ms171993.aspx)? Il existe des instructions spécifiques pour l'utiliser avec .NET [ici] (http://msdn.microsoft.com/en-us/library/ms171878.aspx). – Pondlife

+0

Table cupomVenda, champ idCupomVenda (incrément automatique) plus les mêmes champs en XML, dans l'ordre respectif de type: int, int, date, varchar (20), int, smallint, bit, varchar (80), char (14). Et aussi je vais stocker dans la table Item, avec IdItemCupomVenda (incrément automatique), plus les types de champs dans le même ordre que les champs dans xml: int, int, int, float, float. Plus idCupomVenda (int), pour faire la relation entre eux deux. Merci de votre aide! –

Répondre

0

Cette requête devrait vous aider à démarrer.

La clé est de détruire votre fichier XML. Mais puisque vous insérez dans une table avec une IDENTITÉ, vous devez utiliser des "clés naturelles" pour revenir à la "clé de substitution IDENTITY". Donc, tant que votre "codFilial" (que j'appelle le "codFilialNaturalKey") est une contrainte unique, vous serez en mesure de revenir sur cela pour faire correspondre le PK de la table parente au FK de la table des enfants.

J'ai fait quelques colonnes de données pour vous (ce qui signifie aussi que je n'ai pas fait toutes les colonnes pour vous). Et ajouté des éléments "item" supplémentaires pour montrer que cela fonctionne pour plusieurs "items".

Il existe également un convertisseur DataTime DateTime (UDF). Vous voudrez tirer votre valeur en tant que varchar et ALORS l'exécuter à travers le UDF pour convertir en un 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 

Voici une UDF pour convertir un DataSet DateTime à une valeur SqlServer:

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 
+1

Merci beaucoup @granadaCoder! Je pense que c'est exactement ce dont j'ai besoin, j'ai juste un doute de plus: où vous avez 'SET @data = N'', comment puis-je faire pour utiliser le chemin de mon fichier xml? Parce qu'ils ont une taille vraiment énorme, je ne peux pas ouvrir ensuite pour copier leur texte sur le serveur SQL. –

+0

Je ne l'ai jamais fait comme ça. Je mets le code ci-dessus dans une procédure stockée, et appelle la procédure stockée à partir du code DotNet. Aka, mon 'xml' était 'en mémoire', pas un fichier sur un disque.Vous pouvez donc soit placer votre fichier dans une table d'attente sur le serveur sql et le lire à partir de là, soit lire le fichier dans votre flux de fichiers et l'abaisser de cette façon. Une suggestion de plus ... une ligne à la fois est trop petite, votre fichier entier peut être trop grand, vous pouvez vouloir "chunk it" ... voici une démo un peu: http://granadacoder.wordpress.com/2009/01/27/bulk-insert-example-using-un-idatareader-to-strong-dataset-to-sql-server-xml/ – granadaCoder

+0

"Très grand" est relatif. J'ai fait xml .... que (s'il devait être écrit dans un fichier sur le disque), serait d'environ 4 Mo. Tu dois juste essayer le tien et voir ce qu'il se passe. – granadaCoder

Questions connexes