我有2个表,交易和标识符。每个事务都有一个user_id,每个user_id可以有多个标识符,例如
交易
user_id | amount | timestamp
12 10.00 1234567890
17 5.00 1234567890
12 7.00 1234567890
3 2.50 1234567890
标识符
identifier | user_id
AEFT67 12
JHDASJK 12
KJSIDJ6 3
LKSDLK 5
HSDJH8 17
IUSDI5 17
我想得到这样的结果:
结果:
user_id | identifier | amount | timestamp
12 AEFT67 10.00 1234567890
17 HSDJH8 5.00 1234567890
12 AEFT67 7.00 1234567890
3 KJSIDJ6 2.50 1234567890
这两个表都有数百万行,重要的是我在使用连接时不会重复(否则数量会有误)。
我最初尝试过:
SELECT t.user_id, t.amount, i.identifier
FROM transactions AS t
LEFT JOIN identifiers AS i ON i.id = (
SELECT
i2.id
FROM identifiers i2
WHERE i2.user_id = t.user_id
LIMIT 1
)
WHERE t.timestamp BETWEEN 1234567890 AND 1234567890
注意 - 我实际上并不介意为用户提供哪种标识,但用户可能有很多标识。嵌套的JOIN虽然在大型数据集上非常慢(大约40秒),所以我尝试了:
SELECT t1.user_id, t1.amount, i1.identifier FROM
(SELECT *
FROM transactions as t
WHERE t.timestamp BETWEEN 1234567890 AND 1234567890) as t1
LEFT JOIN
(SELECT * FROM identifiers GROUP BY user_id) i1
ON i1.user_id =t1.user_id
这实际上把时间减少了一半,但仍然很慢。
我觉得我错过了一些明显的东西,在每种情况下我都在搜索标识符表中的大量数据,这会减慢它的速度(数百万行而不是1000行左右)需要)。我觉得如果我能够将第一个查询的结果作为参数在第二个中使用它会更快,即:。
SELECT * FROM
(SELECT *
FROM transactions
WHERE t.timestamp BETWEEN 1234567890 AND 1234567890) as t1
LEFT JOIN
(SELECT * FROM identifiers WHERE user_id in (t1.user_id))
有没有更好的方法来通过引用单个(任何)标识符来获取过滤后的交易?
编辑:这是一个sql小提琴设置:http://sqlfiddle.com/#!9/ecad23/6
EDIT2:为了进一步说明,我需要保留每个事务的记录,因此如果where查询仅应用于事务,则返回的行数应该与您期望的完全相同。表。用户可以拥有多个事务,因此对最终结果进行分组将无法正常工作
答案 0 :(得分:1)
执行所需操作的简单查询是:
SELECT
t.user_id
, amount
, timestamp
, identifier
FROM
transactions AS t
LEFT JOIN identifiers AS i
ON i.user_id = t.user_id
WHERE
t.timestamp BETWEEN 1234567890 AND 1234567890
GROUP BY
t.user_id
, amount
, timestamp
由于查询应该相当容易由DBMS执行和优化,我猜你在某些列上缺少索引,如果它不快。
它的核心是两个表的简单连接。如果确保结果没有任何变化,那么人们可能会在LEFT JOIN
之间交换JOIN
,数据的一致性是完整的,这意味着每个交易都有一个用户。
GROUP BY再次删除联接生成的重复项。 identifier
上没有聚合函数,因此MySql只会选择一个。如果ONLY_FULL_GROUP_BY
标志处于活动状态,这可能会中断,这需要我们在聚合函数中使用identifier
。由于identifier
是varchar
,因此不能简单地使用MIN
或MAX
。但如果没有设置标志,则没有问题。
<强>校正强> 实际上我试过,似乎也可以使用例如varchar上的MAX。我不知道。
答案 1 :(得分:1)
这个可能更快:
SELECT user_id,
amount,
timestamp,
(
SELECT identifier FROM identifiers
WHERE user_id = t.user_id LIMIT 1
) AS identifier
FROM transactions AS t
WHERE timestamp BETWEEN 1234567890 AND 1234567890
所需索引:
transactions: INDEX(timestamp)
identifiers: INDEX(user_id)
一点额外的提升将涉及使用&#34;覆盖&#34;索引代替:
transactions: INDEX(timestamp, user_id, amount)
identifiers: INDEX(user_id, identifier)
检查您的BETWEEN
- 您可能会在结尾处加入额外的秒数。