2011-09-07 70 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