Bulk insert operation are going too slow - mysql

Here is the Structure of my 'venprices' table.
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| conid | int(10) | NO | PRI | NULL | |
| rate | double | YES | | NULL | |
| venid | varchar(50) | NO | PRI | | |
+-------+-------------+------+-----+---------+-------+
JAVA Code :
new Thread(){
public void run(){
XSSFWorkbook myWorkBook;
XSSFSheet mySheet = null;
Iterator<Row> rowIterator = null;
String venid = null, errorlog = null;
int code;
double rate;
int rows, maxcount;
PreparedStatement ps = null;
Connection con;
ProgressMonitor pm;
try {
myWorkBook = new XSSFWorkbook(new FileInputStream(new File(jTextField1.getText())));
mySheet = myWorkBook.getSheetAt(0);
rowIterator = mySheet.iterator();
rowIterator.next();
venid = jComboBox1.getItemAt(jComboBox1.getSelectedIndex());
con = Mycon.getConnection();
ps = con.prepareStatement("DELETE FROM venprices WHERE venid = ?");
ps.setString(1, venid);
ps.executeUpdate();
ps.clearBatch();
ps = con.prepareStatement("insert into venprices values (?,?,?)");
} catch(Exception ioe) {
JOptionPane.showMessageDialog(null, ioe.getMessage());
}
rows = 1;maxcount = mySheet.getLastRowNum();
// Traversing over each row of XLSX file
while (rowIterator.hasNext())
{
try{
Row row = rowIterator.next();
Iterator<Cell> cellIterator = row.cellIterator();
Cell cell = cellIterator.next();
code = (int) cell.getNumericCellValue();
cell = cellIterator.next();
rate = cell.getNumericCellValue();
ps.setInt(1,code);
ps.setDouble(2,rate);
ps.setString(3, venid);
ps.addBatch();
rows++;
}catch(Exception e){errorlog = errorlog + "\n" +rows+ e.getMessage();}
}
try{
System.gc();
ps.executeBatch();
}catch(Exception e){e.printStackTrace();}
if(errorlog == null)
JOptionPane.showMessageDialog(null, "Import Successful. " + rows + " Records Imported.");
else
JOptionPane.showMessageDialog(null, "Error Log :\n"+errorlog);
}
}.start();
The user is expected to insert around 50,000 records in a single shot with an Excel File. But the query takes around 6-7 minutes.
Can anyone please help me in reducing the insert operation time or tell me some tweaks in the insert query?
Thanks in Advance!
Edit 1:
As Requested, Here is the result of show create table venprices
mysql> show create table venprices;
+-----------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+-----------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| venprices | CREATE TABLE `venprices` (
`conid` int(10) NOT NULL,
`rate` double DEFAULT NULL,
`venid` varchar(50) NOT NULL DEFAULT '',
PRIMARY KEY (`conid`,`venid`),
KEY `vepr` (`conid`,`rate`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 |
+-----------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+

I don't know the Java syntax, but add a MySQL START TRANSACTION (BEGIN) at the beginning of your code. And add COMMIT to the end.
Why?
50K rows inserted in 6-7 minutes smells very much like inserting each row in its own transaction (a la autocommit=ON). This is slow because of all the transactional activity on the disk.
By turning the 50K transactions into 1 transaction, you will have a lot less I/O, hence it will run faster.
Secondly... By turning the 50K 1-row INSERTs into 50 1000-row INSERTs, you can get another 10x speedup. This is because of avoiding most of the roundtrip time between client and server and most of the parsing time. (Again, I don't know if Java has a special class to facilitate such; it might.)
These two changes will perhaps be competitive with Drew's LOAD_DATA INFILE approach.

Related

KEA dhcp Mysql Backend

I have an issue with the new ISC DHCP, KEA, on the MySQL backend.
I want to store leases in my DB, it works but some info are not stored.
I obtain this kind of entry in my DB :
+------------+--------+-----------+----------------+---------------------+-----------+----------+----------+----------+
| address | hwaddr | client_id | valid_lifetime | expire | subnet_id | fqdn_fwd | fqdn_rev | hostname |
+------------+--------+-----------+----------------+---------------------+-----------+----------+----------+----------+
| 3232236052 | '° | NULL | 4000 | 2015-07-22 08:54:32 | 1 | 0 | 0 | │
+------------+--------+-----------+----------------+---------------------+-----------+----------+----------+----------+
The address field is the IP adress in decimal, I checked and it's the good one.
I didn't find how change the IP adress to IPv4 format and how store mac address in the hwaddr field in the KEA documentation.
If someone know how to do this I will be really grateful !
Thank you !
As per KEA documentation hwaddr field is VARBINARY. You should be able to see the value IP address and hwaddr using:
SELECT INET_NTOA(address), HEX(hwaddr), lease4.* FROM lease4;
I had a similar need to create KEA host reservations and populate the MAC and IP addresses as regular strings while still having the fields KEA uses updated automatically on an INSERT or UPDATE.
What I ended up doing is creating two new fields that would hold those string values ('hosts.dhcp_identifier_str' and 'hosts.ipv4_address_str'):
ALTER TABLE `hosts` ADD `dhcp_identifier_str` VARCHAR(12) NOT NULL AFTER `dhcp_identifier`;
ALTER TABLE `hosts` ADD `ipv4_address_str` VARCHAR(15) NULL DEFAULT NULL AFTER `ipv4_address`;
Then, I keep the corresponding fields that KEA uses ('hosts.dhcp_identifier' and 'hosts.ipv4_address') up to date by using BEFORE INSERT/UPDATE MySQL triggers:
DELIMITER //
DROP TRIGGER IF EXISTS `host_BINS`//
CREATE TRIGGER `host_BINS` BEFORE INSERT ON `hosts`
FOR EACH ROW BEGIN
IF (NEW.dhcp_identifier = '' AND NEW.dhcp_identifier_str != '') THEN
SET NEW.dhcp_identifier = UNHEX(UPPER(NEW.dhcp_identifier_str));
ELSEIF (NEW.dhcp_identifier_str = '' AND NEW.dhcp_identifier != '') THEN
SET NEW.dhcp_identifier_str = LOWER(HEX(NEW.dhcp_identifier));
END IF;
IF (NEW.ipv4_address IS NULL AND NEW.ipv4_address_str IS NOT NULL) THEN
SET NEW.ipv4_address = INET_ATON(NEW.ipv4_address_str);
ELSEIF (NEW.ipv4_address_str IS NULL AND NEW.ipv4_address IS NOT NULL) THEN
SET NEW.ipv4_address_str = CAST(INET_NTOA(NEW.ipv4_address) AS CHAR);
END IF;
END
//
DROP TRIGGER IF EXISTS `host_BUPD`//
CREATE TRIGGER `host_BUPD` BEFORE UPDATE ON `hosts`
FOR EACH ROW BEGIN
IF (NEW.dhcp_identifier_str != '' AND OLD.dhcp_identifier != UNHEX(UPPER(NEW.dhcp_identifier_str))) THEN
SET NEW.dhcp_identifier = UNHEX(UPPER(NEW.dhcp_identifier_str));
ELSEIF (NEW.dhcp_identifier != '' AND OLD.dhcp_identifier_str != LOWER(HEX(NEW.dhcp_identifier))) THEN
SET NEW.dhcp_identifier_str = LOWER(HEX(NEW.dhcp_identifier));
END IF;
IF (NEW.ipv4_address_str IS NOT NULL AND OLD.ipv4_address != INET_ATON(NEW.ipv4_address_str)) THEN
SET NEW.ipv4_address = INET_ATON(NEW.ipv4_address_str);
ELSEIF (NEW.ipv4_address IS NOT NULL AND OLD.ipv4_address_str != CAST(INET_NTOA(NEW.ipv4_address) AS CHAR)) THEN
SET NEW.ipv4_address_str = CAST(INET_NTOA(NEW.ipv4_address) AS CHAR);
END IF;
END
//
This works whether you INSERT/UPDATE an entry using the dhcp_identifier/ipv4_address or dhcp_identifier_str/ipv4_address_str pairs.
I'm sure you can use the same triggers for the 'lease4' table.
Hope that helps.

Insert values into SQL column with mysql-python

I am trying to insert values into a column of a SQL table, using MySQLdb in Python 2.7. I am having problems with the command to insert a list into 1 column.
I have a simple table called 'name' as shown below:
+--------+-----------+----------+--------+
| nameid | firstname | lastname | TopAdd |
+--------+-----------+----------+--------+
| 1 | Cookie | Monster | |
| 2 | Guy | Smiley | |
| 3 | Big | Bird | |
| 4 | Oscar | Grouch | |
| 5 | Alastair | Cookie | |
+--------+-----------+----------+--------+
Here is how I created the table:
CREATE TABLE `name` (
`nameid` int(11) NOT NULL AUTO_INCREMENT,
`firstname` varchar(45) DEFAULT NULL,
`lastname` varchar(45) DEFAULT NULL,
`TopAdd` varchar(40) NOT NULL,
PRIMARY KEY (`nameid`)
) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8
Here is how I populated the table:
INSERT INTO `test`.`name`
(`firstname`,`lastname`)
VALUES
("Cookie","Monster"),
("Guy","Smiley"),
("Big","Bird"),
("Oscar","Grouch"),
("Alastair","Cookie");
DISCLAIMER: The original source for the above MySQL example is here.
Here is how I created the a new column named TopAdd:
ALTER TABLE name ADD TopAdd VARCHAR(40) NOT NULL
I now have a list of 5 values that I would like to insert into the column TopAdd as the values of that column. Here is the list.
vals_list = ['aa','bb','cc','dd','ee']
Here is what I have tried (UPDATE statement inside loop):
vals = tuple(vals_list)
for self.ijk in range (0,len(self.vals)):
self.cursor.execute ("UPDATE name SET TopAdd = %s WHERE 'nameid' = %s" % (self.vals[self.ijk],self.ijk+1))
I get the following error message:
Traceback (most recent call last):
File "C:\Python27\mySQLdbClass.py", line 70, in <module>
[Finished in 0.2s with exit code 1]main()
File "C:\Python27\mySQLdbClass.py", line 66, in main
db.mysqlconnect()
File "C:\Python27\mySQLdbClass.py", line 22, in mysqlconnect
self.cursor.execute ("UPDATE name SET TopAdd = %s WHERE 'nameid' = %s" % (self.vals[self.ijk],self.ijk+1))
File "C:\Python27\lib\site-packages\MySQLdb\cursors.py", line 205, in execute
self.errorhandler(self, exc, value)
File "C:\Python27\lib\site-packages\MySQLdb\connections.py", line 36, in defaulterrorhandler
raise errorclass, errorvalue
_mysql_exceptions.OperationalError: (1054, "Unknown column 'aa' in 'field list'")
Is there a way to insert these values into the column with a loop or directly as a list?
Try This:
vals_list = ['aa','bb','cc','dd','ee']
for i, j in enumerate(vals_list):
self.cursor.execute(("UPDATE test.name SET TopAdd = '%s' WHERE nameid = %s" % (str(j),int(i+1))
One problem is here:
for self.ijk in range (0,len(self.vals)):
The range function is creating a list of integers (presumably, the list [0, 1, 2, 3, 4]). When iterating over a collection in a for loop, you bind each successive item in the collection to a name; you do not access them as attributes of an instance. (It also seems appropriate to use xrange here; see xrange vs range.) So the self reference is non-sensical; beyond that, ijk is a terrible name for an integer value, and there's no need to supply the default start value of zero. KISS:
for i in range(len(self.vals)):
Not only does this make your line shorter (and thus easier to read), using i to represent an integer value in a loop is a convention that's well understood. Now we come to another problem:
self.cursor.execute ("UPDATE name SET TopAdd = %s WHERE 'nameid' = %s" % (self.vals[self.ijk],self.ijk+1))
You're not properly parameterizing your query here. Do not follow this advice, which may fix your current error but leaves your code prone to wasted debugging time at best, SQL injection and/or data integrity issues at worst. Instead, replace the % with a comma so that the execute function does the work safely quoting and escaping parameters for you.
With that change, and minus the quotation marks around your column name, nameid:
query = "UPDATE name SET TopAdd = %s WHERE nameid = %s;"
for i in range(len(self.vals)):
self.cursor.execute(query, (self.vals[i], i + 1))
Should work as expected. You can still use enumerate as suggested by the other answer, but there's no reason to go around typecasting everything in sight; enumerate is documented and gives exactly the types you already want:
for i, val in enumerate(self.vals):
self.cursor.execute(query, (val, i + 1))

Why mysql program hangs(deadlock)?

I am struggling more than one day on dealing with a mysql hangs(deadlock). In below testcase, I will try to create the database first if it doesn't exist and try to create a table if it doesn't exist too. Then I do a query on the table. Each time I execute the SQL command, I strictly close the cursor. But the program still hangs. I have found two workarounds. 1) close the connection after creating the database and create a new connection. 2) call commit() after the query.
The two workarounds works good but they make me more confused. As my understanding, it's ok to keep connection if the cursors are closed in time and commit() are called after each change. And also, there is no reason to call commit() after query.
So my two workarounds even destroyed my understanding of database operation. I do need some help to point out what's wrong with the program basically.... Just give me some light...
Thanks very much!
#!/usr/bin/python2
import MySQLdb
def NewConnectToMySQL():
conn = MySQLdb.Connect("localhost", "root", "mypassword")
return conn
def CreateDBIfNotExists(conn):
sql = "CREATE DATABASE IF NOT EXISTS testdb"
cur = conn.cursor()
cur.execute(sql)
cur.close()
conn.select_db("testdb")
conn.commit()
"""workaround-1"""
#conn.close()
#conn = NewConnectToMySQL()
#conn.select_db("testdb")
return conn
def CreateTableIfNotExists(conn):
sql = "CREATE TABLE IF NOT EXISTS mytable (id INTEGER, name TEXT)"
cur = conn.cursor()
cur.execute(sql)
cur.close()
conn.commit()
def QueryName(conn, name):
sql = "SELECT * FROM mytable WHERE name = '%s'" % name
cur = conn.cursor()
cur.execute(sql)
info = cur.fetchall()
cur.close()
"""workaround-2"""
#conn.commit()
return info
conn1 = NewConnectToMySQL()
CreateDBIfNotExists(conn1)
CreateTableIfNotExists(conn1)
QueryName(conn1, "tom")
conn2 = NewConnectToMySQL()
CreateDBIfNotExists(conn2)
CreateTableIfNotExists(conn2) #hangs here!!!!!!!!!!
Here is the output of SHOW FULL PROCESSLIST when hangs.
mysql> SHOW FULL PROCESSLIST
-> ;
+-----+------+-----------+--------+---------+------+---------------------------------+------------------------------------------------------------+
| Id | User | Host | db | Command | Time | State | Info |
+-----+------+-----------+--------+---------+------+---------------------------------+------------------------------------------------------------+
| 720 | root | localhost | testdb | Sleep | 96 | | NULL |
| 721 | root | localhost | testdb | Query | 96 | Waiting for table metadata lock | CREATE TABLE IF NOT EXISTS mytable (id INTEGER, name TEXT) |
| 727 | root | localhost | NULL | Query | 0 | NULL | SHOW FULL PROCESSLIST |
+-----+------+-----------+--------+---------+------+---------------------------------+------------------------------------------------------------+
3 rows in set (0.00 sec)

How to achieve default value if column value is NULL?

I want to retrieve some column values from table with these conditions.
If value is NULL (or) Empty String , return some user defined value
If not above condition , return it's value.
How can I figure it out ?
Here is my Table query..
CREATE TABLE AUCTION_CAR_BID(
bid_seq bigint NOT NULL AUTO_INCREMENT,
auction_car_seq bigint NOT NULL,
bid_group_seq bigint NOT NULL,
bid_price int DEFAULT 0 NOT NULL,
over_bid_price int DEFAULT -1 NOT NULL,
result_id int DEFAULT 0 NOT NULL,
remark varchar(500),
PRIMARY KEY (bid_seq))
ENGINE = InnoDB DEFAULT CHARACTER SET utf8;
Here is my efforted codes to get it..
SELECT
COALESCE(OVER_BID_PRICE, -1)
FROM
AUCTION_CAR_BID
WHERE
BID_SEQ = 2354435345;
Another :
SELECT
CASE
WHEN OVER_BID_PRICE IS NULL
OR TRIM(OVER_BID_PRICE) = '' THEN -1
ELSE OVER_BID_PRICE
END OVER_BID_PRICE
FROM
AUCTION_CAR_BID
WHERE
BID_SEQ = 2354435345;
But I always get empty String value(not -1) if given id is not in my table.
Any suggestions would be really appreciated !
If you write this:
SELECT
COALESCE(OVER_BID_PRICE, -1)
FROM
AUCTION_CAR_BID
WHERE
BID_SEQ = 2354435345;
The results can be two types.
First result: Your query no returns rows! Your WHERE condition is unsatisfact so you'll read NULL
Second result: Your query returns rows but the value of your field is NULL, your COALESCE works fine in this case
To resolve you can try this:
SELECT COALESCE(
(SELECT
COALESCE(OVER_BID_PRICE, -1)
FROM AUCTION_CAR_BID
WHERE BID_SEQ = 2354435345)
,-1);
Tell me if it's OK
How about this:
select
case when price is null or id <> 1
then -1
else price
end price
from mytable
DROP TABLE prices;
CREATE TABLE prices (price_id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,price INT NULL);
INSERT INTO prices (price) VALUES (' '),(''),(NULL);
SELECT * FROM prices;
+----------+-------+
| price_id | price |
+----------+-------+
| 1 | 0 |
| 2 | 0 |
| 3 | NULL |
+----------+-------+
SELECT price_id,COALESCE(price,-1) price FROM prices;
+----------+-------+
| price_id | price |
+----------+-------+
| 1 | 0 |
| 2 | 0 |
| 3 | -1 |
+----------+-------+
If there's no row for USER_SEQ = 2354435345 in your table there's no row returned. But aggregate functions always return a row even if the result is empty :-)
SELECT
COALESCE(MIN(OVER_BID_PRICE), -1)
FROM
USER_PARAM
WHERE
USER_SEQ = 2354435345;

How do I insert a NULL value in FUEL / ActiveRecord

Brief forward: I tried to ask this on the FUEL forums, but every time I try and register, their forum says "Failed sending activation email" and I can't log in or reset my account. So hopefully folks here will check it out. I saw some of the developers of FUEL on this site before.
Here is an example mysql table:
CREATE TABLE `test` (
`user_id` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`username` VARCHAR( 128 ) NULL ,
`last_login` DATETIME NULL
) ENGINE = InnoDB
Evidently, unlike a lot of folks, I personally like to take advantage of the NULL value of databases. In layman's terms, NULL means it's never been initialized with a value. In this case if our user has never logged in, I want that reflected in his record by having the last_login value equal NULL. So, NULL = "never logged in."
If I was to add a new user to my database via the command line or through something like phpMyAdmin, I'd type in the following query.
INSERT INTO `test` (
`user_id` ,
`username` ,
`last_login`
)
VALUES (
NULL , 'test_person_1', NULL
);
This is the result of that query.
+---------+---------------+--------------+
| user_id | username | last_login |
+---------+---------------+--------------+
| 1 | test_person_1 | NULL |
+---------+---------------+--------------+
Now, let's use FUEL's ActiveRecord
Here is my model (simple enough):
<?php
class Model_Test extends ActiveRecord\Model {
public $primary_key = 'user_id';
public $table_name = 'test'; // Why does fuel want to pluralize table names? Grr.
}
And here is a super-basic example of a controller method that inserts a record into the table. I know I would never want an action that repeatedly inserts the same data over and over again. This is just a test. Cool?
public function action_save_example1()
{
$o_user = new Model_Test(array(
'username' => 'test_person_2',
));
}
And this is what I get after running that method:
mysql> select * from test;
+---------+---------------+---------------------+
| user_id | username | last_login |
+---------+---------------+---------------------+
| 1 | test_person_1 | NULL |
| 2 | test_person_2 | 0000-00-00 00:00:00 |
+---------+---------------+---------------------+
2 rows in set (0.00 sec)
Notice that test_person_2's DATETIME field is "0000-00-00 00:00:00" That's not NULL. Even if I specifically state that last_login is null, FUEL's ActiveRecord class makes in not null. Example.
public function action_save_example1()
{
$o_user = new Model_Test(array(
'username' => 'test_person_2',
'last_login' => NULL
));
}
I have a feeling that this is the query that ActiveRecord is running.
INSERT INTO `portal_links`.`test` (
`user_id` ,
`username` ,
`last_login`
)
VALUES (
NULL , 'test_person_2', ''
);
There needs to be some sort of logic to test if a value === NULL before inserting or updating and if it is NULL, it should use the keyword NULL and not ''. CodeIgniter's ActiveRecord class seems to understand the difference between NULL and ''.
<?php
// CodeIgniter Example
$o_query = $this->db->insert('test', array(
'user_id' => NULL,
'username' => 'code_igniter_user',
'last_login' => NULL
));
Not that this is the answer to your question. But AR is or just has lost support. Look into the new orm package. Docs are in the making.
Good luck.