MySQL: выберите одну строку из левого соединения только один раз в результате с несколькими строками

#mysql #sql #join #left-join

#mysql #sql #Присоединиться #левое соединение

Вопрос:

Хорошо, с помощью SO и некоторых моих собственных, по общему признанию, ограниченных умственных способностей, я получил свой запрос в основном работающим. Я получаю результат, который мне нужен, LEFT JOIN и он отображается во внешнем запросе. Тем не менее, он отображается несколько раз для каждой строки с тем же идентификатором, что и ожидалось, но это не то, что мне нужно…

Мне интересно, возможно ли показать объединенный результат только один раз.Я нашел много ответов в Интернете только для извлечения первой строки из таблицы, которая была оставлена присоединенной. Однако то, что я ищу, — это показать объединенный результат только один раз в результате (не обязательно в первый раз).

Это [упрощенная версия] моего запроса:

 select
    `order`.order_id,
    `order`.total,
    `order`.comment,
    `order`.shipping_cost,
    `ct`.amount as credit
from `order`
left join (
    select
        customer_id,
        sum(amount) as amount
    from `customer_transaction`
    where
        amount > 0
        and integrated = 0
    group by
        customer_id
) as ct
    on `ct`.`customer_id` = `order`.`customer_id`
where
    `order`.integrated = 0;
  

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

Приведенный выше запрос «работает», пока у одного клиента не будет более одного заказа. В этом случае, если я использую приведенный выше запрос, он будет эффективно считать, что ко всем заказам применен кредит. Например, приведенный выше запрос возвращает результат, подобный:

  ------------- ---------- ----------- --------- --------------- -------- 
| customer_id | order_id | total     | comment | shipping_cost | credit |
 ------------- ---------- ----------- --------- --------------- -------- 
|       52227 |   184589 |   28.6910 |         |       12.7410 | 8.0000 |
|       52227 |   184590 |   28.6910 |         |       12.7410 | 8.0000 |
 ------------- ---------- ----------- --------- --------------- -------- 
  

У этого клиента есть кредит в размере 8 долларов США «на своем счете», а не 16 долларов США. Т.Е. Сумма неиспользованных кредитных транзакций для клиента составляет 8. Я пытаюсь получить этот кредит, чтобы он отображался только один раз. Другими словами, это результат, который я хотел бы:

  ------------- ---------- ----------- --------- --------------- -------- 
| customer_id | order_id | total     | comment | shipping_cost | credit |
 ------------- ---------- ----------- --------- --------------- -------- 
|       52227 |   184589 |   28.6910 |         |       12.7410 | 8.0000 |
|       52227 |   184590 |   28.6910 |         |       12.7410 | NULL   |
 ------------- ---------- ----------- --------- --------------- -------- 
  

(Обратите внимание на значение NULL в правом нижнем углу. Это также может быть 0 или, может быть, даже 8 / $ NUM_ROWS. Мне нужно было бы проверить, будет ли программное обеспечение работать с этим. Вероятно, нет.)

Причина этого сумасшествия в том, что одна часть программного обеспечения хранит кредит как одну или несколько строк, связанных с клиентом, а другая ожидает его как часть одного заказа, и предполагается, что все они будут интегрированы «сразу». Он запускает один запрос, чтобы получить всю информацию о заказе, включая кредит, и второй для позиций (без кредита). Итак … мне нужно внести кредит в заказ, затем установить для него значение integrated = 1, чтобы он не использовался в следующий раз (у меня уже есть эта часть, работающая).

Есть идеи, как я могу отобразить кредит только один раз, или это невозможно с помощью «простого» запроса (в отличие от кода)?

Спасибо

Комментарии:

1. Создайте SQL-скрипку с вашими тестовыми данными и опубликуйте ожидаемый результат.

Ответ №1:

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

 select o.order_id, o.total, o.comment, o.shipping_cost, ct.amount as credit
from (select o.*,
             @rn :=  if(@cid = customer_id, @rn   1, 1) as rn,
             @cid := customer_id
      from `order` o cross join
           (select @rn := 0, @cid := 0) vars
     ) o left join
     (select customer_id, sum(amount) as amount
      from `customer_transaction`
      where amount > 0 and integrated = 0
      group by customer_id
     ) ct
     on `ct`.`customer_id` = o.`customer_id` and o.rn = 1
where o.integrated = 0;
  

A left join сохраняет все строки в первой таблице и соответствующие строки во второй таблице, если это необходимо. Поскольку условие o.rn = 1 соответствует только первой строке, значения будут получены только в первой строке.