SQL запрос из 3-х таблиц

317
21 декабря 2016, 02:25

Второй раз пытаюсь получить ответ на cвой вопрос: Выше приведена структура базы. Опишу все подробно: Первая таблица это сигнализации, в которой есть лишь id, firma и model. Здесь все понятно. Дальше идет таблица costusers - здесь хранятся данные о стоимости каждой сигнализации в разных магазинах, т.е. в поле id_sign хранится id из таблицы signalizacii, дальше поле id_user - это поле которое ссылается на поле uc_id в таблице uc1, и поле cost - это цена. Таблица uc1 - это все магазины во всех городах.

Нужно просто выбрать все сигнализации из таблицы signalizacii и добавить еще одно поле в котором будет хранить минимальная цена для конкретного города. Если вдруг случается так что в таблице costusers нет ни одной цены на какую-то сигнализацию в некотором городе, то нужно вывести 0.

Помогите пожалуйста, бьюсь уже несколько дней над этим запросом... SQL не мое...

Обновление

 select signalizacii.*, COALESCE(MIN(CASE WHEN costusers.cost != 0 THEN costusers.cost END), 0) 
 from signalizacii 
 left join costusers on id_sign = id 
 left join uc1 on costusers.id_user = uc1.user_id
 where uc1.city = 'Краснодар'
 group by signalizacii.id 

Вот что я хотел - показалось мне. Но не тут-то было если в таблице costusers не установлена цена на какую-нибудь сигнализацию то эта сигнализация не выводится вообще.

Проблема до сих пор не решена. Прошу помощи... Пожалуйста. Выше приведенный мной результат к сожалению не выводит сигнализацию вообще если цена на нее не установлена ни одним из магазинов указанного города. А она должна выводится но цена должна быть 0. Помогите пожалуйста.

Answer 1

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

SELECT signalizacii.*, name, coalesce(MIN(cost), 0) FROM uc1
JOIN signalizacii
LEFT JOIN costusers ON costusers.id_user = uc_id
AND costusers.id_sign = id
GROUP BY name, model

Рабочий пример: http://sqlfiddle.com/#!9/c9294/1/0

С отбором по городу:

SELECT signalizacii.*, name, coalesce(MIN(cost), 0) FROM uc1
JOIN signalizacii
LEFT JOIN costusers ON costusers.id_user = uc_id
AND costusers.id_sign = id
WHERE city='Краснодар'
GROUP BY name, model
Answer 2

Не пытаетесь ли вы убить одним камнем двух зайцев? Зачем вообще так нужно все сделать одним SQL запросом? Чего вы этим достигните?

Наверняка список сигнализаций у вас более-менее фиксирован, то есть не меняется каждую секунду. А значит вы можете загрузить одним запросом список всех сигнализаций, сохранить этот список в кеше в нужном порядке. Затем, по мере необходимости, запрашивать цены для какого-то города, при визуализации подставляя в колонку цены по каждой сигнализации.

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

Answer 3

Вам надо сначала объединить таблицы costusers и uc1, а только потом присоединять их объединение к таблице signalizacii.

select
    signalizacii.*,
    ifnull(min(costusers.cost), 0)
from
    signalizacii left join
    (
        costusers join
        uc1 on uc1.user_id = costusers.id_user
    ) on costusers.id_sign = signalizacii.id and uc1.city = 'Краснодар'
group by signalizacii.id

Если Вам не нравится uc1.city = 'Краснодар' в предложении on, то можно использовать подзапрос с where:

select
    signalizacii.*,
    ifnull(min(t.cost), 0)
from
    signalizacii left join
    (
        select
            costusers.id_sign,
            costusers.cost
        from
            costusers join
            uc1 on uc1.user_id = costusers.id_user
        where uc1.city = 'Краснодар'
    ) as t on t.id_sign = signalizacii.id
group by signalizacii.id

Смысл в том, что фильтровать по условию uc1.city = 'Краснодар' Вы должны либо до, либо во время присоединения costusers join uc1 к таблице signalizacii.

Answer 4

Много LEFT JOIN-ов, лучше бы нулевые цены отдельным UNION-ом сделать, а в остальном работает как просили.

SELECT DISTINCT s.*, ISNULL(m.mincost, 0) AS 'mincost'
FROM #signal_signalizacii s
    LEFT JOIN #signal_costusers u ON u.id_sign = s.id
    LEFT JOIN #signal_uc1 c ON u.[user_id] = c.uc_id AND c.city = 'Краснодар'
    OUTER APPLY (
        SELECT MIN(cost) AS 'mincost' FROM #signal_costusers
        WHERE id_sign = s.id
            AND [user_id] = c.uc_id
            AND c.uc_id IS NOT NULL
        ) m
Answer 5

Можете уточнить все таки в какой базе данных выполняется запрос? это Mysql Oracle PostGre или MsSql

Будь это Oracle, можно было бы использовать аналитические функции : http://www.sql.ru/blogs/oracleandsql/1926, конструкция

MIN(cost) OVER(Partition BY city)

работает именно так как вам нужно. Если уточните бд можно будет искать реализацию схожую с аналитическими функциями в Oracle

Простите не заметил что это Mysql, тогда прошу проверить: http://sqlfiddle.com/#!9/0de13/11

с нулами сами разберетесь тогда на что их заменить:

/*
все сигнализации из таблицы signalizacii */
SELECT s.*
  FROM signalizacii s ;
/*
и добавить минимальную цену по городу, т.е. добавляем поле 
Город
min_cost_s_c - минимальная цена на сигнализацию по городу
*/
SELECT s.*, u.city, min(c.cost) as min_cost_s_c
  FROM signalizacii s 
  left join costusers c ON s.id = c.id_sign
  left join uc1 u ON c.id_user = u.user_id
group by s.id, s.firma, s.model, u.city;
/*
если нужна минимальная цена по городу не зависимо от сигнализации, 
просто минимальная цена то делаем так
*/
SELECT s.*, t.city, t.cost
  FROM signalizacii s 
  left join ( select     u.city, 
                     min(c.cost) as cost, 
                     max(c.id_sign) as id_sign
                from costusers c 
                left join uc1 u ON c.id_user = u.user_id
             GROUP BY u.city)  t 
          ON s.id = t.id_sign;
READ ALSO
Как создать двумерный массив на основе данных из базы

Как создать двумерный массив на основе данных из базы

ЗдравствуйтеУ меня есть двумерный массив:

311
Mysql: обновление триггером своей же таблицы

Mysql: обновление триггером своей же таблицы

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

286
Не выводиться информация

Не выводиться информация

Не выводиться никакой информации(ошибок тоже нет), хотя таблица не не пустая

245
mysql_fetch_array() expects parameter 1 to be resource (or mysqli_result), boolean given

mysql_fetch_array() expects parameter 1 to be resource (or mysqli_result), boolean given

Я пытаюсь получить данные из таблицы MySQL, но вылезает одна из этих ошибок:

426