2013-05-28 4 views
0

Я ищу быстрый MySQL-запрос , который возвращает id продукта с наименьшим (min) или самым высоким (max) ценами по сравнению со всеми ценовыми категориями (a, b, c и d).SELECT id, где минимальное или максимальное значение из нескольких столбцов

У меня есть таблица продуктов chocolate_stock с несколькими ценовыми категориями. Очень легко получить самую низкую (min) или самую высокую (max) цена, от указанной категории (a или b или c или d).

id |  name  | price_a | price_b | price_c | price_d | 
-------------------------------------------------------------- 
1 | Chips Ahoy | 250 | 530 | 720 | 120 
-------------------------------------------------------------- 
2 | Chocolate Chunk | 250 | 90 | 32.92 | 110 
-------------------------------------------------------------- 
3 |  Oreo  | 103 | 44.52 | 250 | 850 
-------------------------------------------------------------- 

Категория цен: decimal(10,2). Вот пример, который возвращает самую высокую цену из категорий, но не идентификатор:

$t = 'chocolate_stock'; 
$arrIds = array(1, 3); 

$strQuery = "SELECT id, 
      MAX(price_a) AS price_a, 
      MAX(price_b) AS price_b, 
      MAX(price_c) AS price_c, 
      MAX(price_d) AS price_d 
      FROM $t WHERE id IN(". implode(',', array_map('intval', $arrIds)) .")"; 

Какой самый быстрый способ получить эту информацию?

+0

Просто, чтобы быть в безопасности: вы ожидаете двух записей результатов; учитывая ваши данные примера и запрос, который будет включать все три элемента (т. е. без предложения WHERE): min = (id: 2, price_c: 32.92) и max = (id: 3, price_d: 850)? – VolkerK

+0

Да, я знаю. Я могу ограничить их позже или изменить порядок результатов. – mate64

+0

Каков ваш желаемый результат? В частности, что вы ожидаете от MAX Price_a, где максимальная цена равна 250, и есть две продукции с этой ценой? – GarethD

ответ

1

Этот запрос делает то, что вы хотите:

(select t.* 
from $t t 
where . . . 
order by price_a desc 
limit 1) union all 
(select t.* 
from $t t 
where . . . 
order by price_b desc 
limit 1) union all 
(select t.* 
from $t t 
where . . . 
order by price_c desc 
limit 1) union all 
(select t.* 
from $t t 
where . . . 
order by price_d desc 
limit 1) 

Если у вас есть индекс по id он должен выполнять достаточно хорошо ,

Этот подход требует четырех проходов по таблице (хотя индекс на id должен значительно уменьшить это). Следующий подход требует только один проход через стол:

select MAX(price_a), 
     substring_index(group_concat(id order by price_a desc), ',', 1), 
     max(price_b), 
     substring_index(group_concat(id order by price_b desc), ',', 1), 
     max(price_c), 
     substring_index(group_concat(id order by price_c desc), ',', 1), 
     max(price_d), 
     substring_index(group_concat(id order by price_d desc), ',', 1) 
from $t 
where . . . 

Он использует трюк с group_concat() и substring_index(), чтобы получить максимальный идентификатор для каждого из столбцов.

+0

+1 Отличная реализация 'SQL' – mate64

-1

Вы не получаете идентификатор, потому что MAX возвращает одно значение. Но это не так с id.You можно использовать раздельные запросы, как

SELECT id,MAX(price_a) FROM $t WHERE id IN (". implode(',', array_map('intval', $arrIds)).")"; 
SELECT id,MAX(price_b) FROM $t WHERE id IN (". implode(',', array_map('intval', $arrIds)).")"; 

и т.д.

+0

Спасибо. Разве это невозможно в одном * одном вопросе? – mate64

+0

Нет. Это не так. Это потому, что MAX, MIN, AVG и все возвращают одну запись. Не кратно. По вашей причине вам потребуется несколько идентификаторов с максимальной и минимальной ценой из каждого региона. Это невозможно. – Ananth

1

Это могло бы помочь, если бы вы были пластинчатый то, что вы хотите, чтобы ваш выход выглядеть, но я думаю, что часть вы missing - это предложение HAVING.

первый - попробовать это

select min(id), max(price_a) from $t having price_a = max(price_a) 

Тогда попробуйте

select min(id), min(price_a) from $t having price_a = min(price_a) 
union 
select min(id), max(price_a) from $t having price_a = max(price_a) 
1

Первое, что вы хотели бы сделать, это нормализовать свои данные, для простоты позже запрашивая я хотел бы создать следующий вид:

CREATE VIEW NormalT 
AS 
    SELECT ID, Name, 'Price_a' AS Type, Price_a AS Price 
    FROM T 
    UNION ALL 
    SELECT ID, Name, 'Price_b' AS Type, Price_b AS Price 
    FROM T 
    UNION ALL 
    SELECT ID, Name, 'Price_c' AS Type, Price_c AS Price 
    FROM T 
    UNION ALL 
    SELECT ID, Name, 'Price_d' AS Type, Price_d AS Price 
    FROM T; 

Тогда я не уверен в формате, который вы хотите, если вы хотите минимальное с максимальной для каждой цены, которую вы могли бы использовать это:

SELECT mt.Type2, 
     mt.Type, 
     mt.Price, 
     t.ID, 
     t.Name 
FROM ( SELECT Type, MIN(Price) AS Price, 'MIN' AS Type2 
      FROM NormalT 
      GROUP BY Type 
      UNION ALL 
      SELECT Type, MAX(Price) AS Price, 'MAX' AS Type2 
      FROM NormalT 
      GROUP BY Type 
     ) mt 
     INNER JOIN NormalT T 
      ON mt.Type = T.Type 
      AND mt.Price = t.Price 
ORDER BY mt.Type2, mt.Type, t.ID; 

, которые будут выводить следующие из ваших данных образца:

TYPE2 TYPE  PRICE ID NAME 
MAX  Price_a  250  1 Chips Ahoy 
MAX  Price_a  250  2 Chocolate Chunk 
MAX  Price_b  530  1 Chips Ahoy 
MAX  Price_c  720  1 Chips Ahoy 
MAX  Price_d  850  3 Oreo 
MIN  Price_a  103  3 Oreo 
MIN  Price_b  44.52 3 Oreo 
MIN  Price_c  32.92 2 Chocolate Chunk 
MIN  Price_d  110  2 Chocolate Chunk 

Однако, если это только минимальное и максимальное из всех цен (а, б, в и г), то вы могли бы использовать это:

SELECT mt.Type2, 
     t.Type, 
     mt.Price, 
     t.ID, 
     t.Name 
FROM ( SELECT MIN(Price) AS Price, 'MIN' AS Type2 
      FROM NormalT 
      UNION ALL 
      SELECT MAX(Price) AS Price, 'MAX' AS Type2 
      FROM NormalT 
     ) mt 
     INNER JOIN NormalT T 
      ON mt.Price = t.Price; 

, которые будут выводить это:

TYPE2 TYPE PRICE  ID NAME 
MIN  Price_c  32.92 2 Chocolate Chunk 
MAX  Price_d  850  3 Oreo 

Examples on SQL Fiddle

1

Попробуйте, это эмуляция Analytics, как MYSQL не имеет их по умолчанию:

SELECT id, 
      (select MAX(price_a) from $t t2 where t2.id = t1.id) AS price_a, 
      (select MAX(price_b) from $t t2 where t2.id = t1.id) AS price_b, 
      (select MAX(price_c) from $t t2 where t2.id = t1.id) AS price_c, 
      (select MAX(price_d) from $t t2 where t2.id = t1.id) AS price_d 
      FROM $t t1 WHERE id IN(". implode(',', array_map('intval', $arrIds)) .") 

Источник от: http://www.oreillynet.com/pub/a/mysql/2007/03/29/emulating-analytic-aka-ranking-functions-with-mysql.html?page=3

Смежные вопросы