【问题标题】:SQL: How to speed up a query using indexingSQL:如何使用索引加速查询
【发布时间】:2014-02-13 12:58:30
【问题描述】:

我正在尝试加快查询速度,以查找所有购买了 1970 年之前制造的 MOTORCYCLE 并购买了 2010 年之后制造的另一辆 MOTORCYCLE 的客户。由于我的查询运行非常缓慢,我认为我需要帮助以找到更好的索引.我的尝试记录如下:

表格

CREATE TABLE CUSTOMER (
  id int PRIMARY KEY, 
  fname varchar(30),     
  lname varchar(30)
);

CREATE TABLE MOTORCYCLE (
  id int PRIMARY KEY, 
  name varchar(30), 
  year int -- Manufactured year
);

CREATE TABLE SALES (
  cid int,
  mid int,
  FOREIGN KEY(cid) REFERENCES CUSTOMER(id), 
  FOREIGN KEY(mid) REFERENCES MOTOCYCLE(id),
  PRIMARY KEY(pid, mid, role)
);

索引

这是我的索引(我有些猜测,但这是我的尝试):

CREATE UNIQUE INDEX customerID on CUSTOMER(id);
CREATE INDEX customerName on CUSTOMER(fname, lname);

CREATE UNIQUE INDEX motorcycleID on MOTORCYCLE(id);
CREATE INDEX motorcycleName on MOTORCYCLE(name);
CREATE INDEX motorcycleYear on MOTORCYCLE(year);

CREATE INDEX salesCustomerMotorcycleID on SALES(cid, mid);       
CREATE INDEX salesCustomerID on SALES(cid);
CREATE INDEX castsMotorcycleID on SALES(mid);

查询

我要查找购买 1970 年之前和 2010 年之后制造的自行车的客户的查询如下:

SELECT fname, lname
FROM (SALES INNER JOIN CUSTOMER ON SALES.cid=CUSTOMER.id) INNER JOIN MOTORCYCLE ON MOTORCYCLE.id=SALES.mid
GROUP BY CUSTOMER.id
HAVING MIN(MOTORCYCLE.year) < 1970 AND MAX(MOTORCYCLE.year) > 2010;

这是另一个有效的查询,它避免了 GROUP BYHAVING 子句:

SELECT DISTINCT C.id, fname, lname
FROM (CUSTOMER as C inner join (SALES as S1 INNER JOIN MOTORCYCLE as M1 ON M1.id=S1.mid) on C.id=S1.cid) inner join (SALES as S2 inner join MOTORCYCLE as M2 on S2.mid=M2.id) on C.id=S2.cid
WHERE (M1.year < 1970 AND M2.year > 2010);

关于我可以用来加快查询速度的索引种类有什么建议吗?还是我应该更改我的查询?

更新

我发现另一个查询也有效,但它也太慢了。它已在上面添加。不过,在查找索引以加快速度时,它可能会有所帮助。

【问题讨论】:

  • 我认为使用JOIN 进行查询是更好的方式
  • 我同意 - 这将是一个更简洁的语法,对吧? JOIN 也可以加快查询速度吗? (我相信它会是INNER JOIN ... ON)我刚刚将它添加到 OP 中。
  • 为什么在 MOTOCYCLE(年)上使用 3 个索引?
  • 你是对的 - 它不会加快查询速度。我只是在尝试一些东西,但我现在把它删除了。如果您有任何加快速度的建议,请告诉我。
  • SALES没有列pid,最后一个查询也没有表C2

标签: sql sqlite indexing query-optimization


【解决方案1】:

当您使用 EXPLAIN QUERY PLAN 查看查询时,您会发现在这两种情况下,数据库都会先查找许多相关记录,然后再过滤掉不需要的记录(包括不需要的年份)。

以下查询在匹配之前查找摩托车 ID;哪个更快取决于您的数据的详细信息,并且必须由您衡量:

SELECT *
FROM Customer
WHERE EXISTS (SELECT 1
              FROM Sales
              WHERE cid = Customer.id
                AND mid IN (SELECT id
                            FROM Motorcycle
                            WHERE year < 1970))
  AND EXISTS (SELECT 1
              FROM Sales
              WHERE cid = Customer.id
                AND mid IN (SELECT id
                            FROM Motorcycle
                            WHERE year > 2010));

SELECT *
FROM Customer
WHERE EXISTS (SELECT 1
              FROM Sales AS s1
              JOIN Sales AS s2 ON s1.cid = s2.cid
              WHERE s1.cid = Customer.id
                AND s1.mid IN (SELECT id
                               FROM Motorcycle
                               WHERE year < 1970)
                AND s2.mid IN (SELECT id
                               FROM Motorcycle
                               WHERE year > 2010));

SQL Fiddle

【讨论】:

    【解决方案2】:

    当查询中没有使用聚合函数时,为什么要使用 group by? 如果您不想看到任何重复,请改用 distinct

    【讨论】:

    • 我不确定您的意思 - 我认为我需要汇总才能知道客户是否购买了超出制造年份指定范围的自行车。因此,我使用GROUP BY 来获取客户购买的自行车的 MAX 和 MIN 年。有关如何更改/改进查询的任何建议?
    • SELECT fname, lname, DISTINCT(CUSTOMER.id) FROM (SALES INNER JOIN CUSTOMER ON SALES.cid=CUSTOMER.id) INNER JOIN MOTORCYCLE ON MOTORCYCLE.id=SALES.mid WHERE MOTORCYCLE.year 2010;这应该为您提供所有购买 1970 年之前或 2010 年之后制造的摩托车的客户
    • 不,这不起作用 - 我需要购买 1970 年之前制造的摩托车并购买 2010 年之后制造的摩托车的所有客户。我正在寻找购买过非常旧和非常新的自行车的人。
    • 试试 MOTORCYCLE.year 2010
    • 那行不通 - 加入 MOTORCYCLE 后,两个 MOTORCYCLE.year 实例都指的是同一辆摩托车,所以MOTORCYCLE.year &lt; 1970 AND MOTORCYCLE.year &gt; 2010 永远不会是真的。
    猜你喜欢
    • 2015-08-31
    • 1970-01-01
    • 2011-04-24
    • 2015-12-22
    • 1970-01-01
    • 1970-01-01
    • 2013-06-25
    • 2018-11-30
    • 1970-01-01
    相关资源
    最近更新 更多