2011-09-07 72 views
1

我正在为我学校的午餐订单程序撰写报告,我做了一个查询,该查询给了我需要的结果,但速度非常慢。我正在考虑使用相关的子查询来加速(来自另一个类似的线程的建议),但无法通过语法错误,甚至不知道它是否会完成我想要的。MYSQL加速查询

涉及的两个表是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 

,我想他们都显示为报告的目的,从而从查询的输出会看起来像一个单个记录:披萨Jane Doe的辣。 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)))) 
+3

如果您对数据库有任何控制权,请考虑规范化此设计。学生的名字和姓氏没有业务是订购商品的“选项”。 –

回答

1

我同意,这将是与相应的数据库结构容易很多,但会回答假设你不能改变它。我的答案类似于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; 
+0

有什么区别,感谢我在本例中应该更具体的工作,我确实需要列中的结果。我也无法改变表格结构。由于mysql的限制(以及我自己的经验不足),我最终不得不从代码创建2个视图。但一切运作良好,速度要快得多。 – joeedel

2

几点意见:

  • 规范化表,没有章法的金额将加快这一非标准化的烂摊子到可以接受的地步。
  • 退出`反衬习惯,这是一个眼睛。您只需使用-gasp-空格反向保留字或列名。
  • 你并不需要使用()括号将其扩展,只要只使用AND和不支持子,你不需要任何。
  • 缩进好,缩进使用Tab是坏,计算器上粘贴代码时尤其如此。

至于你的表。

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

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应该工作的方式,而不是你在做它的方式。

BTW括号内是可选的,我只是想“M的可读性。

3

首先,如果你有,你应该真的修改数据库的结构,你现在有什么太大的意义可能性其他人说。

但是我一直在“不得不从现有的代码做报告”的情况,所以我知道你可能根本无法做到这一点;如果这确实是你的话,那么下面的查询应该可以帮助您显著提高速度:

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); 
+0

不要忘记添加索引,这应该可以帮助加快速度,BTW +1 – Johan