需要有关涉及多个表的SQL查询的帮助 - 不能选择加入

时间:2009-02-03 23:10:24

标签: mysql

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

这是我正在尝试运行的查询。它不能很好地扩展,我想知道这里是否有人可以告诉我在哪里可以改进。我不加入r和我,因为我需要显示来自i的行,这些行在w中没有代表。我尝试了左连接,但它的表现并不太好。这更好,但还不理想。所有三个表都非常大。所有这三个都在我加入并选择的字段上编入索引。

任何评论,指示或建设性批评都将不胜感激。

编辑增加:

我应该把这个放在原来的问题中。这是从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

<小时/> 编辑le dorfier - 更多评论......

  

我应该提到w的关键是(member_id,id)。所以每个id都可以在w中存在多次,我只想知道它是否存在。

5 个答案:

答案 0 :(得分:3)

WHERE x IN ()INNER JOINSELECT DISTINCT子查询相同,一般情况下,如果优化程序不转{{1},则子查询的连接通常会更好进入IN - 它应该:

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

,那么可能等同于此
DISTINCT

请发布您的架构。

如果您使用wish作为存在标志,请尝试:

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

您可以使用与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 LEFT JOIN子查询相同的技术。我假设您没有指定SELECT DISTINCT,因为您想知道是否有会员这样做?在这种情况下,请务必使用w.member_id。您应该将SELECT DISTINCT的索引作为id上的第一列,以便执行此操作:

w

答案 1 :(得分:1)

请发布EXPLAIN列表。并解释表格和列的含义。

愿望似乎是一个布尔值 - 而你正在按它排序?


编辑:好吧,看起来它正在按照它的指示去做。凯德似乎在思考这一切可能意味着什么(他可能只是为了努力而投票。)但我真的宁愿你告诉我们。

狂野的猜测让每个人都感到困惑(包括你,我敢肯定。)


好的,根据新信息,这是我(稍微不那么狂野)的猜测。

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

你想为w中的每场比赛换一排吗?或者只是知道i.id,是否有活跃的记录?我假设第二个答案,所以你不需要ORDER BY - 它只适用于一个ID。而且由于你只是从i返回列,如果r中有多行,你只会得到重复的行。

如何发布您希望获得正确答案的内容?

答案 2 :(得分:1)

我应该把这个放在原来的问题中。这是从SQLYog返回的EXPLAIN ID | SELECT_TYPE |表|类型| possible_keys |关键| key_len |裁判|行|额外|
1 | PRIMARY | r | ref | member_id,id | member_id | 3 | const | 3120 |使用where;使用临时;使用filesort
1 | PRIMARY | I | eq_ref | ID | ID | 8 | r.id | 1 |
2 | DEPENDENT SUBQUERY | w | index_subquery | id,status | id | 8 | func | 8 |使用where

答案 3 :(得分:1)

...
ORDER BY wish DESC 
LIMIT 0,50

这似乎是一项巨大的开支。您按计算列“wish”排序,该列无法从索引中受益。这会强制它使用 filesort (由EXPLAIN指示)输出,这意味着它将整个结果集写入磁盘并使用非常慢的磁盘I / O对其进行排序。

当您发布此类问题时,您不应期望人们猜测您是如何定义表和索引的。获得完整定义非常简单:

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

然后将输出粘贴到您的问题中。

目前尚不清楚“wish”列的目的是什么。 “IN”谓词是一个布尔表达式,所以它总是导致0或1.但我猜你正在尝试使用“IN”以期在没有做到的情况下完成连接加入。如果你描述你想要完成的事情会有所帮助。

试试这个:

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;

它使用了一个额外的外部联接,但根据我的EXPLAIN测试, 不会招致文件分发。

答案 4 :(得分:0)

你试过这个吗?

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