I am using mysql+php. The php provides interface to import XLSX table of goods into Mysql table(s). I am using a temporary table created with CREATE TABLE LIKE i.e. empty clone of live table for user evaluation before it is merged with live table
I am using INSERT INTO finalTable SELECT * FROM importTable ON DUPLICATE KEY UPDATE .... to avoid INSERT for every record. The fields to be updated on duplicate key are determined by php with SHOW COLUMNS i.e. all columns from the temporary table.
My problem is that the XLSX does not necessarily contain ALL the fields(name, price, category) i.e. it may be just an update of existing records. The current method will update ALL fields no matter if they were set or not during the import i.e. if the XLSX contained only price update, the rest of fields is null and would overwrite current values.
I tought to alter the temporary table by removing columns(not rows!) that are null for all rows. This way the SHOW COLUMNS would return only update-able columns. If a fields needs to be zeroed, it would be easy to set a special value e.g. "!X!" and use single UPDATE sentence to do so.
Is there a method for this or do you have any better suggestion(I am open to abando ON DUPLICATE KEY too)?
I'm a bit confused by your question.
I am using INSERT ... ON DUPLICATE KEY UPDATE to avoid INSERT for every record.
Like it says, it inserts or updates, you don't avoid anything. If you want to avoid inserts use INSERT IGNORE.
If I were you, I'd merge the tables in two steps.
First step, the insert (just the necessary ones):
INSERT INTO finalTable (col1, col2, ...)
SELECT i.col1, i.col2
FROM importTable i
LEFT JOIN finalTable f ON i.ID = f.ID
WHERE f.ID IS NULL;
Second step, the update:
I don't understand, why you want to delete columns. Unnecessary step which might take a while, and most importantly, your problem of updating with NULL still persists if just a few rows in a column are NULL in your import table. So, here's the solution.
UPDATE finalTable f
INNER JOIN importTable i ON f.ID = i.ID
SET f.col1 = COALESCE(i.col1, f.col1),
f.col2 = COALESCE(i.col2, f.col2),
...;
The trick in the second step is, to use COALESCE(). This function returns the first of its parameters which is not null. So if you have a column which is null in your import table, the value in the final table stays as it is.
UPDATE:
If you insist on having just one statement, you can of course do
INSERT INTO final f
SELECT * FROM import i
ON DUPLICATE KEY UPDATE
SET f.col1 = COALESCE(i.col1, f.col1),
f.col2 = COALESCE(i.col2, f.col2),
...;
Related
I need to add a CSV file in a database. But after I add one file, some weeks later I need to add a new update of the file. Problem is: Duplicate rows are added.
I don't have an ID for rows so I have to see if they are the same with 'City', 'Address' and 'Location Name'. Only if the 3 are matching then we dont put the new row in database.
I tried IGNORE but it seems to only work whit an ID as primary key (and I don't have primary key).
I also read a 'multiple primary key' thread but I did not succeed to create it.
My actual code is (Codeigniter):
$query = $this->db->query('
LOAD DATA INFILE "'.$path.'fichier/'.$fichier.'"
INTO TABLE location FIELDS TERMINATED BY ";"
LINES TERMINATED BY "'.$os2.'"
IGNORE 1 LINES ('.$name[1].','.$name[2].','.$name[3].','.$name[4].','.$name[5].','.$name[6].','.$name[7].','.$name[8].','.$name[9].','.$name[10].','.$name[11].','.$name[12].','.$name[13].','.$name[14].','.$name[15].','.$name[16].','.$name[17].','.$name[18].','.$name[19].')');
I would say, best would be to load your updated CSV in a staging table. Once all data loaded, do a LEFT JOIN with your actual table and find out all the new records and then insert only those new records to your main table (OR) Once all data loaded, flush all the data in main table with this new staging table.
Per your comment:
Yes, one you have loaded data to new table perform a LEFT JOIN with your main table (something like below)
select staging_table.id
from staging_table
left join main_table on staging_table.id = main_table.id
where main_table.id is null;
If someone want to know how I finally succeed:
I created a unique in phpmyadmin. And then I used a IGNORE on my request.
ALTER TABLE location
ADD CONSTRAINT iu_location UNIQUE( col1, col2, col3 );
UPDATE AggregatedData SET datenum="734152.979166667",
Timestamp="2010-01-14 23:30:00.000" WHERE datenum="734152.979166667";
It works if the datenum exists, but I want to insert this data as a new row if the datenum does not exist.
UPDATE
the datenum is unique but that's not the primary key
Jai is correct that you should use INSERT ... ON DUPLICATE KEY UPDATE.
Note that you do not need to include datenum in the update clause since it's the unique key, so it should not change. You do need to include all of the other columns from your table. You can use the VALUES() function to make sure the proper values are used when updating the other columns.
Here is your update re-written using the proper INSERT ... ON DUPLICATE KEY UPDATE syntax for MySQL:
INSERT INTO AggregatedData (datenum,Timestamp)
VALUES ("734152.979166667","2010-01-14 23:30:00.000")
ON DUPLICATE KEY UPDATE
Timestamp=VALUES(Timestamp)
Try using this:
If you specify ON DUPLICATE KEY UPDATE, and a row is inserted that would cause a duplicate value in a UNIQUE index orPRIMARY KEY, MySQL performs an [UPDATE`](http://dev.mysql.com/doc/refman/5.7/en/update.html) of the old row...
The ON DUPLICATE KEY UPDATE clause can contain multiple column assignments, separated by commas.
With ON DUPLICATE KEY UPDATE, the affected-rows value per row is 1 if the row is inserted as a new row, 2 if an existing row is updated, and 0 if an existing row is set to its current values. If you specify the CLIENT_FOUND_ROWS flag to mysql_real_connect() when connecting to mysqld, the affected-rows value is 1 (not 0) if an existing row is set to its current values...
This is not too bad, but we could actually combine everything into one query. I found different solutions on the internet. The simplest, but MySQL only solution is this:
INSERT INTO wp_postmeta (post_id, meta_key)
SELECT
?id,
‘page_title’
FROM
DUAL
WHERE
NOT EXISTS (
SELECT
meta_id
FROM
wp_postmeta
WHERE
post_id = ?id
AND meta_key = ‘page_title’
);
UPDATE
wp_postmeta
SET
meta_value = ?page_title
WHERE
post_id = ?id
AND meta_key = ‘page_title’;
Link to documentation.
I had a situation where I needed to update or insert on a table according to two fields (both foreign keys) on which I couldn't set a UNIQUE constraint (so INSERT ... ON DUPLICATE KEY UPDATE won't work). Here's what I ended up using:
replace into last_recogs (id, hasher_id, hash_id, last_recog)
select l.* from
(select id, hasher_id, hash_id, [new_value] from last_recogs
where hasher_id in (select id from hashers where name=[hasher_name])
and hash_id in (select id from hashes where name=[hash_name])
union
select 0, m.id, h.id, [new_value]
from hashers m cross join hashes h
where m.name=[hasher_name]
and h.name=[hash_name]) l
limit 1;
This example is cribbed from one of my databases, with the input parameters (two names and a number) replaced with [hasher_name], [hash_name], and [new_value]. The nested SELECT...LIMIT 1 pulls the first of either the existing record or a new record (last_recogs.id is an autoincrement primary key) and uses that as the value input into the REPLACE INTO.
i have a contactnumber column in mysql database. In contactnumber column there are more than 20,000 entries. Now when i upload new numbers through .csv file, i dont want duplicate numbers in database.
How can i avoid duplicate numbers while inserting in database.
I initially implemented logic that checks each number in .csv file with each of the number in database.
this works but takes lot of time to upload .csv file containing 1000 numbers.
Pleae suggest how to minimize time required to upload .csv file while not uploading duplicate values.
Simply add a UNIQUE constraint to the contactnumber column:
ALTER TABLE `mytable` ADD UNIQUE (`contactnumber`);
From there you can use the IGNORE option to ignore the error you'd usually be shown when inserting a duplicate:
INSERT IGNORE INTO `mytable` VALUES ('0123456789');
Alternatively, you could use the ON DUPLICATE KEY UPDATE to do something with the dupe, as detailed in this question: MySQL - ignore insert error: duplicate entry
If your contactnumber should not be repeated then make it PRIMARY or at least a UNIQUE key. That way when a value is being inserted as a duplicate, insert will fail automatically and you won't have to check beforehand.
The way I would do it is to create a temporary table.
create table my_dateasyyyymmddhhiiss as select * from mytable where 1=0;
Do your inserts into that table.
and then query out the orphans on the between mytable and the temp table based on contactnumber
then run an inner join query between the two tables and fetch out the duplicate for your telecaller tracking.
finally drop the temporary table.
Thing that this does not address are duplicates within the supplied file (don't know if that would be an issue in this problem)
Hope this help
If you don't want to insert duplicate values in table and rather wants to keep that value in different table.
You can create trigger on table.
like this:
DELIMITER $$
CREATE TRIGGER unique_key BEFORE INSERT ON table1
FOR EACH ROW BEGIN
DECLARE c INT;
SELECT COUNT(*) INTO c FROM table1 WHERE itemid = NEW.itemid;
IF (c > 0) THEN
insert into table2 (column_name) values (NEW.itemid);
END IF;
END$$
DELIMITER ;
I would recommend this way
Alter the contactnumber column as UNIQUE KEY
Using phpmyadmin import the .csv file and check the option 'Do not abort on INSERT error' under Format-Specific Options before submitting
I have two tables in my database, table1 and table2. They are identical. But sometimes I change the data in table1.
How do I copy the data from table1 and update table2 to look the same?
I tried REPLACE INTO table2 SELECT * FROM table1 but it works like INSERT and just make new rows instead of updating the existing ones.
For REPLACE INTO to work as intended, the destination table must have a primary key defined, otherwise MySQL cannot determine whether a row already exists and always assumes a new row. As a result, for tables without a primary key, REPLACE INTO acts exactly like INSERT INTO.
Alternatively, you can use two queries, one UPDATE and one INSERT, with appropriate WHERE (NOT) EXISTS clauses. The advantage of this is that it's portable (REPLACE INTO is a MySQL extension).
Another alternative is to run two commands...
truncate table table2;
insert into table2 select * from table1;
I have a simple table made up of two columns: col_A and col_B.
The primary key is defined over both.
I need to update some rows and assign to col_A values that may generate duplicates, for example:
UPDATE `table` SET `col_A` = 66 WHERE `col_A` = 70
This statement sometimes yields a duplicate key error.
I don't want to simply ignore the error with UPDATE IGNORE, because then the rows that generate the error would remain unchanged. Instead, I want them to be deleted when they would conflict with another row after they have been updated
I'd like to write something like:
UPDATE `table` SET `col_A` = 66 WHERE `col_A` = 70 ON DUPLICATE KEY REPLACE
which unfortunately isn't legal in SQL, so I need help finding another way around.
Also, I'm using PHP and could consider a hybrid solution (i.e. part query part php code), but keep in mind that I have to perform this updating operation many millions of times.
thanks for your attention,
Silvio
Reminder: UPDATE's syntax has problems with joins with the same table that is being updated
EDIT: sorry, the column name in the WHERE clause was wrong, now I fixed it
Answer to revised question:
DELETE FROM
table_A
USING
table AS table_A
JOIN table AS table_B ON
table_A.col_B = table_B.col_B AND
table_B.col_A = 70
WHERE
table_A.col_A = 66
This gets rid of the rows that would cause problems. Then you issue your UPDATE query. Ideally you will do it all inside a transaction to avoid a situation where troublesome rows are re-inserted in between the two queries.
Are there any foreign keys referencing this table? If not then the following should do:
CREATE PROCEDURE `MyProcedure` (IN invarA INT, IN invarB INT)
LANGUAGE SQL
NOT DETERMINISTIC
MODIFIES SQL DATA
SQL SECURITY DEFINER
BEGIN
DELETE FROM table WHERE col_B = invarB;
IF ROW_COUNT() > 0 THEN
INSERT INTO table (`col_A`, `col_B`) VALUES (invarA, invarB);
END IF;
END
Example call:
CALL `MyProcedure`(66, 70)