SQL - возвращает строку, даже если сбой LEFT JOIN - PullRequest
0 голосов
/ 14 мая 2018

У меня есть запрос с несколькими JOINS, который возвращает данные из нескольких таблиц

SELECT A.ItemID, A.Title, A.PictureURL as Image, SUM(B.AmountPaid) as revenue, COUNT(*) AS numOrders, B.CheckoutStatus_LastModifiedTime, C.HitCount as today, D.HitCount as yesterday 
    FROM ebay_activelistings A 
    LEFT JOIN ebay_orders B ON 
        A.ItemID = B.ItemID 
        AND ( (B.CheckoutStatus_LastModifiedTime >= :start_date 
        AND B.CheckoutStatus_LastModifiedTime < :end_date) OR B.CheckoutStatus_LastModifiedTime IS NULL) 
        AND (B.CheckoutStatus_Status = 'Complete' OR B.CheckoutStatus_Status IS NULL) 
    LEFT JOIN ebay_hitswatches C ON A.ItemID = C.ItemID 
    LEFT JOIN ebay_hitswatches D ON A.ItemID = D.ItemID 
    WHERE 
        A.ListingStatus = 'Active' 
        AND C.Timstamp LIKE :end_date2 
        AND D.Timstamp LIKE :start_date2
        " . (count($listingIds) > 0 ? 'AND A.ItemID IN ('.implode(',', $listingIds).')' : '') . "
    GROUP BY A.ItemID

Этот запрос работает нормально и возвращает строку из таблицы "ebay_activelistings" только в том случае, если в таблице "ebay_orders" найден соответствующий "ItemID".

Однако я хочу вернуть строку, несмотря ни на что, даже если в «ebay_orders» нет соответствующего «ItemID», то «B.» данные можно оставить пустыми, но я все еще получаю строку с другими данными.

Любая помощь будет принята с благодарностью, спасибо.

Ответы [ 3 ]

0 голосов
/ 14 мая 2018

Я не уверен, что полностью понимаю поведение вашего запроса и то, что вы ожидаете в результате.

В вашем случае эти условия могут быть слишком строгими:

AND C.Timstamp LIKE :end_date2 
AND D.Timstamp LIKE :start_date2

Может быть, попробовать

AND (C.Timstamp IS NULL OR C.Timstamp LIKE :end_date2 )
AND (D.Timstamp IS NULL OR LIKE :start_date2)
0 голосов
/ 14 мая 2018

Подумайте о внешнем соединении, работающем так:

Если подходящей строки не найдено, генерируется фиктивная строка, которая служит вместо соответствующей строки. Эта фиктивная строка целиком состоит из значений NULL.

Когда мы перейдем к предложению WHERE, любое условие, для которого столбец из внешней объединенной таблицы должен быть ненулевым, исключит сгенерированную фиктивную строку. И это фактически сводит на нет «внешность» соединения, делая его эквивалентным внутреннему соединению.


Чтобы связать это конкретно с запросом в вопросе: это условие в предложении WHERE:

 C.Timstamp LIKE :end_date2 

требует, чтобы Timstamp из внешней объединенной таблицы C был ненулевым.

Это условие оценивается как NULL для любой строки, имеющей значение NULL для C.Timstamp. Поскольку условие не оценивается как ИСТИНА, строка будет исключена. Вот причина поведения, которое мы наблюдаем.


Исправлено устранение требования, что C.Timstamp должен быть ненулевым.

Обычный шаблон - удалить условие из предложения WHERE и переместить в предложение ON объединения, например,

   LEFT
   JOIN ebay_hitswatches C
     ON C.ItemID = A.ItemID
    AND C.Timstamp LIKE :end_date2
  WHERE ...
0 голосов
/ 14 мая 2018

Вы использовали LEFT JOIN, чтобы сохранить строки от A, даже если у них нет соответствующих элементов в B или C или D.

Однако, если в вашем предложении WHERE есть условия, которые относятся кB или C или D, тогда эти строки будут выпадать из вашего ответа.

Помещение условий, касающихся B или C или D, в условие JOIN сохраняет их в вашем окончательном ответе.

Краткий ответ -переместите условия WHERE в условия JOIN.

SELECT A.ItemID, A.Title, A.PictureURL as Image, SUM(B.AmountPaid) as revenue, COUNT(*) AS numOrders, B.CheckoutStatus_LastModifiedTime, C.HitCount as today, D.HitCount as yesterday 
    FROM ebay_activelistings A 
    LEFT JOIN ebay_orders B ON 
        A.ItemID = B.ItemID 
        AND ( (B.CheckoutStatus_LastModifiedTime >= :start_date 
        AND B.CheckoutStatus_LastModifiedTime < :end_date) OR B.CheckoutStatus_LastModifiedTime IS NULL) 
        AND (B.CheckoutStatus_Status = 'Complete' OR B.CheckoutStatus_Status IS NULL) 
    LEFT JOIN ebay_hitswatches C ON A.ItemID = C.ItemID AND C.Timstamp LIKE :end_date2 
    LEFT JOIN ebay_hitswatches D ON A.ItemID = D.ItemID AND D.Timstamp LIKE :start_date2
    WHERE 
        A.ListingStatus = 'Active' $itemClause
    GROUP BY A.ItemID

Но ... в вашем php настройте $ itemClause следующим образом - это будет либо пустым, либо будет включать список чисел.

$itemClause = count($listingIds) > 0 ? ' AND A.ItemID IN ('.implode(',', $listingIds).')' : '';
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...