MYSQL ускорить запрос - PullRequest
       4

MYSQL ускорить запрос

1 голос
/ 08 сентября 2011

Я пишу отчеты для школьной программы заказа обедов, я сделал запрос, который дает мне нужные мне результаты, но очень и очень медленный. Я думал об использовании коррелированных подзапросов для ускорения (предложение из другого подобного потока), но не могу преодолеть синтаксические ошибки и даже не уверен, что это выполнит то, что я хочу.

Обе таблицы: 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))))

Ответы [ 3 ]

3 голосов
/ 08 сентября 2011

Во-первых, как говорили другие, если у вас есть возможность, вы должны действительно пересмотреть структуру вашей базы данных, то, что у вас есть сейчас, не имеет особого смысла.

Однако я 'Я был в ситуации «создания отчетов из существующего кода», поэтому я знаю, что вы, возможно, вообще не сможете этого сделать;если это действительно ваш случай, то следующий запрос должен помочь вам значительно улучшить скорость:

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);
2 голосов
/ 08 сентября 2011

Несколько комментариев:

  • Нормализуйте свою таблицу, никакие уловки не приведут этот ненормализованный беспорядок в приемлемые пропорции.
  • Выйти из привычки ` backtick, это бельмо на глазу.Вам нужно только пометить зарезервированные слова или имена столбцов с -gasp- пробелами.
  • Вам не нужно использовать () круглых скобок для этого расширения, если только используется AND и никаких подвыборов вам не нужно.
  • Отступы хорошие, отступы с использованием табуляции плохие, особенно при вставке кода в 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, а не вы.делаем это.

Кстати, скобки не являются обязательными, мне просто нравится читаемость.

1 голос
/ 08 сентября 2011

Я согласен, что это будет намного проще с соответствующей структурой базы данных, но я отвечу, если вы не сможете изменить ее.Мой ответ похож на 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, и nullв других столбцах данных из 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;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...