tags:

views:

485

answers:

1

Is it possible to do a select in an IF statement in ON DUPLICATE KEY UPDATE?

Lets say I want to setup shipping rates by zip code, and in case of duplicate zip code entered, I want to apply the highest rate to apply.

My database has a table of shipping rates and a table of zip codes. The zip code table has a foreign key referencing the shipping rates, and the zip code which has a unique index.

When there is a duplicate key, I want to set the shipping rate ID to the higher rate. Something like this:

INSERT INTO ZipCodes (ShippingRateID, Zip) VALUES (11, '13754') ON DUPLICATE KEY UPDATE ShippingRateID = IF((SELECT Rate FROM ShippingRates sr WHERE sr.ShippingRateID = ShippingRateID) > [current rate], ShippingRateID, VALUES(ShippingRateID))

When I try to execute this query, MySQL tells me "Subquery returned more than one record". Being that there is only one matching record in the ShippingRates table, I don't understand how more than one record was returned. What is actually happening is that in the sub-select, ShippingRateID is always referring to the ShippingRates table, and the ShippingRateID of the insert is not being used. So without using LIMIT, all the shipping rates are being returned. If I do use LIMIT 1, then I'll always get the first rate, which is wrong.

How do I tell the sub-select to use ShippingRateID from the insert?

+2  A: 
INSERT INTO ZipCodes
(ShippingRateID, Zip)
VALUES (11, '13754')
ON DUPLICATE KEY
UPDATE ShippingRateID = 
    (SELECT IF(Rate > [current rate], ShippingRateID, VALUES(ShippingRateID))
        FROM (
            SELECT ShippingRateID AS AltID, Rate
            FROM ShippingRates
        ) AS sr
        WHERE sr.AltID = ShippingRateID
        LIMIT 1
    )
chaos
How is your update different than mine?
Yisroel
It safeguards against the (nonsensical) multiple-row-retrieval problem and allows the ShippingRateID from the table to ever be retrieved.
chaos
I tested your update, it will always set it to the highest rate of any of the shipping rates, not the highest of the 2 rates that may apply.I dont see why the LIMIT should be needed at all, when the WHERE clause can only match one row. If it is matching more than one row, then clearly ShippingRateID is not being passed to the sub-select correctly.
Yisroel
Okay, edits made, please try new version. More referential issues with ShippingID resolved. The LIMIT 1 is apparently necessary because when setting a field to a subselect, mysql seems to analyze the subquery in terms of whether it ever could conceivably return more than 1 row, not whether it actually does in this case.
chaos
I didn't test this, but if I'm reading it right, the sub-select will always return 11. You are either returning Values(ShippingRateID) which is 11, or sr.ShippingRateID, which is equal to Values(ShippingRateID)
Yisroel
Yeah, I see that. It doesn't make any sense because your original query doesn't make any sense. What logic should determine what shipping rates are 'applicable' to the query?
chaos
Why doesn't my query make sense? All I want to do is determine the higher shipping rate, and update the zip code with the higher rate. So either be the new shipping rate is higher and I want to update the zip code with the new rate, which would be VALUES(ShippingRateID), or the old shipping rate is higher, in which case I don't want to change the shippingRateID
Yisroel
It doesn't make sense because all you're doing is checking to see whether the ShippingRateID you're already using has a higher rate set in its table than your [current rate] value, and if it's possible for [current rate] to be anything other than the rate from the ShippingRates table, it's not clear how. It seems like you have no clear idea of what *other* shipping rate(s) you want to check, so you're checking the one being set in the query, which is a no-op.
chaos
The zip code in the database already has a shipping rate applied, hence the ON DUPLICATE KEY. In this case, the zip code can have one of two shipping rates. I want to apply the higher rate. If the new rate is higher, i want to update the zip code to the new rate. If the rate previously applied to this zip code is higher, I want to keep the existing rate.
Yisroel
Aha, the previous rate. There's the missing piece. Give the current version a spin.
chaos
This query results in the following error: Unknown column 'ZipCodes.ShippingRateID' in 'where clause'
Yisroel
Oy freakin' vey. Try current version then.
chaos
A new MySQL error: You can't specify target table 'ZipCodes' for update in FROM clause
Yisroel
Wow. Okay, yet another version up. This one tries to use a subquery to get around the problem we otherwise have with what 'ShippingRateID' refers to.
chaos