MYSQL - parsing XML and running queries - mysql

I'm struggling parsing XML into MySQL. The other option to parse XML to csv is not feasible as it looks that xmlstarlet is not available for Aix 7.1.0.0.
Investigating MySql Reference Manual I realized that the XML I'm dealing with is not fully supported. I have four different types of XML files. Let's take one as example.
<MovementReport version="0100">
<ControlArea>
<Sender>
<Division>WCS1</Division>
<Confirmation>2</Confirmation>
</Sender>
<CreationDateTime>2018-04-17T15:39:32Z</CreationDateTime>
<RefId>
<Id>6897731</Id>
</RefId>
</ControlArea>
<DataArea>
<RequestId>080030603</RequestId>
<FromLocation>
<MHA>ID1</MHA>
<Rack></Rack>
<X></X>
<Y></Y>
</FromLocation>
<StUnit>
<StUnitId>M1813236 </StUnitId>
</StUnit>
<ToLocation>
<MHA>A</MHA>
<Rack>011</Rack>
<X>065</X>
<Y>019</Y>
</ToLocation>
<ReasonCode>00</ReasonCode>
<StandAloneFlag>W</StandAloneFlag>
<Information>No info!</Information>
</DataArea>
</MovementReport>
I have to use ROWS IDENTIFIED BY in order to have some columns populated. I tried almost all tags in the above command and I came up with the following sql
USE xml_lcs; TRUNCATE TEST01;
LOAD XML LOCAL INFILE '33770626.xml'
INTO TABLE TEST01 ROWS IDENTIFIED BY '<DataArea>'
SET N_ID='A';
LOAD XML LOCAL INFILE '33770626.xml'
INTO TABLE TEST01 ROWS IDENTIFIED BY '<ToLocation>'
(#MHA, #Rack, #X, #Y)
SET t_MHA=#MHA, t_Rack=#Rack,t_X=#X, t_Y=#Y;
LOAD XML LOCAL INFILE '33770626.xml'
INTO TABLE TEST01 ROWS IDENTIFIED BY '<StUnit>'
SET N_ID='A';
LOAD XML LOCAL INFILE '33770626.xml'
INTO TABLE TEST01 ROWS IDENTIFIED BY '<FromLocation>'
(#MHA, #Rack, #X, #Y)
SET f_MHA=#MHA, f_Rack=#Rack,f_X=#X, f_Y=#Y;
LOAD XML LOCAL INFILE '33770626.xml'
INTO TABLE TEST01 ROWS IDENTIFIED BY '<Sender>'
SET N_ID='A';
LOAD XML LOCAL INFILE '33770626.xml'
INTO TABLE TEST01 ROWS IDENTIFIED BY '<RefId>'
SET N_ID='A';
LOAD XML LOCAL INFILE '33770626.xml'
INTO TABLE TEST01 ROWS IDENTIFIED BY '<ReasonCode>'
SET N_ID='A';
The above sql code result in the following table
All columns are VARCHAR.
I would like to have one row for each file, so at the end the above XML file would result in..
Any idea how to achieve that ?
Thanks a lot for your time and help.
Ema

You should use another language to parse the file and insert the data. But if you want a MySQL only solution, you can use your query to store the data into a temprary table and then use an aggregation query to combine the data into one row and copy it into the real table.
First create the temporary table:
CREATE TEMPORARY TABLE TEMP01 LIKE TEST01;
Use your code to load the data from XML to the temporary table. Change TEST01 to TEMP01.
After that copy the data with:
INSERT INTO TEST01 (N_ID, Division, RequestId, ... , StUnitId)
SELECT N_ID
, MIN(Division)
, MIN(RequestId)
...
, MIN(StUnitId)
FROM TEMP01
GROUP BY N_ID;
Since you have only one distinct value per column, it doesn't matter if you use MIN or MAX here. In MySQL 5.7 you can also use ANY_VALUE instead.

You also have the option to use functions like LOAD_FILE() and ExtractValue(). Be aware of the necessary privileges.
Example:
SELECT LOAD_FILE('/path/to/file/33770626.xml') INTO #`xml`;
INSERT INTO `TEST01`
SELECT
NULLIF(TRIM(ExtractValue(#`xml`, 'MovementReport/DataArea/ToLocation/MHA')), ''),
NULLIF(TRIM(ExtractValue(#`xml`, 'MovementReport/ControlArea/Sender/Division')), ''),
NULLIF(TRIM(ExtractValue(#`xml`, 'MovementReport/ControlArea/RefId/Id')), ''),
NULLIF(TRIM(ExtractValue(#`xml`, 'MovementReport/DataArea/FromLocation/Rack')), ''),
.
.
.
NULLIF(ExtractValue(#`xml`, 'MovementReport/DataArea/StUnit/StUnitId'), '');

Related

how to load a file in mysql tables column?

I need to set one column's value which would be big scss file with lots of characters (thats why I cant just UPDATE TABLE table WHERE value = 'very big txt file'). So I found that I can use msyql function LOAD_FILE('/loc/to/bigfile.scss') Full command looks like this:
UPDATE table
SET value = LOAD_FILE('/path/to/bigfile.scss')
WHERE
name = 'my_theme' ;
column 'value' cannot be null and mysql gives me error:
ERROR 1048 (23000) at line 1: Column 'value' cannot be null
Which means that this LOAD_FILE didn't work.
file location is 100 % correct.
Could you help me?
An alternate way is to use 'LOAD DATA INFILE'
LOAD DATA INFILE 'bigfile.scss'
INTO TABLE employee
(value);
Official documentation here and more examples of these here.

Parametric query when using 'load data infile'

I use parametric queries for normal insert/updates for security.
How do I do that for queries like this:
LOAD DATA INFILE '/filepath' INTO TABLE mytable
In my case, the path to the file would be different everytime (for different requests). Is it fine to proceed like this (since I am not getting any data from outside, the file is from the server itself):
path = /filepath
"LOAD DATA INFILE" + path + "INTO TABLE mytable"
Since LOAD DATA is not listed in SQL Syntax Allowed in Prepared Statements you can't prepare something like
LOAD DATA INFILE ? INTO TABLE mytable
But SET is listed. So a workaround could be to prepare and execute
SET #filepath = ?
And then execute
LOAD DATA INFILE #filepath INTO TABLE mytable
Update
In Python with MySQLdb the following query should work
LOAD DATA INFILE %s INTO TABLE mytable
since no prepared statement is used.
To answer your "is it fine to proceed like this" question, your example code will fail because the resulting query will be missing quotes around the filename. If you changed it to the following it could run, but is still a bad idea IMO:
path = "/filepath"
sql = "LOAD DATA INFILE '" + path + "' INTO TABLE mytable" # note the single quotes
While you may not be accepting outside input today, code has a way of sticking around and getting reused/copied, so you should use the API in a way that will escape your parameters:
sql = "LOAD DATA INFILE %s INTO TABLE mytable"
cursor.execute(sql, (path,))
And don't forget to commit if autocommit is not enabled.

How to insert BLOB and CLOB files in MySQL?

I want to store images and .docx/.doc, .pptx/.ppt, .pdf files using the front end of my software. I don't understand how to implement this and how to insert the BLOB and CLOB files into the table. Please help.
I am using Kubuntu 11.04, MySQL5, Qt 4.7.3.
Two ways:
1 - Use a LOAD_FILE function -
INSERT INTO table1 VALUES(1, LOAD_FILE('data.png'));
2 - Insert file as hex string, e.g. -
INSERT INTO table1 VALUES
(1, x'89504E470D0A1A0A0000000D494844520000001000000010080200000090916836000000017352474200AECE1CE90000000467414D410000B18F0BFC6105000000097048597300000EC300000EC301C76FA8640000001E49444154384F6350DAE843126220493550F1A80662426C349406472801006AC91F1040F796BD0000000049454E44AE426082');
INSERT INTO MY_TABLE(id, blob_col) VALUES(1, LOAD_FILE('/full/path/to/file/myfile.png')
LOAD_FILE has many conditions attached to it. From the MySQL documentation:
LOAD_FILE(file_name)
Reads the file and returns the file contents as a string. To use this
function, the file must be located on the server host, you must
specify the full path name to the file, and you must have the FILE
privilege. The file must be readable by all and its size less than
max_allowed_packet bytes. If the secure_file_priv system variable is
set to a nonempty directory name, the file to be loaded must be
located in that directory.
If the file does not exist or cannot be read because one of the
preceding conditions is not satisfied, the function returns NULL.
Also, there there are bugs with LOAD_FILE in Linux. See http://bugs.mysql.com/bug.php?id=38403 for the bug, and MySQL LOAD_FILE returning NULL for workarounds. On Ubuntu 12.04, MySQL 5.5.32, this works for me:
Copy file to /tmp
Change ownership to mysql user chown mysql:mysql /tmp/yourfile
Log into mysql as mysql root user so you are sure you have FILE privilege
Run your insert statement
Or you could merely use the MySQL Workbench, select the rows, last rows, insert a row without the blob, then just right click and select "Load Value From File".
INSERT INTO table1 VALUES(1, LOAD_FILE(data.png));
won't work but
INSERT INTO table1 VALUES(1, LOAD_FILE('data.png'));
should (assuming data.png exists in the local directory)
for those People who are getting "Column 'image' cannot be null" error while saving Blob through query :-
Open your MySql Command Line Client and login with root user and type
mysql> SHOW VARIABLES LIKE "secure_file_priv";
this will show you the secure path used by MySql to access the files. something like
+------------------+-----------------------+
| Variable_name | Value |
+------------------+-----------------------+
| secure_file_priv | /var/lib/mysql-files/ |
+------------------+-----------------------+
you can either paste files inside this folder or change the "secure_file_priv" variable value to "empty string" so that it can read file from anywhere.
If you are using mysql workbench, just right click on the field (cell) and select 'load value from file' option and then browse to the file and click open and then click on apply. It will automatically generate query like this
UPDATE `dbname`.`tablename` SET `columnname` = ? WHERE (`row` = '1');

dumping data from views in mysql

i have a view and want to extract its data into a file that has create table statement as well data.
i know that mysqldump doesn't work on views.
Obviously, there isn't an automated way to generate the CREATE TABLE statement of a table that does not exist. So you basically have two options:
Create an actual table, dump it and remove it afterwards.
Write a lot of code to analyse the view and underlying tables and generate the appropriate SQL.
First option is not optimal at all but it's easy to implement:
CREATE TABLE my_table AS
SELECT *
FROM my_view
You can now dump the table with mysqldump. When you're done:
DROP TABLE my_table
Second option can be as optimal as you need but it can get pretty complicate and it depends a lot on your actual needs and tool availability. However, if performance is an issue you can combine both approaches in a quick and dirty trick:
CREATE TABLE my_table AS
SELECT *
FROM my_view
LIMIT 1;
SHOW CREATE TABLE my_table;
Now, you use your favourite language to read values from my_view and build the appropriate INSERT INTO code. Finally:
DROP TABLE my_table;
In any case, feel free to explain why you need to obtain SQL code from views and we may be able to find better solutions.
Use SELECT ... INTO OUTFILE to create a dump of the data.
I have written a bash function to export the "structure" and data of a VIEW without creating a full copy of the data. I tested it with MySQL 5.6 on a CentOS 7 server. It properly takes into account columns with JSON values and strings like "O'Mally", though you may need to tweak it further for other special cases.
For the sake of brevity, I did not make it robust in terms of error checks or anything else.
function export_data_from_view
{
local DB_HOST=$1
local SCHEMA=$2
local VIEW=$3
local TMP_TABLE_NAME="view_as_table_$RANDOM"
local SQL1="
create table $TMP_TABLE_NAME as
(select * from $VIEW where 1=0);
show create table $TMP_TABLE_NAME \G
"
# Create an empty table with the structure of all columns in the VIEW.
# Display the structure. Delete lines not needed.
local STRUCT=$(
mysql -h $DB_HOST -BANnq -e "$SQL1" $SCHEMA |
egrep -v "\*\*\*.* row \*\*\*|^${TMP_TABLE_NAME}$" |
sed "s/$TMP_TABLE_NAME/$VIEW/"
)
echo
echo "$STRUCT;"
echo
local SQL2="
select concat( 'quote( ', column_name, ' ),' )
from information_schema.columns
where table_schema = '$SCHEMA'
and table_name = '$VIEW'
order by ORDINAL_POSITION
"
local COL_LIST=$(mysql -h $DB_HOST -BANnq -e "$SQL2")
# Remove the last comma from COL_LIST.
local COL_LIST=${COL_LIST%,}
local SQL3="select $COL_LIST from $VIEW"
local INSERT_STR="insert into $VIEW values "
# Fix quoting issues to produce executeable INSERT statements.
# \x27 is the single quote.
# \x5C is the back slash.
mysql -h $DB_HOST -BANnq -e "$SQL3" $SCHEMA |
sed '
s/\t/,/g; # Change each TAB to a comma.
s/\x5C\x5C\x27/\x5C\x27/g; # Change each back-back-single-quote to a back-single-quote.
s/\x27NULL\x27/NULL/g; # Remove quotes from around real NULL values.
s/\x27\x27{/\x27{/g; # Remove extra quotes from the beginning of a JSON value.
s/}\x27\x27/}\x27/g; # Remove extra quotes from the end of a JSON value.
' |
awk -v insert="$INSERT_STR" '{print insert "( " $0 " );"}'
local SQL4="drop table if exists $TMP_TABLE_NAME"
mysql -h $DB_HOST -BANnq -e "$SQL4" $SCHEMA
echo
}

make a table in MS Access

I have a query in MS ACCESS, I ran it in MS ACCESS:
SELECT * FROM table1
INNER JOIN table2 ON table1.f1=table2.f1 WHERE table1.f2=table2.f2
It works fine. However, I need to save the results into another table. So, I changed it to:
SELECT * Into a1
FROM table1 INNER JOIN table2 ON table1.f1=table2.f1 WHERE table1.f2=table2.f2
It does not work. I receive this error: "Cannot Open database. It may not be a database that your application recognizes, or the file may be corrupt."
Does anybody know how I can save the results in a database or txt file?
Thank you very much.
You can use the insert into command, see: http://msdn.microsoft.com/en-us/library/bb208861(office.12).aspx
Also appears that the database is in read only mode.
Is the database read-only?
Some things to check:
Is the DB file's read-only attribute set?
Did you use "Open Read Only" to open the DB?
Are you out of disk space?
Is there enough disk space to create the new table?
You can easily output the results as a .txt file or a .csv file (that you can view in Excel). To export a .txt file:
DoCmd.TransferText acExportDelim, , "myQuery", "C:\myQuery.txt", True
You can research TransferText in help to see the options for a .csv file.
This should work easily.
try create a new table with the values mentioned at your select.
step 1:
CREATE TABLE table_shadi
(
column_name1 data_type,
column_name2 data_type,
column_name3 data_type,
....
)
make sure you defined the same datatypes and number of fields as expected from you query
step 2:
Insert into table_shadi(column_name1,column_name2,column_name3)
SELECT column_name1,column_name2,column_name3
FROM table1
INNER JOIN table2
ON table1.f1=table2.f1
WHERE table1.f2=table2.f2
Hope it helps.