MySQL在值更改时插入新行

ste*_*esu 2 mysql sql perl sql-update

对于我正在进行的个人项目,我想在Steam,Impulse,EA Origins和其他几个站点上制作游戏价格线图.目前,我已经修改了SteamCalculator.com使用的脚本,以记录每个国家/地区代码中的每个游戏或每个网站的当前价格(如果适用的话,销售价格).我还有一个列存储价格的日期.我当前的表看起来像这样:

THIS STRUCTURE IS NO LONGER VALID. SEE BELOW
+----------+------+------+------+------+------+------+------------+
| steam_id |  us  |  at  |  au  |  de  |  no  |  uk  |    date    |
+----------+------+------+------+------+------+------+------------+
|  112233  |  999 |  899 |  999 | NULL |  899 |  699 |  2011-8-21 |
|  123456  | 1999 |  999 | 1999 |  999 |  999 |  999 |  2011-8-20 |
|    ...   |  ... |  ... |  ... |  ... |  ... |  ... |     ...    |
+----------+------+------+------+------+------+------+------------+
Run Code Online (Sandbox Code Playgroud)

目前每个国家/地区都是单独更新的(有一个for循环通过这些国家/地区),但如果它会简化它,那么可以修改它以暂时将新价格存储到数组中,然后一次更新整行.无论如何,出于性能原因,我最终可能会这样做.

现在我的问题是确定如果其中一个价格发生变化,如何最好地更新此表.例如,我们假设2011年8月22日该游戏112233在美国以4.99美元的价格开售,奥地利的价格为3.99欧元,其他价格保持不变.我需要这个表看起来像这样:

THIS STRUCTURE IS NO LONGER VALID. SEE BELOW
+----------+------+------+------+------+------+------+------------+
| steam_id |  us  |  at  |  au  |  de  |  no  |  uk  |    date    |
+----------+------+------+------+------+------+------+------------+
|  112233  |  999 |  899 |  999 | NULL |  899 |  699 |  2011-8-21 |
|  123456  | 1999 |  999 | 1999 |  999 |  999 |  999 |  2011-8-20 |
|    ...   |  ... |  ... |  ... |  ... |  ... |  ... |     ...    |
|  112233  |  499 |  399 |  999 | NULL |  899 |  699 |  2011-8-22 |
+----------+------+------+------+------+------+------+------------+
Run Code Online (Sandbox Code Playgroud)

我不希望创建一个新的行EVERY价格检查时间,否则我将结束一天后,有上百万重复价格一天行.我也不想为每个更改的价格创建一个新行,如下所示:

THIS STRUCTURE IS NO LONGER VALID. SEE BELOW
+----------+------+------+------+------+------+------+------------+
| steam_id |  us  |  at  |  au  |  de  |  no  |  uk  |    date    |
+----------+------+------+------+------+------+------+------------+
|  112233  |  999 |  899 |  999 | NULL |  899 |  699 |  2011-8-21 |
|  123456  | 1999 |  999 | 1999 |  999 |  999 |  999 |  2011-8-20 |
|    ...   |  ... |  ... |  ... |  ... |  ... |  ... |     ...    |
|  112233  |  499 |  899 |  999 | NULL |  899 |  699 |  2011-8-22 |
|  112233  |  499 |  399 |  999 | NULL |  899 |  699 |  2011-8-22 |
+----------+------+------+------+------+------+------+------------+
Run Code Online (Sandbox Code Playgroud)

我可以通过使每个问题(steam_id, <country>)成为唯一索引然后添加ON DUPLICATE KEY UPDATE到每个数据库查询来防止第一个问题而不是第二个问题.如果价格不同,这只会添加一行,但是会为每个更改的国家/地区添加新行.它也不允许在两个不同的日子里为单个游戏提供相同的价格(例如,假设游戏112233稍后销售并返回到9.99美元),所以这显然是一个糟糕的选择.

我可以通过创建(steam_id, date)唯一索引然后添加ON DUPLICATE KEY UPDATE到每个查询来防止第二个问题但不是第一个问题.运行脚本的每一天日期都已更改,因此它将创建一个新行.这种方法每天都会有数百条相同价格的生产线.

如果(并且仅当)自最近日期以来任何价格发生变化,我如何告诉MySQL创建新行?

更新 -

根据此线程中人员的建议,我已更改了数据库的架构,以便将来添加新的国家/地区代码,并避免一次需要更新整个行的问题.新架构看起来像:

+----------+------+---------+------------+
| steam_id |  cc  |  price  |    date    |
+----------+------+---------+------------+
|  112233  |  us  |   999   |  2011-8-21 |
|  123456  |  uk  |   699   |  2011-8-20 |
|    ...   |  ... |   ...   |     ...    |
+----------+------+---------+------------+
Run Code Online (Sandbox Code Playgroud)

在这个新架构的基础上,我发现我可以使用以下SQL查询来获取最新更新的价格:

SELECT `price` FROM `steam_prices` WHERE `steam_id` = 112233 AND `cc`='us' ORDER BY `date` ASC LIMIT 1
Run Code Online (Sandbox Code Playgroud)

在这一点上,我的问题归结为:

是否可以(仅使用SQL而不是应用程序逻辑)仅在条件为真时插入行?例如:

INSERT INTO `steam_prices` (...) VALUES (...) IF price<>(SELECT `price` FROM `steam_prices` WHERE `steam_id` = 112233 AND `cc`='us' ORDER BY `date` ASC LIMIT 1)
Run Code Online (Sandbox Code Playgroud)

MySQL手册我找不到任何方法来做到这一点.我发现如果唯一索引相同,您可以忽略或更新.但是,如果我将价格作为一个独特的索引(允许我更新日期,如果它是相同的),那么我将无法识别游戏何时开始销售,然后返回其原始价格.例如:

+----------+------+---------+------------+
| steam_id |  cc  |  price  |    date    |
+----------+------+---------+------------+
|  112233  |  us  |   999   |  2011-8-20 |
|  112233  |  us  |   499   |  2011-8-21 |
|  112233  |  us  |   999   |  2011-8-22 |
|    ...   |  ... |   ...   |     ...    |
+----------+------+---------+------------+
Run Code Online (Sandbox Code Playgroud)

此外,在找到并阅读MySQL Conditional INSERT之后,我创建并尝试了以下查询:

INSERT INTO `steam_prices`(
    `steam_id`,
    `cc`,
    `update`,
    `price`
)
SELECT '7870', 'us', NOW(), 999
FROM `steam_prices`
WHERE
    `price`<>999
    AND `update` IN (
        SELECT `update`
        FROM `steam_prices`
        ORDER BY `update`
        ASC LIMIT 1
    )
Run Code Online (Sandbox Code Playgroud)

想法是插入行,'7870', 'us', NOW(), 999如果(并且仅当)price最近的行update不是999.当我运行它时,我得到以下错误:

1235 - 此版本的MySQL尚不支持'LIMIT&IN/ALL/ANY/SOME子查询'

有任何想法吗?

pax*_*blo 6

如果您只是将模式更改为以下内容,您可能会发现这更容易:

steam_id      integer
country       varchar(2)
date          date
price         float
primary key   (steam_id,country,date)
Run Code Online (Sandbox Code Playgroud)

(与其他适当的指数)然后只是依次担心每个国家.

换句话说,你的for循环有一个唯一的ID /国家组合,所以它可以简单地查询该组合的最新日期记录,如果它不同则添加一个新行.

这将使您的选择更加复杂,但我相信这是一个更好的解决方案,特别是如果有任何机会可以在未来添加更多国家(在这种情况下不会破坏架构).