MySQL并使子查询更有效率

时间:2017-06-21 23:36:56

标签: mysql sql

我使用MySQL并且我有一个查询。还有一个子查询。

SELECT * FROM rg, list, status
WHERE (
  (rg.required_status_id IS NULL AND rg.incorrect_status_id IS NULL) || 
  (status.season_id = rg.required_status_id AND status.user_id = list.user_id) || 

  (rg.incorrect_status_id IS NOT NULL AND 
    list.user_id NOT IN (SELECT user_id FROM status WHERE user_id = list.user_id AND season_id = rg.incorrect_status_id)
  )
)

问题是代码的以下部分:

(rg.incorrect_status_id IS NOT NULL AND 
  list.user_id NOT IN (SELECT user_id FROM status WHERE user_id = list.user_id AND season_id = rg.incorrect_status_id)
)

我如何检查表格"状态"有一行user_idlist.user_id相同而season_idrg.incorrect_status_id相同?

更新

这是我当前的代码,但它根本不起作用。我不知道该怎么做。

SELECT * FROM rg, list, status
  LEFT JOIN status AS stat
    INNER JOIN rg AS rglist
    ON rglist.incorrect_status_id = stat.season_id
  ON stat.season_id = rglist.incorrect_status_id

  WHERE (
    (rg.required_status_id IS NULL AND rg.incorrect_status_id IS NULL) || 
    (status.season_id = rg.required_status_id AND status.user_id = list.user_id) || 

    (rg.incorrect_status_id IS NOT NULL AND stat.user_id IS NULL)
  )
)

更新2

我修改了名称,但基本思路是一样的。

FROM sarjojen_rglistat, sarjojen_rglistojen_osakilpailut, kilpailukausien_kilpailut, sarjojen_osakilpailuiden_rgpisteet
, sarjojen_kilpailukaudet, sarjojen_kilpailukausien_kilpailusysteemit
/* , kayttajien_ilmoittautumiset */
/* , sarjojen_kilpailukausien_pelaajastatukset */

LEFT OUTER JOIN sarjojen_kilpailukausien_pelaajastatukset
ON sarjojen_kilpailukausien_pelaajastatukset.sarjan_kilpailukausi_id = sarjojen_rglistat.vaadittu_pelaajastatus_id

LEFT OUTER JOIN kayttajien_ilmoittautumiset
ON kayttajien_ilmoittautumiset.kayttaja_id = sarjojen_kilpailukausien_pelaajastatukset.kayttaja_id

现在说:

未找到列:1054未知列' sarjojen_rglistat.vaadittu_pelaajastatus_id'在' on条款'

为什么会这样?

我有一张名为" sarjojen_rglistat"还有一个专栏" vaadittu_pelaajastatus_id"。

2 个答案:

答案 0 :(得分:1)

1)查询引擎更容易查询和生成有效的计划。

如果你仔细关注查询的以下部分,你可能会发现一些有点“怪异”的事情。要走了。这是一个线索,这种方法可能有点过于复杂。

...(
list.user_id NOT IN (
    SELECT  user_id
    FROM    status
            /* Note the sub-query cannot ever return a user_id different
               to the one checked with "NOT IN" above */
    WHERE   user_id = list.user_id
        AND season_id = rg.incorrect_status_id)
)

查询过滤list.user_id不在结果集中,该结果集不能包含除list.user_id以外的user_id'当然,子查询可以返回零结果。所以基本上归结为简单的存在检查。

首先,你应该写一下:

...(
NOT EXISTS (
    SELECT  *
    FROM    status
    WHERE   user_id = list.user_id
        AND season_id = rg.incorrect_status_id)
)

2)明确你的"什么加在一起的桌子" (这也指回1)。

您的查询从3个表中选择而未指定任何连接条件:

FROM rg, list, status

这将导致交叉连接生成结果集,该结果集是所有可能的行匹配的排列组合。如果您的WHERE子句很简单,查询引擎可能会隐式地将某些过滤条件提升为连接条件,但事实并非如此。因此,即使例如每个表中的行数非常少:

status   20
rg       100
list     1000

Your intermediate result set (before WHERE is applied),
would need 1000 * 100 * 20 = 2000000 rows!

通过连接条件清楚地说明每个表的行是如何匹配的,这有很大帮助。它不仅使查询更易于阅读和理解,而且还有助于避免忽略可能成为性能考虑因素的连接条件。

请注意,在指定连接条件时,某些行可能没有匹配项,这是了解和理解不同类型的连接非常重要的地方。特别是在您的情况下,WHERE子句中的大多数复杂性似乎来自于行/不匹配时尝试解决。有关一些有用的信息,请参阅this answer

您的FROM / WHERE子句可能看起来更像以下内容。 (很难确定,因为您还没有说明您的表格关系或查询的预期输入/输出。但它应该让您走上正确的轨道。)

FROM    rg
        /* Assumes rg rows form the base of the query, and not to have
           some rg rows excluded due to non-matches in list or status. */
        LEFT OUTER JOIN status ON
            status.season_id = rg.required_status_id
        LEFT OUTER JOIN list ON
            status.user_id = list.user_id
WHERE   rg.incorrect_status_id IS NULL
    /* As Barmar commented, it may also be useful to break this
       OR condition out as a separate query UNION to the above.  */
    OR  (
            rg.incorrect_status_id IS NOT NULL
        AND NOT EXISTS (
            SELECT  *
            FROM    status
            WHERE   user_id = list.user_id
                AND season_id = rg.incorrect_status_id)
        )

请注意,此查询非常清楚表格的连接方式与用于过滤连接结果集的内容之间的区别。

3)最后也非常重要的是,如果没有正确的索引,即使是最好的查询也没有什么好处!

使用错误索引(或相反,具有良好索引的错误查询)的良好查询无论如何都将是低效的。计算机速度足够快,您可能不会注意到小型数据库,但您确实尝试使用候选索引来查找数据和工作负载的最佳组合。

在上面的查询中,您可能需要以下索引。 (有些可能已经被主键约束所覆盖。)

status.season_id
status.user_id
list.user_id
rg.required_status_id
rg.incorrect_status_id

答案 1 :(得分:1)

使用UNION个子查询来处理与OR合并的3个案例。然后,您可以在每个子查询中使用显式JOIN来明确表格彼此之间的关系(或者在您执行完整的交叉产品时根本不相关,就像{{1 }})。

rg.required_status_id IS NULL AND rg.incorrect_status_id IS NULL