MySQL запрос для предметов, где средняя цена меньше, чем X? - PullRequest
2 голосов
/ 05 февраля 2012

Я зашел в тупик с тем, как сделать следующее чисто в MySQL, и я прибег к тому, чтобы взять свой набор результатов и впоследствии манипулировать им в ruby, что не кажется идеальным.

Вот вопрос.С набором данных 'items' вроде:

id  state_id  price  issue_date  listed
1   5         450    2011        1
1   5         455    2011        1
1   5         490    2011        1 
1   5         510    2012        0
1   5         525    2012        1
...

Я пытаюсь получить что-то like :

SELECT * FROM items 
WHERE ([some conditions], e.g. issue_date >= 2011 and listed=1) 
 AND state_id = 5
GROUP BY id
HAVING AVG(price) <= 500
ORDER BY price DESC 
LIMIT 25

По сути, я хочу получить "группу""предметов, средняя цена которых падает ниже определенного порога.Я знаю, что мой приведенный выше пример «группировать по» и «иметь» не верен, поскольку он просто даст AVG(price) этого одного элемента, что на самом деле не имеет смысла.Я просто пытаюсь проиллюстрировать мой желаемый результат.

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

В настоящее время я просто делаю вышеуказанный запрос без HAVING AVG(price) и добавляю отдельные элементы по одному (в рубине), пока не достигну желаемого среднего.Было бы здорово, если бы я мог понять, как это сделать в SQL.Использование подзапросов или что-то умное, например, присоединение таблицы к себе, безусловно, приемлемые решения, если они работают хорошо!Спасибо!

ОБНОВЛЕНИЕ: В ответ на ответ Тудора ниже приведены некоторые пояснения.Всегда будет целевое количество в дополнение к целевому среднему.И мы всегда сортировали бы результаты по цене от низкой к высокой и по дате.

Так что, если бы у нас было 10 товаров по цене 5 долларов, и мы хотели бы найти 5 товаров со средним значением <6 долларов, мыпросто вернуть первые 5 предметов.Мы не вернем только первый и не вернем первые 3, сгруппированные с последними 2. По сути, сейчас мой код в ruby ​​работает. </p>

Ответы [ 3 ]

2 голосов
/ 05 февраля 2012

Я бы сделал почти обратное тому, что предоставил Джаспер ... Начните ваш запрос с ваших критериев, чтобы явно ограничить несколько элементов, которые МОГУТ квалифицироваться, вместо того, чтобы получать все элементы и выполнять дополнительный выбор для каждой записи. Может показаться большим ударом по производительности ... может быть неправильно, но вот мое предложение ..

select
      i2.*
   from
      ( SELECT  i.id
           FROM items i
           WHERE 
                 i.issue_date > 2011 
             AND i.listed = 1
             AND i.state_id = 5
           GROUP BY
              i.id
           HAVING 
              AVG( i.price) <= 500 ) PreQualify

      JOIN items i2
         on PreQualify.id = i2.id
             AND i2.issue_date > 2011 
             AND i2.listed = 1
             AND i2.state_id = 5
   order by
      i2.price desc
   limit
      25

Не уверен в порядке, особенно если вы хотите группировать по элементам ... Кроме того, я бы обеспечил индекс для (state_id, Listed, id, issue_date)

РАЗЪЯСНЕНИЕ на комментарии

Я думаю, что я прав. Не путайте предложение «HAVING» с «WHERE». ГДЕ говорит ДА или НЕ включает в зависимости от определенных условий. HAVING означает, что после того, как все предложения where и группировка выполнены, набор результатов «ПОТЕНЦИАЛЬНО» примет ответ. ТОГДА HAVING проверяется, и если он все еще имеет право, включает в набор результатов, в противном случае выбрасывает его. Попробуйте выполнить следующее только из запроса INNER ... Выполните один раз БЕЗ предложения HAVING, затем снова С предложением HAVING ...

SELECT  i.id, avg( i.price )
   FROM items i
   WHERE i.issue_date > 2011 
     AND i.listed = 1
     AND i.state_id = 5
   GROUP BY
      i.id
   HAVING 
      AVG( i.price) <= 500

По мере того, как вы будете больше заниматься написанием запросов, попробуйте отдельные части, чтобы увидеть, что вы получаете, а не то, о чем вы думаете ... Вы узнаете, как / почему определенные вещи работают. Кроме того, вы сейчас говорите в своем обновленном вопросе о получении нескольких идентификаторов и цен по очевидным низким и высоким диапазонам ... но вы также применяете ограничение. Если у вас было 20 предметов, и у каждого было 10 квалификационных записей, ваш лимит 25 будет показывать все первый элемент и 5 во второй ... это НЕ то, что я думаю, что вы хотите ... вы можете хотеть 25 из каждого квалифицированного "Я бы". Это обернуло бы этот запрос на еще один уровень ...

1 голос
/ 05 февраля 2012

То, что делает MySQL, вполне логично. То, что вы хотите сделать, не имеет смысла:

  • если у вас есть, скажем, 4 элемента, каждый с ценой 5, и вы ставите HAVING AVERAGE <= 7, что вы говорите, что запрос должен возвращать ALL перестановок, например:

    • {1} - поскольку элемент с идентификатором 1 может быть группой сам по себе
    • {1,2}
    • {1,3}
    • {1,4}
    • {1,2,3}
    • {1,2,4} * * один тысяча двадцать-одна

    ...

    • и так далее?

Ваш алгоритм вычисления среднего в ruby ​​также недействителен, если у вас есть элементы со значениями 5, 1, 7, 10 - и вы ищете среднее значение меньше 7, элемент со значением 10 может быть возвращен только в группа со элементом значения 1. Но, по вашему алгоритму (если я правильно понял), элемент со значением 1 возвращается в первую группу.

Обновление

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

После поиска в Google я нашел эту статью , которая пытается решить проблему ранцев с ИИ, написанным на SQL.

Рассматривая цену вашего предмета как вес, имея количество предметов и желаемое среднее значение, вы можете рассчитать максимальное значение, которое можно ввести в «рюкзак», умножив desired_cost на number_of_items

0 голосов
/ 05 февраля 2012

Я не совсем уверен в вашем вопросе, но я думаю, что это решение вашей проблемы:

SELECT * FROM items 
WHERE (some "conditions", e.g. issue_date > 2011 and listed=1) 
 AND state_id = 5
 AND id IN (SELECT id
            FROM items
            GROUP BY id
            HAVING AVG(price) <= 500)

ORDER BY price DESC
LIMIT 25

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

...