2011-12-20 15 views
7

MySQL ma ładny oświadczenie: LOAD XML LOCAL plik_weLOAD XML LOCAL INFILE z Niespójne nazwy kolumn

Na przykład, jeśli masz poniższej tabeli:

CREATE TABLE person (
    person_id INT NOT NULL PRIMARY KEY, 
    fname VARCHAR(40) NULL, 
    lname VARCHAR(40) NULL 
); 

i następujący plik XML o nazwie person.xml :

<list> 
     <person> 
      <person_id>1</person_id> 
      <fname>Mikael</fname> 
      <lname>Ronström</lname> 
     </person> 
     <person> 
      <person_id>2</person_id> 
      <fname>Lars</fname> 
      <lname>Thalmann</lname> 
     </person> 
</list> 

można to zrobić:

LOAD XML LOCAL INFILE 'person.xml' 
INTO TABLE person 
ROWS IDENTIFIED BY '<person>'; 

Moje pytanie brzmi: co by było, gdyby nazwy kolumn były inne w pliku XML niż w tabeli? Na przykład:

<list> 
     <person> 
      <PersonId>1</PersonId> 
      <FirstName>Mikael</FirstName> 
      <LastName>Ronström</LastName> 
     </person> 
     <person> 
      <PersonId>2</PersonId> 
      <FirstName>Lars</FirstName> 
      <LastName>Thalmann</LastName> 
     </person> 
</list> 

Jak można osiągnąć to samo za pomocą instrukcji MySQL bez manipulowania plikiem XML? Szukałem wszędzie, ale nie mogłem znaleźć odpowiedzi.

Odpowiedz

4

Poniżej przedstawiono opcje dostępne do mnie:

Wariant 1: Tworzenie tabeli tymczasowej z różnymi nazwami pól (jak sugeruje innych odpowiedzi). Byłoby to satysfakcjonujące podejście. Jednak gdy spróbowałem, pojawił się nowy problem: instrukcja LOAD XML nie akceptuje z jakiegoś powodu pustych elementów o zminimalizowanym formacie (na przykład <person />). Tak więc instrukcja nie powiodła się, ponieważ pliki XML, które muszę ładować sporadycznie, mają puste elementy w tym formacie.

Opcja 2: Przekształć plik XML za pomocą XSLT przed uruchomieniem instrukcji LOAD XML, aby zmienić nazwy elementów i zmodyfikować formaty pustych elementów. Nie było to możliwe, ponieważ pliki XML są bardzo duże, a silniki przetwarzające XSLT ładują cały XML do pamięci przed przetworzeniem.

Opcja 3: Całkowicie pomiń instrukcję LOAD XML i użyj parsera SAX do przeanalizowania pliku XML i wstawiania rekordów bezpośrednio do bazy danych za pomocą JDBC i przygotowanych instrukcji. Mimo że surowe JDBC i przygotowane sprawozdania są generalnie wydajne, okazało się, że jest zbyt wolne. DUŻO niż w instrukcji LOAD XML.

Opcja 4: Użyj instrukcji LOAD DATA zamiast instrukcji LOAD XML i korzystaj z opcjonalnych klauzul powiązanych z tą instrukcją, aby dopasować ją do moich potrzeb (np. Linie oddzielone przez itp.). Mogło to zadziałać, ale byłoby podatne na błędy i niestabilne.

Opcja 5: Parsowanie pliku za pomocą szybkiego parsera i jednoczesnego odczytu i zapisu elementów XML jednocześnie i wygenerowanie nowego pliku XML ze zmodyfikowanymi nazwami w pożądanym formacie dla instrukcji LOAD XML.

Skończyło się na użyciu opcji 5. Użyłem Java Streaming API dla XML (StAX) zarówno do odczytu pliku XML i generowania zmodyfikowanego pliku XML, a następnie uruchomienia LOADAL INFILE XML przez JDBC z poziomu aplikacji internetowej. Działa doskonale i jest superszybki.

2

Można by utworzyć tabelę tymczasową używając nazw kolumn z pliku XML (choć będzie to miało być zrobione ręcznie w zapytaniu create temporary table), wczytać plik XML do tej tabeli, a następnie insert into person select * from tmp_table_name.

+0

To jest bardzo dobry obejść. Jednak, gdy skomentowałem odpowiedź Billa Karwina, istnieje inny problem z instrukcją LOAD XML. Nie akceptuje zminimalizowanych pustych tagów, takich jak . Czy możesz wymyślić rozwiązanie tego problemu? – stepanian

+0

Nigdy nie pracowałem z 'LOAD XML', więc poszedłem z wykształconym domysłem na temat tej odpowiedzi. Więc nie, niestety nie znam rozwiązania tego problemu, innego niż ładowanie pliku XML w innym języku (np. PHP), parseowanie go, a następnie wysyłanie wynikowego zapytania do bazy danych. –

+1

FYI Niepowodzenie ładowania tagów było błędem i został naprawiony od _Prior do MySQL 5.5.46, LOAD XML nie obsługiwał pustych elementów XML w postaci poprawnie. (Bug # 67542, Bug # 16171518) _. Od [tutaj] (https://dev.mysql.com/doc/refman/5.5/en/load-xml.html) – radman

10

Pola w pliku XML, które nie odpowiadają nazwom kolumn fizycznych, są ignorowane. Kolumny w tabeli, które nie mają odpowiednich pól w pliku XML, mają wartość NULL.

Co mogę zrobić, to załadować do tabeli temp, jak sugeruje @Kolink, ale z dodatkowymi kolumnami. Dodaj klauzulę SET podczas ładowania danych z pliku XML.

CREATE TEMP TABLE person_xml LIKE person; 

ALTER TABLE person_xml 
    ADD COLUMN FirstName VARCHAR(40), 
    ADD COLUMN LastName VARCHAR(40), 
    ADD COLUMN PersonId INT; 

LOAD XML LOCAL INFILE 'person.xml' INTO TABLE person_xml 
    SET person_id = PersonId, fname = FirstName, lname = LastName; 

SELECT * FROM person_xml; 
+-----------+--------+-------------+-----------+-------------+----------+ 
| person_id | fname | lname  | FirstName | LastName | PersonId | 
+-----------+--------+-------------+-----------+-------------+----------+ 
|   1 | Mikael | Ronström | Mikael | Ronström |  1 | 
|   2 | Lars | Thalmann | Lars  | Thalmann |  2 | 
+-----------+--------+-------------+-----------+-------------+----------+ 

Następnie należy skopiować do prawdziwej tabeli, wybierając podzbiór kolumn.

INSERT INTO person SELECT person_id, fname, lname FROM person_xml; 

Ewentualnie upuść dodatkowe kolumny i użyj SELECT *.

ALTER TABLE person_xml 
    DROP COLUMN PersonId, 
    DROP COLUMN FirstName, 
    DROP COLUMN LastName; 

INSERT INTO person SELECT * FROM person_xml; 

SELECT * FROM person; 

+-----------+--------+-------------+ 
| person_id | fname | lname  | 
+-----------+--------+-------------+ 
|   1 | Mikael | Ronström | 
|   2 | Lars | Thalmann | 
+-----------+--------+-------------+ 
+0

Dzięki. Pojawił się nowy problem. Najwyraźniej to stwierdzenie nie lubi pustych elementów w postaci . Wymaga oddzielnych tagów otwierania i zamykania. Nie jestem pewien, czy jest to na odwrót. – stepanian

+0

Powiedziałbym, że powinieneś nauczyć się XSLT. –

+0

W czym pomoże XSLT? – stepanian

4

trochę hacky ale roboczego roztworu za pomocą starych, dobrych LOAD DATA INFILE:

LOAD DATA LOCAL INFILE '/tmp/xml/loaded.xml' 
INTO TABLE person 
CHARACTER SET binary 
LINES STARTING BY '<person>' TERMINATED BY '</person>' 
(@person) 
SET 
    person_id = ExtractValue(@person:=CONVERT(@person using utf8), 'PersonId'), 
    fname = ExtractValue(@person, 'FirstName'), 
    lname = ExtractValue(@person, 'LastName') 
; 

PS: Prawdopodobnie będziesz musiał dodatkowo grać z ogranicznikiem pola, jeśli dane zawierają przecinki.

+0

Może to być podatne na błędy. Zbyt hacky. Dziękuję za odpowiedź. – stepanian

+0

Dziękuję. Przewaga prędkości nad "LOAD XML" jest wielkością rzędu – myol

0

mysql schematu tabeli: organization_type (id, name)

organizationtype.xml:

<NewDataSet> 
    <row> 
     <ItemID>1</ItemID> 
     <ItemCreatedBy>53</ItemCreatedBy> 
     <ItemCreatedWhen>2014-03-10T22:53:43.947+10:00</ItemCreatedWhen> 
     <ItemModifiedBy>53</ItemModifiedBy> 
     <ItemModifiedWhen>2014-03-10T22:53:43.99+10:00</ItemModifiedWhen> 
     <ItemOrder>1</ItemOrder> 
     <ItemGUID>e2ad051f-b7ea-4feb-b91e-f558f6f632a0</ItemGUID> 
     <Name>Company Type 1</Name> 
    </row> 

a zapytanie import mysql będzie wyglądać następująco:

LOAD XML INFILE '/var/lib/mysql-files/organizationtype.xml' 
INTO TABLE organization_type (@ItemID, @Name) 
SET [email protected], [email protected] 
Powiązane problemy