【问题标题】:Which points makes sql queries faster哪些点让sql查询更快
【发布时间】:2018-06-16 15:01:58
【问题描述】:

我对 SQL 代码非常陌生,我只选择了几列并且主要使用 GROUP BY 函数,但我的代码需要 2 分钟才能显示结果,也许它不是一个长查询,但我需要更快。如何更快地进行 SQL 查询?

对于我的代码,我有一个包含联赛比赛的表格;

例如:

CustomerID        MatchDate         League              Matches                HomeTeam                AwayTeam
------------------------------------------------------------------------------------------------------------------------
1                 11-12-2006        La Liga             Barcelone-R.Madrid     Barcelona               RealMadrid
2                 10-10-2006        Premier League      Everton-Arsenal        Everton                 Arsenal
3                 09-10-2006        Premier League      Arsenal-Tottenham      Barcelona               RealMadrid
4                 10-10-2006        Bundesliga          Bayern-Mainz           Bayern                  MainZ 

我的目标是,计算主队和客队的每场比赛的总 ID,以便按联赛、球队和赛季查找每支球队和小组的总观看次数。 同一支球队可以是 HomeTeam 和 AwayTeam,所以我使用了这段代码。

SELECT League, SUM(totalnum), Teams, Season FROM 
    (
        (SELECT DATE_FORMAT(MatchDate, '%Y') as 'Season', HomeTeam as Teams, League, count(distinct CustomerID) as "totalnum" 
            FROM MY_TABLE GROUP BY League, Teams, Season ) 
        UNION ALL 
        (SELECT DATE_FORMAT(MatchDate, '%Y') as 'Season', AwayTeam as Teams, League, count(distinct CustomerID) as "totalnum" 
            FROM MY_TABLE GROUP BY League, Teams, Season )
    ) aa
GROUP BY League, Teams, Season
ORDER BY totalnum DESC

我可以得到结果,但我需要更短。哪些点会影响我的查询。

【问题讨论】:

  • 表格有多少行?
  • 确保您的查询有索引。
  • 几乎一百万行
  • 索引将没有用,OP 需要所有行 - 没有“where 子句”
  • @GerardH.Pille 你的 30 年,我的 15 年,都无所谓。我已经看到经验证据表明,使用FULL INDEX SCAN 比使用FULL TABLE SCAN 可以更便宜地解决一组案例。关于索引维护成本的说明,这是比一个查询更广泛的讨论:数据写入的频率以及增加的成本是多少,执行查询的频率以及节省的成本是多少,索引是否有益于其他查询,触发器/派生表/等会是更好的解决方案吗? ALL 索引会产生写入成本,听起来你提出了一个稻草人的论点。

标签: mysql sql query-performance


【解决方案1】:

如果您应用这两个索引,我希望您现有的查询应该加快...

CREATE INDEX MY_TABLE_league_home_date_cust
    ON MY_TABLE(
        League, HomeTeam, MatchDate, CustomerID
    );

CREATE INDEX MY_TABLE_league_away_date_cust
    ON MY_TABLE(
        League, AwayTeam, MatchDate, CustomerID
    );


也就是说,我怀疑您的查询中涉及的最高成本是COUNT(DISTINCT CustomerID)。这样做涉及必须对所有数据进行排序。这可能意味着以下索引可能会更好......

CREATE INDEX MY_TABLE_cust_league_home_date
    ON MY_TABLE(
        CustomerID, League, HomeTeam, MatchDate
    );

CREATE INDEX MY_TABLE_cust_league_away_date
    ON MY_TABLE(
        CustomerID, League, AwayTeam, MatchDate
    );


不过,另一个观察结果是,在您的查询中,您 COUNT() 是唯一的“家庭客户”,然后是 COUNT() 是唯一的“离开客户”,然后是 SUM() 他们。这意味着任何参加过主场比赛和客场比赛的人都算两次。这是故意的吗?

如果不是这样,您可能会发现查询的成本更高...

SELECT
  Team,
  League,
  DATE_FORMAT(MatchDate, '%Y')   AS Season,
  COUNT(DISTINCT CustomerID)     AS total
FROM 
(
  SELECT CustomerID, League, HomeTeam AS Team, MatchDate FROM MyTable
  UNION ALL
  SELECT CustomerID, League, AwayTeam AS Team, MatchDate FROM MyTable
)
  combined_view
GROUP BY
  Team, League, Season
ORDER BY
  total DESC


我认为您总体上最好的选择是为 Season 添加一个计算列,然后使用第一个索引的稍微修改的版本...

ALTER TABLE
  MY_TABLE
ADD Season VARCHAR(4) AS (
  DATE_FORMAT(MatchDate, '%Y')
);

CREATE INDEX MY_TABLE_league_home_season_cust
    ON MY_TABLE(
        League, HomeTeam, Season, CustomerID
    );

CREATE INDEX MY_TABLE_league_away_season_cust
    ON MY_TABLE(
        League, AwayTeam, Season, CustomerID
    );

SELECT
  Team,
  League,
  Season,
  COUNT(DISTINCT CustomerID)     AS total
FROM 
(
  SELECT CustomerID, League, HomeTeam AS Team, Season FROM MyTable
  UNION ALL
  SELECT CustomerID, League, AwayTeam AS Team, Season FROM MyTable
)
  combined_view
GROUP BY
  Team, League, Season
ORDER BY
  total DESC

【讨论】:

  • 嘿,@MatBailie 谢谢你的尝试。不幸的是,在创建索引并使用您的代码之后,运行时间比运行时间要长一点。 Ps,我无法在表格中添加或删除某些内容。
  • 那么您需要考虑替代途径而不是优化。我添加的准确查询是否必需,或者您估计的SUM(COUNT(DISTINCT)+COUNT(DISTINCT))是否足够?如果您的足够,则更简单的估计是否合理;只需删除DISTINCT?更重要的是准确性还是执行时间?您需要清楚地探索您的需求和问题陈述,建立在容易的概念上的需求并不少见,但在意识到它很难/缓慢/昂贵时,提出了一个更简单的需求。
  • 没错。首先,当我问这个问题时,我认为我对时间的查询很愚蠢,但看起来我应该选择时间/准确度!
  • @Axis - 任一索引对是否提高了查询的执行时间(来自您的原始帖子)
  • @Axis - 另外,请您包括包含现有索引等的表定义吗?还有,怎么能加索引,不能加列?
【解决方案2】:

你可以试试这个吗?

SELECT DATE_FORMAT(A.MatchDate, '%Y') as 'Season',  
       case c.col
         when 'home' then A.HomeTeam
         when 'away' then A.AwayTeam
       end as Teams,
       A.League, count(distinct A.CustomerID) as "totalnum" 
        FROM MY_TABLE A
        cross join ( select 'home' as col union all select 'away') c
     GROUP BY League, Teams, Season
ORDER BY totalnum DESC

在 SQL Fiddle 中查看结果:新:http://sqlfiddle.com/#!9/dd0335/11(上一个:http://sqlfiddle.com/#!9/dd0335/9

【讨论】:

  • 几乎相同的执行时间
  • 当然。我有一百万行表格,包括球队、比赛、联赛等。我需要唯一的客户(这很重要),他们观看每个团队但行商店匹配所以意味着两个团队,我的想法是每两个团队添加每个客户,以便找到手表的数量。最后的想法是找到每个联赛和每个团队的总观看次数。(我将把这个结果用于另一个数据平台,所以我不能在两个不同的列中使用,很遗憾)
  • 对不起,我的意思是 MySQL 的执行计划 (dev.mysql.com/doc/refman/5.5/en/execution-plan-information.html)。我已经简化了我的答案。
  • MySQL 不会做任何我们没有要求他做的事情。确定计数(不同):如果同一客户一年内多次观看同一支球队的比赛,您只想将其计为一次?
  • 然后,也许规范化您的架构可能会给您带来性能提升:两张表,一张包含匹配项,一张包含访问(仅包含客户 ID 和匹配 ID)。需要读取的数据会少很多。
猜你喜欢
  • 2015-06-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-02-24
  • 1970-01-01
相关资源
最近更新 更多