简体   繁体   English

需要涉及多个表的SQL查询的帮助-不能选择联接

[英]Need help with an SQL query involving multiple tables - Join not an option

SELECT i.*, i.id IN (
  SELECT id
  FROM w 
  WHERE w.status='active') AS wish 
FROM i
INNER JOIN r ON i.id=r.id
WHERE r.member_id=1 && r.status='active' 
ORDER BY wish DESC 
LIMIT 0,50

That's a query that I'm trying to run. 这是我要运行的查询。 It doesn't scale well, and I'm wondering if someone here can tell me where I could improve things. 它的伸缩性不好,我想知道这里有人可以告诉我我可以改进的地方。 I don't join w to r and i because I need to show rows from i that are unrepresented in w. 我不将w加入r和i中,因为我需要显示i中未在w中代表的行。 I tried a left join, but it didn't perform too well. 我尝试了左联接,但效果不佳。 This is better, but not ideal yet. 这更好,但还不理想。 All three tables are very large. 三个表都很大。 All three are indexed on the fields I'm joining and selecting on. 这三个字段都在我要加入并选择的字段上建立了索引。

Any comments, pointers, or constructive criticisms would be greatly appreciated. 任何意见,指示或建设性的批评将不胜感激。

EDIT Addition: 编辑加法:

I should have put this in my original question. 我应该把这个放在我最初的问题中。 It's the EXPLAIN as return from SQLYog. 这是从SQLYog返回的EXPLAIN。

id|select_type       |table|type          |possible_keys|key      |key_len|ref  |rows|Extra|  
1 |PRIMARY           |r    |ref           |member_id,id |member_id|3      |const|3120|Using where; Using temporary; Using filesort  
1 |PRIMARY           |i    |eq_ref        |id           |id       |8      |r.id |1   |  
2 |DEPENDENT SUBQUERY|w    |index_subquery|id,status    |id       |8      |func |8   |Using where


EDIT le dorfier - more comments ... EDIT le dorfier-更多评论...

I should mention that the key for w is (member_id, id). 我应该提到w的键是(member_id,id)。 So each id can exist multiple times in w, and I only want to know if it exists. 所以每个id在w中可以存在多次,我只想知道它是否存在。

WHERE x IN () is identical to an INNER JOIN to a SELECT DISTINCT subquery, and in general, a join to a subquery will typically perform better if the optimizer doesn't turn the IN into a JOIN - which it should: WHERE x IN ()SELECT DISTINCT子查询的INNER JOIN相同,并且通常,如果优化程序不将IN转换为JOIN ,则SELECT DISTINCT查询的INNER JOIN通常会更好地执行-它应该:

SELECT i.*
FROM i
INNER JOIN (
    SELECT DISTINCT id
    FROM w 
    WHERE w.status = 'active'
) AS wish 
    ON i.id = wish.id
INNER JOIN r
    ON i.id = r.id
WHERE r.member_id = 1 && r.status = 'active' 
ORDER BY wish.id DESC 
LIMIT 0,50

Which, would probably be equivalent to this if you don't need the DISTINCT : 如果您不需要DISTINCT ,则可能等效于此:

SELECT i.*
FROM i
INNER JOIN w 
    ON w.status = 'active'
    AND i.id = wish.id
INNER JOIN r
    ON i.id = r.id
    AND r.member_id = 1 && r.status = 'active' 
ORDER BY i.id DESC 
LIMIT 0,50

Please post your schema. 请发布您的架构。

If you are using wish as an existence flag, try: 如果您将愿望用作存在标记,请尝试:

SELECT i.*, CASE WHEN w.id IS NOT NULL THEN 1 ELSE 0 END AS wish
FROM i
INNER JOIN r
    ON i.id = r.id
    AND r.member_id = 1 && r.status = 'active' 
LEFT JOIN w 
    ON w.status = 'active'
    AND i.id = w.id
ORDER BY wish DESC 
LIMIT 0,50

You can use the same technique with a LEFT JOIN to a SELECT DISTINCT subquery. 您可以对LEFT JOIN使用相同的技术来SELECT DISTINCT子查询。 I assume you aren't specifying the w.member_id because you want to know if any members have this? 我假设您没有指定w.member_id因为您想知道是否有任何成员具有此名称? In this case, definitely use the SELECT DISTINCT . 在这种情况下,请绝对使用SELECT DISTINCT You should have an index with id as the first column on w as well in order for that to perform: 您还应该在w的第一列中有一个idid的索引,以使其执行:

SELECT i.*, CASE WHEN w.id IS NOT NULL THEN 1 ELSE 0 END AS wish
FROM i
INNER JOIN r
    ON i.id = r.id
    AND r.member_id = 1 && r.status = 'active' 
LEFT JOIN (
    SELECT DISTINCT w.id
    FROM w 
    WHERE w.status = 'active'
) AS w
    ON i.id = w.id
ORDER BY wish DESC 
LIMIT 0,50

Please post the EXPLAIN listing. 请发布EXPLAIN列表。 And explain what the tables and columns mean. 并解释表和列的含义。

wish appears to be a boolean - and you're ORDERing by it? 愿望似乎是一个布尔值-您正在按它订购吗?


EDIT: Well, it looks like it's doing what it's being instructed to do. 编辑:好吧,看起来它正在按照指令进行操作。 Cade seems to be thinking expansively on what this all could possibly mean (he probably deserves a vote just for effort.) But I'd really rather you tell us. Cade似乎在广泛思考这一切可能意味着什么(他可能应该为努力而投票。)但我真的很希望您告诉我们。

Wild guessing just confuses everyone (including you, I'm sure.) 疯狂的猜测只会使所有人(包括您在内)都感到困惑。


OK, based on new info, here's my (slightly less wild) guess. 好吧,根据新信息,这是我的猜测(略少一些)。

SELECT i.*,  
    CASE WHEN EXISTS (SELECT 1 FROM w WHERE id = i.id AND w.status = 'active' THEN 1 ELSE 0 END) AS wish  
FROM i  
INNER JOIN r ON i.id = r.id AND r.status = 'active'  
WHERE r.member_id = 1

Do you want a row for each match in w? 您是否要为w中的每个匹配项排一行? Or just to know for i.id , whether there is an active w record? 还是只知道i.id,是否有活动的w记录? I assumed the second answer, so you don't need to ORDER BY - it's for only one ID anyway. 我假设了第二个答案,因此您无需进行ORDER BY-无论如何,仅用于一个ID。 And since you're only returning columns from i, if there are multiple rows in r, you'll just get duplicate rows. 并且由于您仅从i返回列,因此如果r中有多行,您将只获得重复的行。

How about posting what you expect to get for a proper answer? 如何发布您期望获得正确答案的内容?

I should have put this in my original question. 我应该把这个放在我最初的问题中。 It's the EXPLAIN as return from SQLYog. 这是从SQLYog返回的EXPLAIN。
id|select_type|table|type|possible_keys|key|key_len|ref|rows|Extra| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
1|PRIMARY|r|ref|member_id,id|member_id|3|const|3120|Using where; 1 | PRIMARY | r | ref | member_id,id | member_id | 3 | const | 3120 |在哪里使用; Using temporary; 使用临时; Using filesort 使用文件排序
1|PRIMARY|i|eq_ref|id|id|8|r.id|1| 1 | PRIMARY | i | eq_ref | id | id | 8 | r.id | 1 |
2|DEPENDENT SUBQUERY|w|index_subquery|id,status|id|8|func|8|Using where 2 | DEPENDENT SUBQUERY | w | index_subquery | id,status | id | 8 | func | 8 |在哪里使用

...
ORDER BY wish DESC 
LIMIT 0,50

This appears to be the big expense. 这似乎是一大笔费用。 You're sorting by a computed column " wish " which cannot benefit from an index. 您正在按无法从索引中受益的计算列“ wish ”进行排序。 This forces it to use a filesort (as indicated by the EXPLAIN) output, which means it writes the whole result set to disk and sorts it using disk I/O which is very slow. 这迫使它使用文件排序 (如EXPLAIN所示)输出,这意味着它将整个结果集写入磁盘,并使用非常慢的磁盘I / O对其进行排序。

When you post questions like this, you should not expect people to guess how you have defined your tables and indexes. 当您发布这样的问题时,您不应期望人们猜测您如何定义表和索引。 It's very simple to get the full definitions: 完整的定义非常简单:

mysql> SHOW CREATE TABLE w;
mysql> SHOW CREATE TABLE i;
mysql> SHOW CREATE TABLE r;

Then paste the output into your question. 然后将输出粘贴到您的问题中。

It's not clear what your purpose is for the " wish " column. 尚不清楚您的“ wish ”列的用途是什么。 The " IN " predicate is a boolean expression, so it always results in 0 or 1. But I'm guessing you're trying to use " IN " in hopes of accomplishing a join without doing a join. IN ”谓词是一个布尔表达式,因此它始终导致0或1。但是我猜您正在尝试使用“ IN ”,以期不进行联接就完成联接。 It would help if you describe what you're trying to accomplish. 如果您描述要完成的工作,这将有所帮助。

Try this: 尝试这个:

SELECT i.*
FROM i
 INNER JOIN r ON i.id=r.id
 LEFT OUTER JOIN w ON i.id=w.id AND w.status='active'
WHERE r.member_id=1 AND r.status='active'
 AND w.id IS NULL
LIMIT 0,50;

It uses an additional outer join, but it doesn't incur a filesort according to my test with EXPLAIN. 它使用附加的外部联接,但是根据我对EXPLAIN的测试,它不会引起文件排序。

Have you tried this? 你有尝试过吗?

SELECT i.*, w.id as wish FROM i
LEFT OUTER JOIN w ON i.id = w.id
  AND w.status = 'active'
WHERE i.id in (SELECT id FROM r WHERE r.member_id = 1 AND r.status = 'active')
ORDER BY wish DESC
LIMIT 0,50

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM