6

Been looking around the web for a while now and don't seem to be able to come across anything similar to what I want. I know it's something to do with the way I'm writing my query but any help would be appreciated.

The basics of what I am trying to do is:

  • Insert some items into a table if it does not exist
  • Update an item if it does exist

It exists in the format:

name, barcode, item, quantity, location, price and date

name - can be used in several rows barcode - is for a specific item but can be used as several locations item - is the same as barcode but contains the name quantity - self explanatory location - this can be different locations price - that is attached to a specific item date - last time that item was purchased

The tricky thing is, a "name" can have several items (barcode and item) at different locations for different prices. The idea is that a customer can see how much they bought an item for at a set time, so they know how much they would need to sell it for.

However the price that they bought it at can vary so they need to create another row in the table if the price is different from a previous purchase.

The idea behind the whole thing is for it to record how much a "name" has of each item at each location and then the price they bought it at and when they last purchased it.

Hope that makes sense.

In psuedo code:

    Insert into table if does not exist
    - name, barcode, item, quantity, location, price and date
    If name, barcode, item, location and price are the same
    - Update quantity and date (if more recent)
roadkill247
  • 213
  • 1
  • 5
  • 12

3 Answers3

14

First, add a UNIQUE constraint on name, barcode, item, location and price.

ALTER TABLE  tableX
  ADD CONSTRAINT tableX_UQ
    UNIQUE (name, barcode, item, location, price) ;

Then you can use INSERT INTO ... ON DUPLICATE KEY UPDATE:

INSERT INTO tableX
  (name, barcode, item, location, price, quantity, date)
VALUES
  (?, ?, ?, ?, ?, ?, ?)
ON DUPLICATE KEY UPDATE
  quantity = CASE WHEN VALUES(date) > date
               THEN quantity + VALUES(quantity)        -- add quantity
               ELSE quantity                           -- or leave as it is
             END
, date = CASE WHEN VALUES(date) > date
               THEN VALUES(date) ;                     -- set date to new date
               ELSE date                               -- or leave as it is
             END 

REPLACE could also be used but there are differences in the behaviour (which especially matter if you have foreign keys). For details, see this question “INSERT IGNORE” vs “INSERT … ON DUPLICATE KEY UPDATE” and the answer by @Bill Kawin which discusses the differences between INSERT IGNORE, INSERT ... ON DUPLICATE KEY and REPLACE.

Community
  • 1
  • 1
ypercubeᵀᴹ
  • 113,259
  • 19
  • 174
  • 235
  • Fantastic, managed to get that working however, if I only want it to update IF the date is newer then the existing one, what would need to be added ? – roadkill247 Jan 10 '13 at 14:16
  • And if it isn't newer, leave the date as it is? And change only quantity? Or leave both unaltered (so do nothing at all in that case)? – ypercubeᵀᴹ Jan 10 '13 at 14:39
  • Hi ypercude, yeah if the date isn't any newer then leave both unaltered – roadkill247 Jan 11 '13 at 09:05
  • Then this (edited) version should work. I'm not sure if you want to add quantities (THEN `quantity + VALUES(quantity)`) or just `THEN VALUES(quantity)` when the date is newer but that you can take care of. – ypercubeᵀᴹ Jan 11 '13 at 09:09
  • have had another issue using this. Have come across something interesting, when I used a small amount of values it works fine, however on large amounts the quantity is updated even if the date is equal the last date used – roadkill247 Jan 11 '13 at 11:22
  • What datatype is the column `date`? And did you use this version or the one in your answer? – ypercubeᵀᴹ Jan 11 '13 at 11:23
  • date is a VARCHAR and then the query contained in my answer below as it is read as "2013-01-09 06:20:50" – roadkill247 Jan 11 '13 at 12:30
  • So, do you want to compare dates or datetimes? – ypercubeᵀᴹ Jan 11 '13 at 12:33
  • Also: your query and mine will have different behaviour when the new date(time) provided is exactly equal to the existing datetime. – ypercubeᵀᴹ Jan 11 '13 at 12:34
  • ok for some strange reason if I can the "<" on the quantity query to ">" solves all the issues are larger amounts. So we are good to go, thanks for your patience – roadkill247 Jan 11 '13 at 12:40
  • Hi I have similar issue. I also want to Insert items into a table if it does not exist or Update an item if it does exist. my query is is **insert into tablename (col1, col2,col3, col4, col5,col6,col7,clo8) values (v1,v2,v3,v4,v5,v6,v7,v8) on duplicate key UPDATE col5,col6,col7,col8** but it still inserting the new row not updating the existing row. please guide where I am doing wrong. – Sandeep Singh Feb 05 '14 at 14:24
5

You could use the replace syntax in mysql.

But that would work only with a unique index on... the fields needing to be unique.

So you'd have to create a unique index like that :

alter <yourtable> add unique index(name, barcode, item, location, price);

then your insert/update syntax would become

replace into <yourtable> (name, barcode, item, quantity, location, price, date)
 VALUES('name1', 'barcode1', 2, 'location1', 12.0, '25/12/2012');

EDIT

Example of stored procedure (simplified and untested) :

DELIMITER &&
DROP PROCEDURE IF EXISTS MyBase.UpdateOrInsert $$
CREATE PROCEDURE MyBase.UpdateOrInsert
(
  IN _name VARCHAR(10),
  IN _barcode VARCHAR(50),
  IN _quantity INTEGER
  IN _date DATE
)

DECLARE existingDate DATE default NULL;
BEGIN

SELECT date 
INTO existingDate
FROM <yourTable> where name = _name and barcode = _barcode;

if (existingDate IS NULL) then
   insert into <yourtable> (name, barcode, quantity, date) VALUES(_name, _barcode, _qantity, _date);
else
  if (existingDate < _date) then
     update <yourtable> 
     set quantity = _quantity,
       date = _date
     where name = _name
     and   barcode = _barcode;
  end if;
end if;
END &&
Raphaël Althaus
  • 59,727
  • 6
  • 96
  • 122
  • Thanks for the reply, another useful answer, however if i wanted to add in addition to this that the quantity only be updated if the date is newer then the current one in the table? As I'm parsing an XML file so some of the data is re-read – roadkill247 Jan 10 '13 at 14:17
  • @roadkill247 I think you can't do this with either ypercube's answer nore mine... I would say you'd have to go for a stored procedure. Or manage that in the parsing of your xml file, before going to database... Cause unique index (which is the base of both answer) can manage uniqueness, not "greater" / "lower" conditions. – Raphaël Althaus Jan 10 '13 at 14:21
  • Thanks for the quick reply. The XML I can't change as it is provided by a 3rd party so when you say a stored procedure what exactly do you mean? – roadkill247 Jan 10 '13 at 14:29
  • @roadkill247 I don't mean you need to change the xml, but work with the "serialized" result of your xml. Anyway, a stored procedure is... a procedure (method) that you can implement and call in a db like mysql. Google "stored procedure mysql" to start ;) – Raphaël Althaus Jan 10 '13 at 14:32
  • @roadkill247 I added a start of an example of a SP. – Raphaël Althaus Jan 10 '13 at 14:49
  • @roadkill247: If you decide to go with a REPLACE solution, make sure you are aware of its possible side effects. Take a look at this question: [What are practical differences between `REPLACE` and `INSERT … ON DUPLICATE KEY UPDATE` in MySQL?](http://stackoverflow.com/questions/9168928/what-are-practical-differences-between-replace-and-insert-on-duplicate-ke) – Andriy M Jan 10 '13 at 15:25
  • thanks for all your help, nearly got it working with a stored procedure, but will look into more, thanks for the help! – roadkill247 Jan 11 '13 at 09:55
1

Thanks to all the help from those above the solution I found in the end was very close to both. Find it below:

INSERT INTO `stock`
    (name, barcode, item, quantity, location, price, date)  
VALUES
    (?,?,?,?,?,?,?)                         
ON DUPLICATE KEY UPDATE
     quantity = CASE WHEN 
                VALUES(date) < $date
                THEN quantity + $quantity
                ELSE quantity 
                END,
    date = CASE WHEN 
                VALUES(date) < $date
                THEN VALUES(date)
                ELSE $date
                END
roadkill247
  • 213
  • 1
  • 5
  • 12
  • although using this have come across something interesting, when I used a small amount of values it works fine, however on large amounts the quantity is updated even if the date is equal the last date used – roadkill247 Jan 11 '13 at 11:09