Предложение LEFT OUTER JOIN + WHERE в Couchbase

Я пытаюсь выполнить LEFT OUTER JOIN при фильтрации в правой части соединения.

Для этого я создал следующий индекс:

CREATE INDEX `idx_store_order` ON `myBucket`(("Store::" || `storeId`)) WHERE ((`docType` = "Order") or (`docType` is missing))

и я пытаюсь выполнить следующий запрос:

SELECT store.status, order.clientId, store.docId 
FROM myBucket store
LEFT OUTER JOIN myBucket order ON KEY ("Store::" || order.storeId) FOR store
WHERE store.docType="Store"
AND (order.docType="Order" OR order.docType IS MISSING)
AND order.clientId="9281ae36-a418-4ea3-93f0-bfd7b1a38248"

У меня есть 30 документов с docType="Store", но когда я выполняю этот запрос, я не получаю 30 результатов. Если я удалю последнее предложение и сгруппирую по хранилищу, я получу 30 результатов, так что последнее предложение влияет на окончательные результаты.

Я также пробовал следующее утверждение (безуспешно) в качестве последнего предложения:

(AND order.clientId="9281ae36-a418-4ea3-93f0-bfd7b1a38248" OR order.docType IS MISSING)

Я что-то упускаю? Спасибо

ИЗМЕНИТЬ Вот запрос с объяснением:

[
  {
    "plan": {
      "#operator": "Sequence",
      "~children": [
        {
          "#operator": "IndexScan",
          "index": "idx_docType",
          "index_id": "e498d0c0ee2f0d9d",
          "keyspace": "myBucket",
          "namespace": "default",
          "spans": [
            {
              "Range": {
                "High": [
                  "\"Store\""
                ],
                "Inclusion": 3,
                "Low": [
                  "\"Store\""
                ]
              }
            }
          ],
          "using": "gsi"
        },
        {
          "#operator": "Parallel",
          "~child": {
            "#operator": "Sequence",
            "~children": [
              {
                "#operator": "Fetch",
                "as": "store",
                "keyspace": "myBucket",
                "namespace": "default"
              },
              {
                "#operator": "IndexJoin",
                "as": "order",
                "for": "store",
                "keyspace": "myBucket",
                "namespace": "default",
                "on_key": "(\"Store::\" || (`order`.`storeId`))",
                "outer": true,
                "scan": {
                  "index": "idx_store_order",
                  "index_id": "a97fce5158e6e573",
                  "using": "gsi"
                }
              },
              {
                "#operator": "Filter",
                "condition": "((((`store`.`docType`) = \"Store\") and (((`order`.`docType`) = \"Order\") or ((`order`.`docType`) is missing))) and (((`order`.`clientId`) = \"9281ae36-a418-4ea3-93f0-bfd7b1a138248\") or (`order` is missing)))"
              },
              {
                "#operator": "InitialProject",
                "result_terms": [
                  {
                    "expr": "(`store`.`status`)"
                  }
                ]
              },
              {
                "#operator": "FinalProject"
              }
            ]
          }
        }
      ]
    },
    "text": "SELECT store.status\nFROM myBucket store\nLEFT OUTER JOIN myBucket order ON KEY (\"Store::\" || order.storeId) FOR store\nWHERE store.docType=\"Store\"\nAND (order.docType=\"Order\" OR order.docType IS MISSING)\nAND (order.clientId=\"9281ae36-a418-4ea3-93f0-bfd7b1a138248\" OR order IS MISSING)"
  }
]

РЕДАКТИРОВАТЬ2

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

Например Магазин 1 - у клиента X нет заказов Магазин 2 - у клиента X есть один заказ, и некоторая информация отображается в информации о магазине


person Manuel Reis    schedule 10.01.2017    source источник
comment
Попробуйте этот запрос: SELECT count(*) FROM myBucket WHERE clientId="9281ae36-a418-4ea3-93f0-bfd7b1a38248". Результат больше 1?   -  person quest4truth    schedule 11.01.2017
comment
@ quest4truth Без указания docType, да, его больше 1. В этом конкретном сценарии у клиента нет заказов, но, поскольку я выполняю LOJ, я ожидал, что в результате будут возвращены магазины.   -  person Manuel Reis    schedule 11.01.2017
comment
Предложение WHERE оценивается после LEFT OUTER JOIN, поэтому ваш результирующий набор все равно будет соответствующим образом сокращен. См. bennadel.com/blog/70-sql-query. -order-of-operations.htm для объяснения того, как выполняется инструкция SELECT.   -  person quest4truth    schedule 11.01.2017


Ответы (2)


Внешние соединения производят все документы левой стороны независимо от успешного соответствия предикату ключа соединения (а не любому условию в предложении where). Это означает, что вы получите 30 результатов независимо от того, есть ли у вас соответствующий order.storeId или нет.

В этом случае последний фильтр находится по идентификатору клиента, который применяется после JOIN и, следовательно, фильтрует некоторые документы. Проверьте / опубликуйте вывод EXPLAIN для проверки.

person Prasad Varakur    schedule 11.01.2017
comment
Я обновил свой ответ запросом объяснения. Даже если я фильтрую набор с 30 результатами, не должна ли часть OR order IS MISSING сохранять 30 результатов? Если нет, то как добиться ожидаемого результата? Спасибо - person Manuel Reis; 11.01.2017
comment
Какая у вас цель? Вы пытаетесь понять, как это работает, или реальные результаты имеют первостепенное значение? Если фактические результаты имеют первостепенное значение, опишите, чего вы ожидаете. - person quest4truth; 11.01.2017
comment
@ quest4truth Фактические результаты имеют первостепенное значение. Я ожидаю, что результаты всегда будут слева (магазины), а результаты будут справа, только если они существуют (Заказы). Поскольку в магазине есть несколько заказов, я пытался фильтровать по clientId. При фильтрации по clientId я все еще ожидаю получить 30 результатов в конце из-за того, что я сказал в моем предыдущем комментарии - person Manuel Reis; 11.01.2017
comment
Хорошо, но это противоречие. У вас не может быть 30 результатов, если вы отфильтруете эти результаты по клиенту, у которого нет 30 записей. LOJ происходит перед фильтром. Фильтр удаляет их после того, как произойдет LOJ. Вам просто нужны заказы от магазина к покупателю? Если покупателя нет, чего вы хотите? Для чего нужна фильтрация по клиенту, если вам нужно все, независимо от того, есть клиент или нет? Конечно, вы не хотите приписывать заказчику чужой заказ. - person quest4truth; 11.01.2017
comment
Имеет смысл в том, что вы говорите; мне нужно выполнить LOJ на двух разных полях (что я не думаю, что это возможно в couchbase). Клиент всегда есть - просто у него может еще не быть отношений с магазином. В этом сценарии я все еще хочу показать магазины. Говоря более конкретно, я хочу показать все магазины, и если пользователь имеет отношение к нему (заказы), то также покажу информацию о них (я обновлю вопрос этой информацией). Теперь в этом больше смысла? - person Manuel Reis; 11.01.2017
comment
Да, в этом есть смысл. Подскажите, смогу ли я задать вопрос - person quest4truth; 11.01.2017
comment
Вы можете присоединиться к нескольким полям, например: SELECT stuff FROM place LEFT OUTER JOIN 2ndPlace ON {key clause} LEFT OUTER JOIN 3rdPlace ON {nextkeyclause}. Я не буду пытаться публиковать ответ, потому что я не понимаю часть ON KEY ... - person quest4truth; 11.01.2017
comment
Привет, @ quest4truth. выполнение второго соединения с информацией о клиенте приведет к получению информации о пользователе, чего я не хочу. В couchbase соединения выполняются по ключу документа, отсюда и трудности, с которыми я до сих пор сталкиваюсь (вы не можете присоединиться к свойству документа). - person Manuel Reis; 11.01.2017

В настоящее время в N1QL предложение WHERE не считается частью предиката JOIN, поэтому вам необходимо сделать следующее. Вам нужно исключить порядок повсюду или использовать другой псевдоним.

SELECT store.status, order.userId, store.docId 
FROM myBucket store
LEFT OUTER JOIN myBucket order ON KEY ("Store::" || order.storeId) FOR store
WHERE store.docType="Store"
AND (
(order IS MISSING)
OR
 ((order.docType="Order" OR order.docType IS MISSING)
AND order.clientId="9281ae36-a418-4ea3-93f0-bfd7b1a38248")
person geraldss    schedule 12.01.2017