Имя: Пароль:
IT
 
MySQL помогите с запросом
,
0 vladnet
 
29.07.14
19:21
Есть запрос который склеивается с таблицей ordered_products:

SELECT
    products.code,
    products.name AS products_name,
    orders.code as order_code,
    SUM(orders.already_paid) AS already_paid,
    SUM(products.amount) AS amount,
    ordered_products.supplier_price AS supplier_price

    FROM smacs2_production.orders
    ON checks.id = orders.check_id
    LEFT JOIN smacs2_production.products
    ON orders.id = products.order_id
    LEFT JOIN smacs2_production.ordered_products AS ordered_products
    ON ordered_products.product_id = products.id


Но после того как меняю в нем таблицу ordered_products на внуренний запрос
(SELECT
  ordered_products.vat,
  ordered_products.product_id,
  MAX(ordered_products.created_at) AS created_at
  FROM smacs2_production.ordered_products AS ordered_products
  GROUP BY ordered_products.product_id)

Чтобы исключить дубли, запрос начинает жутко тормозить. Как можно поправить, вообще хотел временную таблицу завести, но нет ее в mysql чтоли и не понятно поможет ли или нет.

Причем по отдельности запросы выполняются быстрее намного чем один вместе...

Подскажите пожалуйста что можно сделать
1 kokamoonga
 
29.07.14
19:26
(0) полностью тормозящий запрос как выглядит?
2 ДенисЧ
 
29.07.14
19:27
А explain что про него говорит?
3 kokamoonga
 
29.07.14
19:31
(0) ну и кстати, довольно оригинальный способ выборки недублирующихся строк. DISTINCT чем не угодил?
4 vladnet
 
29.07.14
19:32
(1) Он очень большой

SELECT
orders.discount,
products.code,
products.name AS products_name,
orders.code as order_code,
orders.delivery_date,
products.price*(1-orders.discount/100) AS price,
SUM(orders.already_paid) AS already_paid,
SUM(orders.shipping_cost) AS shipping_cost,
SUM(products.amount) AS amount,
SUM(products.delivered_amount) AS delivered_amount,
   SUM(invoice_positions.total_cost_clean) AS total_cost_clean,
SUM(invoice_positions.vat_amount) AS vat_amount,
SUM(invoice_positions.amount) AS invoice_amount,
ordered_products.vat AS ordered_products_vat,
ordered_products.supplier_price AS supplier_price,
invoice_positions.vat,
invoice_positions.gtd_number,
  DATE(cashbox_operations.made_at) AS ssss

  FROM smacs2_production.cashbox_operations
LEFT JOIN smacs2_production.checks
  ON cashbox_operations.id = checks.cashbox_operation_id
LEFT JOIN smacs2_production.orders
  ON checks.id = orders.check_id
  LEFT JOIN smacs2_production.products
   ON orders.id = products.order_id
  
  LEFT JOIN -- smacs2_production.ordered_products
        (SELECT
          ordered_products.vat,
          ordered_products.supplier_price,
          ordered_products.product_id,
          MAX(ordered_products.created_at) AS created_at
          FROM smacs2_production.ordered_products AS ordered_products
          GROUP BY ordered_products.product_id) AS ordered_products
    ON ordered_products.product_id = products.id

  LEFT JOIN smacs2_production.stock_products
   ON stock_products.id = products.stock_product_id
  LEFT JOIN smacs2_production.invoice_positions
   ON invoice_positions.id = stock_products.id
WHERE ((products.workflow_state = 'cancelled' AND cashbox_operations.made_at < products.updated_at) OR products.workflow_state <> 'cancelled') AND cashbox_operations.kind = 'sale'  AND ((products.workflow_state = 'cancelled' AND products.is_deleted = 1) OR products.is_deleted <> 1)
  AND DATE(cashbox_operations.made_at) >= '2014-06-27'
  AND DATE(cashbox_operations.made_at) < '2014-06-28'
  AND orders.shipping_id <> 10
      AND orders.payment_id = 1
GROUP BY orders.delivery_date, orders.discount, orders.code, products.code, products.name, products.price*(1-orders.discount/100), invoice_positions.vat, invoice_positions.gtd_number
ORDER BY orders.delivery_date, orders.code, products.code, ordered_products.created_at DESC
5 vladnet
 
29.07.14
19:32
(2) еще бы знать что это ( Я больше запросы на 1с пишу
6 vladnet
 
29.07.14
19:34
(3) Почитал что советуют, подскажи пожалуйста как им, может distinct быстрее.

вот это надо оптимизировать

SELECT
  ordered_products.vat,
  ordered_products.product_id,
  MAX(ordered_products.created_at) AS created_at
  FROM smacs2_production.ordered_products AS ordered_products
  GROUP BY ordered_products.product_id

Получить надо последний vat который есть в таблице по поле created_at
7 ДенисЧ
 
29.07.14
19:34
(5) зайди в консоль сервера скажи explain и твой запрос. ; не забудь.
ЧТо получится - покажи.
8 kokamoonga
 
29.07.14
19:34
(5) вместо  GROUP BY id  лучше SELECT DISTINCT

группировка на больших таблицах довольно дорогая операция.
9 vladnet
 
29.07.14
19:36
(8) А можно при этом как то сделать так чтобы брали записи у которых максимальная дата создания?
10 kokamoonga
 
29.07.14
19:38
(9) а сейчас берет что попало?
11 vladnet
 
29.07.14
19:39
(7) пока сам не разбирался что это

1    PRIMARY    orders    ref    PRIMARY,fk_orders_shippings,fk_orders_payment_ids    fk_orders_payment_ids    5    const    249292    Using where; Using temporary; Using filesort
1    PRIMARY    checks    eq_ref    PRIMARY    PRIMARY    4    smacs2_production.orders.check_id    1    Using where
1    PRIMARY    cashbox_operations    eq_ref    PRIMARY    PRIMARY    4    smacs2_production.checks.cashbox_operation_id    1    Using where
1    PRIMARY    products    ref    fk_products_orders,index_products_on_workflow_state,index_products_on_updated_at,updated_at    fk_products_orders    4    smacs2_production.orders.id    1    Using where
1    PRIMARY    <derived2>    ALL    (null)    (null)    (null)    (null)    1297947    
1    PRIMARY    stock_products    eq_ref    PRIMARY    PRIMARY    4    smacs2_production.products.stock_product_id    1    Using index
1    PRIMARY    invoice_positions    eq_ref    PRIMARY    PRIMARY    4    smacs2_production.stock_products.id    1    
2    DERIVED    ordered_products    index    (null)    fk_ordered_products_products    5    (null)    1758871
12 vladnet
 
29.07.14
19:41
(10) сейчас то правильно берет

например в таблице есть записи

2014.01.04 молоко 12
2014.02.04 молоко 11
2014.03.04 молоко 10
2014.03.04 колбаса 11

нужно чтобы вернуло
2014.03.04 молоко 10
2014.03.04 колбаса 11

т.е. последние поставки по каждому товару
13 ДенисЧ
 
29.07.14
19:41
(11) В индексы не попадает.
14 vladnet
 
29.07.14
19:49
(13) А как добавить индексы. И можно ли добавить их временные? База не моя. И по каким полям добавить?
15 kokamoonga
 
29.07.14
19:59
(0) >>> вообще хотел временную таблицу завести, но нет ее в mysql

Прямо-таки и нет?.. http://dev.mysql.com/doc/refman/5.6/en/create-table.html

Может помочь, если этот подзапрос с группировкой вынести во временную
16 kokamoonga
 
29.07.14
20:00
(15) + это конечно если у юзера есть права на CREATE/DROP
17 vladnet
 
29.07.14
20:01
Может пример дашь простой?
В 1с это просто говоришь INTO ИмяВременнойТаблицы
18 kokamoonga
 
29.07.14
20:05
(17) там же в доке куча примеров, типа такого

CREATE TEMPORARY TABLE IF NOT EXISTS `schema`.`Employee` (
`idEmployee` VARCHAR(45) NOT NULL ,
`Name` VARCHAR(255) NULL ,
`idAddresses` VARCHAR(45) NULL ,
PRIMARY KEY (`idEmployee`) ,
CONSTRAINT `fkEmployee_Addresses`
FOREIGN KEY `fkEmployee_Addresses` (`idAddresses`)
REFERENCES `schema`.`Addresses` (`idAddresses`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8
COLLATE = utf8_bin
19 vladnet
 
29.07.14
20:08
Да есть, но не понятно как это всунуть в запрос. Чтобы в запросе было:
1. создание временной таблицы
2. помещение во временную таблицу подзапроса
3. выполнение основного запроса
4. грохнуть временную таблицу?
20 kokamoonga
 
29.07.14
20:13
(19) 1. Создание в (18). С поправкой на нужные поля. Очевидно они должны совпадать с твоей будущей выборкой.

2. Помещение подзапроса типа так:

INSERT INTO `temporary_table`
VAlUES (val1, val2,...valN)
SELECT val1, val2,...,valN
FROM `ordered_products`
WHERE clause

3. Выполнение основного запроса с заменой подзапроса на :

SELECT * FROM temporary_table


4. DROP temporary_table  (http://dev.mysql.com/doc/refman/5.1/en/drop-table.html)
21 kokamoonga
 
29.07.14
20:14
(20) + даже если не поможет, увлекательно проведешь время за чтением документации:)
22 vladnet
 
29.07.14
20:15
(21) Ага )) Спасибо, надеюсь как нибудь решим. Но насколько понял тут индексы надо добавлять.
23 kokamoonga
 
29.07.14
20:17
(22) тут надо от запросов-монстров избавляться. когда таблицы распухнут до серьезных размеров, с такими запросами никакие временные таблицы не помогут.
24 Fragster
 
гуру
29.07.14
20:18
еще неплохо бы описать, что автор хочет получить своим запросом (возможно можно просто пересмотреть его логику)
25 kokamoonga
 
29.07.14
20:19
(24) ну собственно я об этом в (23).

Часть логики запроса, как мне кажется, может без напряжения переехать в основной код. А другую часть можно переписать как-то попроще.
26 vlandev
 
29.07.14
20:59
Специально удалять временную таблицу не обязательно:

A TEMPORARY table is visible only to the current session, and is dropped automatically when the session is closed

Хотя это зависит от особенностей подключения к MySQL.
27 kokamoonga
 
29.07.14
22:23
(26) Лучше перебдеть чем недобдеть.
28 vladnet
 
30.07.14
10:27
Запрос очень простой, есть заказ, у заказа есть табличная часть. Нужно получить все заказанные товары и присоединить к ним последнюю цену из поставок этого товара нам.

Ничего серьезного по моему в запросе нет.

Проблема в том что запросы по отдельности выполняются быстро. А как только их склеиваю получается очень долго. На 1с у меня никогда таких проблем не было.
29 rsv
 
30.07.14
10:42
(28) Делайте  джойны последовательно , а не все сразу. Обнаружите узкое место .
30 rsv
 
30.07.14
10:44
Возможно в join  придется добавить пару условий соединения и так далее  или еще чего ...
31 rsv
 
30.07.14
10:45
Имхо вся литература по mySQL  по вывод -   Хорош на  коротких простых запросах  к нормализованным таблицам.
32 Fragster
 
гуру
30.07.14
11:26
(29) у него в explain всё написано, каких индексов не хватает и т.п. вопрос в том, правильна ли сама логика запроса
33 Fragster
 
гуру
30.07.14
11:27
(28) дай доступ к phpmyadmin
34 vladnet
 
30.07.14
11:32
(33) К сожалению там не вебка (
Затык появляется после того как я меняю

  LEFT JOIN smacs2_production.ordered_products AS ordered_products

На

  LEFT JOIN (SELECT
  ordered_products.vat,
  ordered_products.product_id,
  MAX(ordered_products.created_at) AS created_at
  FROM smacs2_production.ordered_products AS ordered_products
  GROUP BY ordered_products.product_id) AS ordered_products

Причем если по отдельности выполнять то все хорошо работает
35 vladnet
 
30.07.14
11:33
(33) Могу через Амми админ доступ дать посмотреть все. В скайпе: miragevlad
36 vladnet
 
30.07.14
11:34
Через временные таблицы кстати пока не получается, у меня нет доступа создавать таблицы, по крайней мере пока ((
37 Fragster
 
гуру
30.07.14
11:41
(35) есть тимвьювер и почта в личке
38 vladnet
 
30.07.14
11:52
(37) написал
39 kokamoonga
 
30.07.14
14:02
(28) никто и не говорил, что запрос сложный. Просто в нем 4 джонйна на больших (потенциально больших) таблицах. Плюсом к этом подзапрос, который вообще-то не нужен. Поля выборки, условия и группировку можно вынести в общие группы.
40 kokamoonga
 
30.07.14
15:24
соорудил нечто похожее по смыслу на реальных данных.

Два запроса:

SELECT
  customers.customerID,
  customers.Login,
  customers.Email,
  orders.orderID,
  MAX(orders.order_amount) AS order_amount
FROM orders
  INNER JOIN customers
    ON orders.customerID = customers.customerID
WHERE orders.order_amount > 1000
GROUP BY orders.customerID
ORDER BY orders.customerID



SELECT
  customers.customerID,
  customers.Login,
  customers.Email,
  Sub.orderID,
  MAX(Sub.order_amount) AS order_amount
FROM
  (SELECT
      orders.orderID,
      orders.order_amount,
      orders.customerID
    FROM orders
   WHERE orders.order_amount > 1000
    ) Sub
  LEFT OUTER JOIN
  customers
    ON Sub.customerID = customers.customerID
GROUP BY Sub.customerID
ORDER BY customers.customerID


Вариант с простым джойном без подзапроса стабильно быстрее процента на 2,5-3. Может это погрешность, но выглядит логично.
41 kokamoonga
 
30.07.14
15:25
(40) + в первом варианте не INNER, а LEFT JOIN