繁体   English   中英

MYSQL加快查询

[英]MYSQL speed up query

我正在为学校的午餐订购程序编写报告,我进行了查询,可以得到所需的结果,但是非常慢。 我当时在考虑使用相关的子查询来加快速度(来自另一个类似线程的建议),但无法摆脱过去的语法错误,甚至不确定它是否可以完成我想要的。

涉及的两个表是Ordered_Items和Ordered_Options,“订购的物品”表通常在“订购的选项”表中具有3-4条相关记录,这些记录代表学生订购午餐时选择的选项。

典型的记录如下所示

Ordered_Items.Record_Number = 1
Ordered_Items.Name = Pizza

Ordered_Options.Ordered_Items_rn = 1
Ordered_Options.Value = Jane
Ordered_Options.rn = 43

Ordered_Options.Ordered_Items_rn = 1
Ordered_Options.Value = Doe
Ordered_Options.rn = 44

Ordered_Options.Ordered_Items_rn = 1
Ordered_Options.Value = Pepperoni
Ordered_Options.rn = 45

我希望它们全部显示为单个记录以用于报告目的,因此查询的输出应类似于:Pizza Jane Doe Pepperoni。 Ordered_Options.rn是一致的,意味着名字总是= 44,依此类推。 我把我的代码放在下面。

SELECT
  OI.Name AS ItemName,
  opt1.`Value` AS StudentFirst,
  opt2.`Value` AS StudentLast,
  opt3.`Value` AS Grade,
  opt4.`Value` AS Milk
FROM
  ((((
  Ordered_Items OI
  LEFT JOIN Ordered_Options opt1 ON ((
            (OI.record_number = opt1.Ordered_Items_rn)
         AND(opt1.Options_rn = 43))))
  LEFT JOIN Ordered_Options opt2 ON((
            (OI.record_number = opt2.Ordered_Items_rn)
         AND(opt2.Options_rn = 44))))
  LEFT JOIN Ordered_Options opt3 ON ((
            (OI.record_number = opt3.Ordered_Items_rn)
     AND(opt3.Options_rn = 46))))
  LEFT JOIN Ordered_Options opt4 ON ((
            (OI.record_number = opt4.Ordered_Items_rn)
         AND(opt4.Options_rn = 55))))

首先,正如其他人所说的那样,如果您有可能应该真正修改数据库的结构,那么现在所拥有的并没有多大意义。

但是我一直处于“必须从现有代码中生成报告”的情况,所以我知道您可能根本无法做到这一点; 如果确实是您的情况,那么以下查询将帮助您显着提高速度:

SELECT 
  `Record_Number`, 
  Concat(Name, ' ', Group_Concat(Value ORDER BY rn ASC SEPARATOR ' ')) AS Request
FROM 
  `Ordered_Items` oi 
  LEFT OUTER JOIN `Ordered_Options` oo ON (oo.Ordered_Items_rn = oi.Record_Number) 
GROUP BY 
  oi.Record_Number

应该给你这样的东西:

Record_Number   Request
1               Pizza Jane Doe Pepperoni
2               Fries Bobby Table French

由于您是新手,请记住单击左侧的箭头作为解决方案,这样可以解决您的问题。 如果不是,请在对此答案的评论中指出原因。

我用于它的示例数据库:

--
-- Table structure for table `Ordered_Items`
--    
CREATE TABLE IF NOT EXISTS `Ordered_Items` (
  `Record_Number` int(11) NOT NULL,
  `Name` text NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

--
-- Dumping data for table `Ordered_Items`
--

INSERT INTO `Ordered_Items` (`Record_Number`, `Name`) VALUES
(1, 'Pizza'),
(2, 'Fries');

-- --------------------------------------------------------

--
-- Table structure for table `Ordered_Options`
--

CREATE TABLE IF NOT EXISTS `Ordered_Options` (
  `Ordered_Items_rn` int(11) NOT NULL,
  `Value` text NOT NULL,
  `rn` int(11) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

--
-- Dumping data for table `Ordered_Options`
--

INSERT INTO `Ordered_Options` (`Ordered_Items_rn`, `Value`, `rn`) VALUES
(1, 'Jane', 43),
(1, 'Doe', 44),
(1, 'Pepperoni', 45),
(2, 'Bobby', 43),
(2, 'Table', 44),
(2, 'French', 45);

一些评论:

  • 标准化您的表,没有任何技巧可以将这种未标准化的混乱加速到可接受的比例。
  • 摆脱`倒钩的习惯,这是一种令人讨厌的事情。 您只需要使用-gasp-空格反引号保留字或列名。
  • 您不需要使用()括号来进行这种扩展,只要只使用AND并且没有子选择就不需要任何括号。
  • 缩进是好的,使用tab缩进是不好的,尤其是在将代码粘贴到stackoverflow上时。

关于您的桌子。

扔掉您现在拥有的那个并创建一个新表。

table ordered_item
-----------
id integer auto_increment primary key  
item_id integer not null foreign key references item(id) on delete restrict on update cascade
customer_id integer not null foreign key references customer(id) on delete restrict on update cascade
qty integer not null
.....

table customer
--------------
id integer auto_increment primary key    
firstname varchar(200)
.....

table item
----------
id integer auto_increment primary key  
name varchar(200)
price decimal(10,2)  //should really be in a separate table

以下查询将为客户1124提供每个订单订购的所有项目以及总计。

SELECT customer.name, io.id as order_id, sum(io.qty) as number_of_items, i.* 
FROM item i
INNER JOIN ordered_item oi ON (oi.item_id = i.id)
INNER JOIN customer c ON (c.id = oi.customer_id)
WHERE customer.id = '1124'
GROUP BY io.id WITH ROLLUP

这是SQL应该起作用的方式,而不是您这样做的方式。

顺便说一句,括号是可选的,我只是喜欢'm以提高可读性。

我同意,使用适当的数据库结构,这将容易得多,但是会回答假设您无法更改它。 我的答案与Lepidosteus相似,但给您的结果带有单独列中的值,而不是串联在一起的。 它从使用if运算符的子查询开始。

select oi.Record_Number as Record_Number, oi.Name as Name,
    if (oo.rn = 43, oo.Value, null) as firstName,
    if (oo.rn = 44, oo.Value, null) as lastName,
    if (oo.rn = 45, oo.Value, null) as topping
from Ordered_Items as oi join Ordered_Options as oo 
    on oi.Record_Number = oo.Ordered_Items_rn;

这样就为表提供了一个表,其中有多个行用于Ordered_Items中的每个记录,每个记录对于Ordered_Options中的数据列之一具有正确的值,而在Ordered_Options中的其他数据列中具有空值。

|Record_Number |Name   |firstName |lastName |topping   |
--------------------------------------------------------
|1             |Pizza  |Jane      |null     |null      |
|1             |Pizza  |null      |Doe      |null      |
|1             |Pizza  |null      |null     |Pepperoni |

现在,将此子查询包装在将单个Ordered_Item行的所有记录分组在一起的查询中,并使用group_concat运算符从Ordered_Options中收集每个数据列的值(忽略空值)。

select c.Record_Number, c.Name,
    group_concat(c.firstName) as firstName,
    group_concat(c.lastName)  as lastName,
    group_concat(c.topping)   as topping
from
(select oi.Record_Number as Record_Number, oi.Name as Name,
    if (oo.rn = 43, oo.Value, null) as firstName,
    if (oo.rn = 44, oo.Value, null) as lastName,
    if (oo.rn = 45, oo.Value, null) as topping
 from Ordered_Items as oi join Ordered_Options as oo 
    on oi.Record_Number = oo.Ordered_Items_rn) as c
group by c.Record_Number, c.Name;

暂无
暂无

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

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