Выборка с подзапросами
В Reindexer поддерживается выборка данных с подзапросами (вложенными запросами), когда SELECT-запросы используются как часть WHERE-условий. В подзапросе может происходить выборка из того же неймспейса, что и в основном запросе, либо из другого.
Результатом подзапроса может быть:
-
Определенное поле, на которое указывает
SELECT
. Для этого требуется установить фильтр по одному полю.
Ограничения по использованию подзапросов
В текущей реализации имеются следующие ограничения по использованию подзапросов:
- Подзапрос не может содержать других подзапросов.
- Отсутствие поддержки операторов
JOIN
иMERGE
в подзапросах. - Подзапрос не может возвращать кортежи из нескольких полей и/или результатов агрегаций.
- Подзапросы в reindexer не могут быть использованы внутри UPDATE и DELETE-запросов.
Примеры использования подзапросов
Ниже приведены типовые примеры выборки данных с использованием подзапросов.
Выборка данных без фильтрации в основном запросе
При выполнении представленного ниже запроса составится список всех id
из неймспейса second_ns
, у которых age >= 18
.
Если хотя бы один из этих id >= 100
, то условие истинно: в результат попадут все записи из main_ns
.
Если по результатам выполнения подзапроса не обнаружится ни одного id >= 100
, условие ложно: запрос не вернет записей.
query := db.Query("main_ns").
WhereQuery(db.Query("second_ns").Select("id").Where("age", reindexer.GE, 18), reindexer.GE, 100)
SELECT * FROM main_ns WHERE (SELECT id FROM second_ns WHERE age >= 18) >= 100
curl --location 'http://127.0.0.1:9088/api/v1/db/new_db/query' \
--header 'Content-Type: application/json' \
--data '{
"namespace": "main_ns",
"type": "select",
"filters": [
{
"op": "and",
"cond": "ge",
"value": 100,
"subquery": {
"namespace": "second_ns",
"select_filter": [
"id"
],
"filters": [
{
"op": "and",
"cond": "ge",
"field": "age",
"value": 18
}
]
}
}
]
}'
Выборка данных с фильтрацией в основном запросе
Подобные запросы на выборку данных с фильтрацией в основном запросе работают похожим на inner join
образом.
Но в отличие от join
здесь всегда будет происходить полная предварительная выборка данных из неймспейса, указанного в подзапросе, тогда как в случае с join
используется эвристическая логика для предварительной выборки.
query := db.Query("main_ns").
Where("id", reindexer.EQ, db.Query("second_ns").Select("id").Where("age", reindexer.LE, 18))
SELECT * FROM main_ns WHERE id = (SELECT id FROM second_ns WHERE age <= 18)
curl --location 'http://127.0.0.1:9098/api/v1/db/new_db/query' \
--header 'Content-Type: application/json' \
--data '{
"namespace": "main_ns",
"type": "select",
"filters": [
{
"op": "and",
"cond": "eq",
"field": "id",
"subquery": {
"namespace": "second_ns",
"select_filter": [
"id"
],
"filters": [
{
"op": "and",
"cond": "le",
"field": "age",
"value": 18
}
]
}
}
]
}'
Результат выборки по такому запросу полностью эквивалентен результату выборки по запросу с фильтрующим inner join
:
SELECT * FROM main_ns INNER JOIN (SELECT * FROM second_ns WHERE age <= 18 LIMIT 0) ON main_ns.id = second_ns.id
Выборка данных с агрегацией в подзапросе
Reindexer поддерживает агрегации в подзапросе.
Допускается использование не более одной агрегации.
Поддерживаемые агрегации: Min
, Max
, Avg
, Sum
, Count
, CountCached
(подробнее о них — в разделе «Агрегатные функции»).
Выборка с использованием агрегатной функции AggregateMax
query := db.Query("main_ns").
WhereQuery(db.Query("second_ns").Where("age", reindexer.GE, 18).AggregateMax("age"), reindexer.GE, 20)
Выборка с использованием агрегатной функции ReqTotal
query := db.Query("main_ns").
Where("id", reindexer.GT, db.Query("second_ns").Where("age", reindexer.LE, 18).ReqTotal())
Выборка с использованием агрегатной функции MAX()
:
SELECT * FROM main_ns WHERE (SELECT MAX (age) FROM second_ns WHERE age >= 18) >= 20
Выборка с использованием агрегатной функции COUNT()
:
SELECT * FROM main_ns WHERE id > (SELECT COUNT(*) FROM second_ns WHERE age <= 18)
Выборка с использованием агрегатной функции MAX()
:
curl --location 'http://127.0.0.1:9088/api/v1/db/new_db/query' \
--header 'Content-Type: application/json' \
--data '{
"namespace": "main_ns",
"type": "select",
"filters": [
{
"op": "and",
"cond": "ge",
"value": 20,
"subquery": {
"namespace": "second_ns",
"aggregations": [
{
"fields": [
"age"
],
"type": "MAX"
}
],
"filters": [
{
"op": "and",
"cond": "ge",
"field": "age",
"value": 18
}
]
}
}
]
}'
Выборка с использованием агрегатной функции COUNT()
:
curl --location 'http://127.0.0.1:9098/api/v1/db/new_db/query' \
--header 'Content-Type: application/json' \
--data '{
"namespace": "main_ns",
"type": "select",
"filters": [
{
"op": "and",
"cond": "gt",
"field": "id",
"subquery": {
"namespace": "second_ns",
"aggregations": [
{
"fields": [],
"type": "COUNT"
}
],
"filters": [
{
"op": "and",
"cond": "ge",
"field": "age",
"value": 18
}
]
}
}
]
}'
Выборка данных с проверкой условия
Если необходимо проверить, удовлетворяет ли хотя бы один из элементов основного запроса результату подзапроса, используйте выборку данных с условиями ANY
или EMPTY
.
query := db.Query("main_ns").
WhereQuery(db.Query("second_ns").Where("age", reindexer.LE, 18), reindexer.EMPTY, nil)
SELECT * FROM main_ns WHERE (SELECT * FROM second_ns WHERE age <= 18) IS EMPTY
curl --location 'http://127.0.0.1:9088/api/v1/db/new_db/query' \
--header 'Content-Type: application/json' \
--data '{
"namespace": "main_ns",
"type": "select",
"filters": [
{
"op": "and",
"cond": "empty",
"subquery": {
"namespace": "second_ns",
"filters": [
{
"op": "and",
"cond": "le",
"field": "age",
"value": 18
}
]
}
}
]
}'
Выборка данных из одного и того же неймспейса в основном запросе и подзапросе
query := db.Query("second_ns").
Where("age", reindexer.GT, db.Query("second_ns").Where("age", reindexer.GT, 18).AggregateAvg("age"))
SELECT * FROM second_ns WHERE age > (SELECT AVG(age) FROM second_ns WHERE age > 18)
curl --location 'http://127.0.0.1:9098/api/v1/db/new_db/query' \
--header 'Content-Type: application/json' \
--data '{
"namespace": "second_ns",
"type": "select",
"filters": [
{
"op": "and",
"cond": "gt",
"field": "age",
"subquery": {
"namespace": "second_ns",
"aggregations": [
{
"fields": [
"age"
],
"type": "AVG"
}
],
"filters": [
{
"op": "and",
"cond": "gt",
"field": "age",
"value": 18
}
]
}
}
]
}'