Najlepszy sposób na rozdrabnianie danych XML do kolumn bazy danych SQL Server

Jaki jest najlepszy sposób na rozdrabnianie danych XML do różnych kolumn bazy danych? Do tej pory używałem głównie węzłów i funkcji wartości jak tak:

INSERT INTO some_table (column1, column2, column3)
SELECT
Rows.n.value('(@column1)[1]', 'varchar(20)'),
Rows.n.value('(@column2)[1]', 'nvarchar(100)'),
Rows.n.value('(@column3)[1]', 'int'),
FROM @xml.nodes('//Rows') Rows(n)

Jednak uważam, że staje się to bardzo powolne nawet dla danych xml o umiarkowanych rozmiarach.

Author: eddiegroves, 2008-09-14

8 answers

Natknąłem się na to pytanie, mając bardzo podobny problem, uruchomiłem zapytanie Przetwarzające plik XML 7.5 MB (~około 10,000 węzłów) przez około 3.5~4 godziny, zanim w końcu się poddałem.

Jednak, po trochę więcej badań okazało się, że po wpisaniu XML za pomocą schematu i stworzył indeks XML (chciałbym masowo wstawiony do tabeli) to samo zapytanie zakończone w ~ 0.04 ms.

Jak to na poprawę wydajności!

Kod do utworzenia schemat:

IF EXISTS ( SELECT * FROM sys.xml_schema_collections where [name] = 'MyXmlSchema')
DROP XML SCHEMA COLLECTION [MyXmlSchema]
GO

DECLARE @MySchema XML
SET @MySchema = 
(
    SELECT * FROM OPENROWSET
    (
        BULK 'C:\Path\To\Schema\MySchema.xsd', SINGLE_CLOB 
    ) AS xmlData
)

CREATE XML SCHEMA COLLECTION [MyXmlSchema] AS @MySchema 
GO

Kod do utworzenia tabeli z wpisaną kolumną XML:

CREATE TABLE [dbo].[XmlFiles] (
    [Id] [uniqueidentifier] NOT NULL,

    -- Data from CV element 
    [Data] xml(CONTENT dbo.[MyXmlSchema]) NOT NULL,

CONSTRAINT [PK_XmlFiles] PRIMARY KEY NONCLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

Kod do utworzenia indeksu

CREATE PRIMARY XML INDEX PXML_Data
ON [dbo].[XmlFiles] (Data)
Należy jednak pamiętać o kilku rzeczach. Implementacja schematu SQL Server nie obsługuje xsd: include. Oznacza to, że jeśli masz schemat, który odwołuje się do innych schematów, będziesz musiał skopiować wszystkie z nich do jednego schematu i dodać, że.

Również dostałbym błąd:

XQuery [dbo.XmlFiles.Data.value()]: Cannot implicitly atomize or apply 'fn:data()' to complex content elements, found type 'xs:anyType' within inferred type 'element({http://www.mynamespace.fake/schemas}:SequenceNumber,xs:anyType) ?'.

Jeśli próbowałem poruszać się nad węzłem, który wybrałem za pomocą funkcja węzłów. Np.

SELECT
    ,C.value('CVElementId[1]', 'INT') AS [CVElementId]
    ,C.value('../SequenceNumber[1]', 'INT') AS [Level]
FROM 
    [dbo].[XmlFiles]
CROSS APPLY
    [Data].nodes('/CVSet/Level/CVElement') AS T(C)

Okazało się, że najlepszym sposobem na poradzenie sobie z tym było użycie zewnętrznego APPLY, aby w efekcie wykonać "zewnętrzne połączenie" w XML.

SELECT
    ,C.value('CVElementId[1]', 'INT') AS [CVElementId]
    ,B.value('SequenceNumber[1]', 'INT') AS [Level]
FROM 
    [dbo].[XmlFiles]
CROSS APPLY
    [Data].nodes('/CVSet/Level') AS T(B)
OUTER APPLY
    B.nodes ('CVElement') AS S(C)
Mam nadzieję, że to komuś pomoże, bo to był mój dzień.
 46
Author: Dan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-03 14:17:37

W moim przypadku używam SQL 2005 SP2 (9.0).

Pomogło tylko dodanie opcji (OPTIMIZE FOR (@your_xml_var = NULL)). Wyjaśnienie znajduje się pod linkiem poniżej.

Przykład:

INSERT INTO @tbl (Tbl_ID, Name, Value, ParamData)
SELECT     1,
    tbl.cols.value('name[1]', 'nvarchar(255)'),
    tbl.cols.value('value[1]', 'nvarchar(255)'),
    tbl.cols.query('./paramdata[1]')
FROM @xml.nodes('//root') as tbl(cols) OPTION ( OPTIMIZE FOR ( @xml = NULL ) )

Https://connect.microsoft.com/SQLServer/feedback/details/562092/an-insert-statement-using-xml-nodes-is-very-very-very-slow-in-sql2008-sp1

 5
Author: jccprj,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-16 01:04:46

Nie wiem, jaka jest najlepsza metoda. Użyłem konstrukcji OPENXML:

INSERT INTO Test
SELECT Id, Data 
FROM OPENXML (@XmlDocument, '/Root/blah',2)
WITH (Id   int         '@ID',
      Data varchar(10) '@DATA')

Aby go przyspieszyć, możesz tworzyć indeksy XML. Możesz ustawić indeks specjalnie dla wartość optymalizacja wydajności funkcji. Możesz również użyć wpisanych kolumn xml, które działają lepiej.

 3
Author: aku,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-14 10:29:15

Mieliśmy podobny problem tutaj. Nasz DBA (SP, you the man) przyjrzał się mojemu kodowi, trochę poprawił składnię i uzyskaliśmy prędkość, której oczekiwaliśmy. To było niezwykłe, ponieważ mój select z XML był dużo szybki, ale insert był bardzo powolny. Więc spróbuj zamiast tego tej składni:

INSERT INTO some_table (column1, column2, column3)
    SELECT 
        Rows.n.value(N'(@column1/text())[1]', 'varchar(20)'), 
        Rows.n.value(N'(@column2/text())[1]', 'nvarchar(100)'), 
        Rows.n.value(N'(@column3/text())[1]', 'int')
    FROM @xml.nodes('//Rows') Rows(n) 

Więc podanie parametru text() naprawdę wydaje się mieć wpływ na wydajność. Wzięliśmy naszą wstawkę z 2K wierszy z "musiałem napisać, że źle-pozwól mi to zatrzymać" do około 3 sekund. Które był 2x szybszy niż surowe instrukcje insert, które były uruchamiane przez połączenie.

 3
Author: edhubbell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-06-12 20:05:00

Nie twierdziłbym, że jest to "najlepsze" rozwiązanie, ale napisałem ogólną procedurę SQL CLR w tym celu-pobiera ona "tabelaryczną" strukturę Xml (taką jak ta zwracana przez XML RAW) i wyświetla resultset.

Nie wymaga dostosowania / znajomości struktury" tabeli " w Xml, a okazuje się być niezwykle szybki / wydajny (choć nie był to cel projektowy). Właśnie zniszczyłem zmienną xml 25MB (untyped) w niecałe 20 sekund, zwracając 25000 rzędy dość szerokiego stołu.

Mam nadzieję, że to komuś pomoże: http://architectshack.com/ClrXmlShredder.ashx

 2
Author: Tao,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-06-19 21:55:31

To nie jest odpowiedź, bardziej dodatek do tego pytania - właśnie natknąłem się na ten sam problem i mogę podać liczby, o które prosi edg w komentarzu.

Mój test ma xml, w wyniku którego wstawiono 244 rekordy - czyli 244 węzły.

Kod, który przepisuję, trwa średnio 0,4 sekundy.(10 testów uruchomionych, rozłożonych od .56 SEK .do344 sekund) wydajność nie jest głównym powodem przepisywania kodu, ale nowy kod musi działać równie dobrze lub lepiej. To stary kod zapętla węzły xml, wywołując sp, aby wstawić raz na pętlę

Nowy kod jest prawie tylko jednym sp; podaj xml w; zniszcz go.

Testy z włączonym kodem pokazują, że nowy sp trwa średnio 3,7 sekundy - prawie 10 razy wolniej.

Moje zapytanie jest w formularzu zamieszczonym w tym pytaniu;

INSERT INTO some_table (column1, column2, column3)
SELECT
Rows.n.value('(@column1)[1]', 'varchar(20)'),
Rows.n.value('(@column2)[1]', 'nvarchar(100)'),
Rows.n.value('(@column3)[1]', 'int'),
FROM @xml.nodes('//Rows') Rows(n)

Wydaje się, że plan wykonania pokazuje, że dla każdej kolumny sql server wykonuje oddzielną "funkcję wartości tabeli [XMLReader]" zwracającą wszystkie 244 wiersze, łączenie wszystkich kopii zapasowych za pomocą zagnieżdżonych pętli (Inner Join). Tak więc w moim przypadku, gdzie jestem rozdrabniania z / wstawianie do około 30 kolumn, wydaje się, że dzieje się to osobno 30 razy.

Będę musiał zrzucić ten kod, nie sądzę, że żadna optymalizacja nie przejdzie przez tę metodę, która jest z natury powolna. Zamierzam wypróbować metodę sp_xml_preparedocument/OPENXML i sprawdzić, czy wydajność jest lepsza. Jeśli ktoś natknie się na to pytanie z wyszukiwarki internetowej (tak jak ja) to bardzo zaleca się wykonanie testów wydajności przed użyciem tego typu rozdrabniania w SQL Server

 0
Author: DannykPowell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-10 18:03:19

Istnieje XML Bulk load COM object (. Net przykład )

From MSDN :

Możesz wstawić dane XML do SQL Bazy danych serwera za pomocą wstawki oświadczenie i funkcja OPENXML; jednak Narzędzie do masowego ładowania zapewnia lepszą wydajność, gdy potrzeba Wstawienia dużej ilości XML data.

 0
Author: si618,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-10 18:17:54

Moim obecnym rozwiązaniem dla dużych zestawów XML (>500 węzłów) jest użycie SQL Bulk Copy (System.Data.SqlClient.SqlBulkCopy) za pomocą zestawu danych załadować XML do pamięci, a następnie przekazać tabelę do SqlBulkCopy (definiowanie schematu XML pomaga).

Oczywiście istnieją pułapki, takie jak niepotrzebne użycie zestawu danych i wczytywanie całego dokumentu do pamięci. Chciałbym pójść dalej w przyszłości i zaimplementować własny IDataReader, aby ominąć metodę DataSet, ale obecnie DataSet jest " dobry wystarczy" do pracy.

Zasadniczo nigdy nie znalazłem rozwiązania mojego pierwotnego pytania dotyczącego powolnej wydajności dla tego typu rozdrabniania XML. Może to być powolne z powodu wpisywanych zapytań xml, które są z natury powolne lub mają coś wspólnego z transakcjami i logiem serwera SQL. Domyślam się, że wpisane funkcje xml nigdy nie były przeznaczone do pracy na nietrywialnych rozmiarach węzłów.

XML Bulk Load: próbowałem tego i tobyło szybko ale miałem problem z uruchomieniem COM dll pod Środowiska 64-bitowe i generalnie staram się unikać bibliotek DLL COM, które nie wydają się już obsługiwane.

Sp_xml_preparedocument / OPENXML: nigdy nie szedłem tą drogą, więc byłbym zainteresowany, aby zobaczyć, jak to działa.

 0
Author: eddiegroves,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-10 19:19:04