我正在寻找一种方法来输出MySQL中表中每条记录的选定相关记录。我会进一步解释......
我有2个表货币和 exchange_rates 。这些表由 currency_code 字段连接,每个货币记录都有多个相关的汇率记录,每个汇率记录代表不同的一天。因此,货币和exchange_rates之间存在1:多的关系。
我想从每个货币的exchange_rates
表中检索完整记录,但能够定义关于要选择的相关记录的特定标准。不仅是每种货币的最新exchange_rate,而且可能是具有字段exchange_rates
的每种货币的最新criteria_x=NULL
记录。
遗憾的是你不能在派生表中使用LIMIT
,否则这样的东西会是一个整洁可读的解决方案......
SELECT `currencies`.`currency_code`, `currencies`.`country`, `exchange_rates`.`id`,
FROM_UNIXTIME(`exchange_rates`.`datestamp`), `rate`
FROM `currencies`
INNER JOIN (
SELECT `id`, `currency_code`, `invoice_id`, `datestamp`, `rate`
FROM `exchange_rates`
WHERE `criteria_x`=NULL AND `criteria_y` LIKE 'A'
ORDER BY `datestamp` DESC
LIMIT 0, 1
) AS `exchange_rates` ON `currencies`.`currency_code`=`exchange_rates`.`currency_code`
ORDER BY `currencies`.`country`
LIMIT
子句应用于父查询而不是派生表。
这是我发现这样做的唯一方法......
SELECT `currencies`.`currency_code`, `currencies`.`country`,
FROM_UNIXTIME( SUBSTRING_INDEX( SUBSTRING_INDEX(`exchange_rates`.`concat`, '-', 1), '-', -1)) AS `datestamp`,
SUBSTRING_INDEX( SUBSTRING_INDEX(`exchange_rates`.`concat`, '-', 2), '-', -1) AS `id`,
SUBSTRING_INDEX( SUBSTRING_INDEX(`exchange_rates`.`concat`, '-', 3), '-', -1) AS `rate`
FROM `currencies`
INNER JOIN (
SELECT `currency_code`, MAX(CONCAT_WS('-', `datestamp`, `id`, `rate`)) AS `concat`
FROM `exchange_rates`
WHERE `criteria_x`=NULL AND `criteria_y` LIKE 'A'
GROUP BY `exchange_rates`.`currency_code`
) AS `exchange_rates` ON `currencies`.`currency_code`=`exchange_rates`.`currency_code`
ORDER BY `currencies`.`country`
将一堆字段连接在一起并在其上运行MAX()
以获取组内的排序顺序,然后使用SUBSTRING_INDEX()
在父查询中解析这些字段。问题是这个方法只有在我可以在连接字段上使用MIN()
或MAX()
时才有效。如果我想按字符串排序或按多个条件排序但限制为单个记录,那就不理想了。
另外,它让我身体上的痛苦不得不诉诸可怕的字符串操作来从关系数据库中获取我想要的数据 - 必须有更好的方法!
有人对任何更好的方法有任何建议吗?
答案 0 :(得分:3)
在尝试提供答案之前,有一些一般性问题需要讨论(简要)。
您的第一个疑问是:
SELECT `currencies`.`currency_code`, `currencies`.`country`, `exchange_rates`.`id`,
FROM_UNIXTIME(`exchange_rates`.`datestamp`), `rate`
FROM `currencies`
INNER JOIN (
SELECT `id`, `currency_code`, `invoice_id`, `datestamp`, `rate`
FROM `exchange_rates`
WHERE `criteria_x`=NULL AND `criteria_y` LIKE 'A'
ORDER BY `datestamp` DESC
LIMIT 0, 1
) AS `exchange_rates` ON `currencies`.`currency_code`=`exchange_rates`.`currency_code`
ORDER BY `currencies`.`country`
criteria_x = NULL
表示法;应该写成criteria_x IS NULL
。 MySQL可能允许它;只要您知道它是非标准的,您就可以使用它。LIKE 'A'
在标准SQL中不包含元字符(%
或_
),则该标准不合理。简单平等你会更好:= 'A'
。你的问题是:
我想从每个货币的
exchange_rates
表中检索完整记录,但能够定义关于要选择的相关记录的特定标准。不仅是每种货币的最新汇率,而且可能是具有字段criteria_x IS NULL
的每种货币的最新汇率。
因此,您希望为符合所需其他条件的每种货币选择最新的汇率记录。我们可以假设汇率表中currency_code
和datestamp
的组合存在唯一约束;这意味着总会有至多一个匹配的行。如果没有匹配的行,您没有指定应显示的内容;当然,内部联接根本不会列出该货币。
使用SQL查询,我通常会逐步构建和测试整个查询,为以前开发的已知可以工作并产生正确输出的查询添加额外的材料。如果它很简单和/或我收集了过多的傲慢,我会首先尝试一个复杂的查询,但是当(克星)它不起作用时,我会回到构建和测试过程。将其视为测试驱动(查询)开发。
SELECT id, currency_code, invoice_id, datestamp, rate
FROM exchange_rates
WHERE criteria_x IS NULL AND criteria_y = 'A'
ORDER BY currency_code, datestamp DESC
SELECT currency_code, MAX(datestamp)
FROM exchange_rates
WHERE criteria_x IS NULL AND criteria_y = 'A'
GROUP BY currency_code
SELECT x.id, x.currency_code, x.invoice_id, x.datestamp, x.rate
FROM exchange_rates AS x
JOIN (SELECT currency_code, MAX(datestamp) AS datestamp
FROM exchange_rates
WHERE criteria_x IS NULL AND criteria_y = 'A'
GROUP BY currency_code
) AS m
ON x.currency_code = m.currency_code AND x.datestamp = m.datestamp
这需要将currency表加入上一个查询的输出:
SELECT c.currency_code, c.country, r.id,
FROM_UNIXTIME(r.datestamp), r.rate
FROM currencies AS c
JOIN (SELECT x.id, x.currency_code, x.invoice_id, x.datestamp, x.rate
FROM exchange_rates AS x
JOIN (SELECT currency_code, MAX(datestamp) AS datestamp
FROM exchange_rates
WHERE criteria_x IS NULL AND criteria_y = 'A'
GROUP BY currency_code
) AS m
ON x.currency_code = m.currency_code AND x.datestamp = m.datestamp
) AS r
ON c.currency_code = r.currency_code
ORDER BY c.country
除了Oracle仅允许“) r
”而不是“) AS r
”用于表别名和使用FROM_UNIXTIME()
,我认为应该可以正常使用当前版本的几乎任何您需要提及的SQL DBMS。
由于最终查询中未返回发票ID,我们可以从中间查询的选择列表中删除它。一个好的优化器可能会自动完成。
如果您想要查看货币信息,即使没有符合条件的汇率,您也需要将最外层查询中的JOIN更改为LEFT JOIN(也称为LEFT OUTER JOIN)。如果您只想查看某个货币的子集,则可以在最后(最外层)查询阶段应用该过滤器,或者(如果过滤器基于汇率表中可用的信息,例如货币代码)最里面的子查询(最有效)或中间子查询(效率不高,除非优化器实现它可以将过滤器推送到最里面的子查询)。
正确性通常是主要标准;绩效是次要标准。但是,问题中提到了性能。第一条规则是测量此处显示的“简单”查询。只有当证明太慢时才需要进一步担心。当您需要担心时,您会检查查询计划,以查看是否存在缺少关键索引的情况。只有当查询仍然不够快时才开始尝试使用其他技巧。这些技巧往往对特定的DBMS非常具体。例如,可能存在可用于使DBMS以不同方式处理查询的优化器提示。
答案 1 :(得分:2)
如果我已正确理解您的问题,您只需自行加入exchange_rates
即可选择感兴趣的费率:
SELECT currencies.currency_code,
currencies.country,
exchange_rates.id,
FROM_UNIXTIME(exchange_rates.datestamp),
exchange_rates.rate
FROM currencies
JOIN (
SELECT currency_code, MAX(datestamp) AS datestamp
FROM exchange_rates
WHERE criteria_x IS NULL AND criteria_y LIKE 'A'
GROUP BY currency_code
) AS exchange_wantd USING (currency_code)
JOIN exchange_rates USING (currency_code, datestamp)
ORDER BY currencies.country
答案 2 :(得分:1)
尝试此查询。它可以正常工作,但如果你提供一些数据,我将能够正确地做到这一点
SELECT `currencies`.`currency_code` as `CurrencyCode`,
`currencies`.`country`,
FROM_UNIXTIME( SUBSTRING_INDEX( SUBSTRING_INDEX(`exchange_rates`.`concat`, '-', 1), '-', -1)) AS `datestamp`,
SUBSTRING_INDEX( SUBSTRING_INDEX(`exchange_rates`.`concat`, '-', 2), '-', -1) AS `id`,
SUBSTRING_INDEX( SUBSTRING_INDEX(`exchange_rates`.`concat`, '-', 3), '-', -1) AS `rate`,
(SELECT
MAX(CONCAT_WS('-', `datestamp`, `id`, `rate`)) AS `concat`
FROM `exchange_rates`
WHERE `criteria_x`= NULL
AND `criteria_y` LIKE 'A'
GROUP BY `exchange_rates`.`currency_code`
HAVING `exchange_rates`.`currency_code` =`CurrencyCode`
) as `Concat`
FROM `currencies`
ORDER BY `currencies`.`country`
答案 3 :(得分:0)
如果我没有正确理解你,如果你不强迫数据库像人一样思考,那么答案就很简单了。与Jonathan Leffler一样,我理解您打算“选择符合所需其他标准的每种货币的最新汇率记录”。
“最新”当然是问题,因为该信息未明确存储在数据库中,因此我们首先确定此信息。
SELECT currency_code, MAX(datestamp) AS datestamp FROM exchange_rates GROUP BY currency_code
我们将重复使用这个,所以我们给结果命名
(SELECT currency_code, MAX(datestamp) AS datestamp FROM exchange_rates GROUP BY currency_code) AS dates_we_want
拥有我们可能想要的所有信息的域名是date_we_want,currency和exchange_rates表格的每种可能组合的记录
(SELECT currency_code, MAX(datestamp) AS datestamp FROM exchange_rates GROUP BY currency_code) AS dates_we_want, currencies AS c, exchange_rates AS er
选择我们想要的记录:
匹配currency_codes
dates_we_want.currency_code = er.currency_core AND dates_we_want.currency_code = c.currency_core
最近的费率
dates_we_want.datestamp = er.datestamp
投射到结果中。你
想要从exchange_rates表中检索完整记录
简单地翻译为
er.*
将所有内容放在SELECT语句中,并为您提供更改以放置任意约束:
SELECT er.*
FROM
(SELECT currency_code, MAX(datestamp) AS datestamp
FROM exchange_rates GROUP BY currency_code
) AS dates_we_want,
currencies AS c, exchange_rates AS er
WHERE
dates_we_want.currency_code=er.currency_core
AND
dates_we_want.currency_code=c.currency_core
AND
dates_we_want.datestamp=er.datestamp
AND
`criteria_x`=NULL AND `criteria_y` LIKE 'A'