aboutsummaryrefslogtreecommitdiffstats
path: root/yql/essentials/docs/ru/syntax/subquery.md
blob: 735bf3521992e43423208a812b193376242794f6 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
# Шаблоны подзапросов (subquery)

## DEFINE SUBQUERY {#define-subquery}

`DEFINE SUBQUERY` позволяет объявить шаблон подзапроса (subquery), который представляет собой параметризуемый блок из нескольких выражений верхнего уровня (statements), и затем многократно его использовать путем применения в секции `FROM` выражения [SELECT](select/index.md) или входных данных в [PROCESS](process.md)/[REDUCE](reduce.md) с указанием параметров.
В отличие от [действий](action.md) шаблон подзапроса должен заканчиваться выражением `SELECT`/`PROCESS`/`REDUCE`, чей результат и является возвращаемым значением подзапроса. При этом выражение верхнего уровня `SELECT`/`PROCESS`/`REDUCE` нельзя использовать более одного раза, как и модифицирующие выражения (например, `INSERT`).

 После `DEFINE SUBQUERY` указывается:

1. [Именованное выражение](expressions.md#named-nodes), по которому объявляемый шаблон будет доступен далее в запросе;
2. В круглых скобках список именованных выражений, по которым внутри шаблона подзапроса можно обращаться к параметрам;
3. Ключевое слово `AS`;
4. Сам список выражений верхнего уровня;
5. `END DEFINE` выступает в роли маркера, обозначающего последнее выражение внутри шаблона подзапроса.

Один или более последних параметров subquery могут быть помечены вопросиком как необязательные — если они не были указаны при вызове subquery, то им будет присвоено значение `NULL`.

{% note info %}

В больших запросах объявление шаблонов подзапросов можно выносить в отдельные файлы и подключать их в основной запрос с помощью [EXPORT](export_import.md#export) + [IMPORT](export_import.md#import), чтобы вместо одного длинного текста получилось несколько логических частей, в которых проще ориентироваться. Важный нюанс: директива `USE my_cluster;` в импортирующем запросе не влияет на поведение объявленных в других файлах подзапросов.

{% endnote %}

Даже если список параметров в определении шаблона подзапроса пустой при использовании его во `FROM` секции нужно указать скобки `()`. Такое может быть удобно использовать, чтобы ограничить область видимости именованных выражений, используемых только в одном подзапросе.

В некоторых случаях вместо операции `DEFINE SUBQUERY` удобнее использовать эквивалентную форму в виде [лямбда функции](expressions.md#lambda).
В этом случае лямбда функция должна принимать первым аргументом специальный объект `world`, через который передаются зависимости о том, какие видны PRAGMA или COMMIT в точке использования шаблона запроса. Также этот объект нужно передавать первым аргументом вместе с остальными аргументами при их наличии другим шаблонам запросов, если они используются в лямбда функции.
Возвращаемым значением лямбда функции должно быть значение с типом список структур (выходная таблица) или список вариантов над кортежом из структур (несколько выходных таблиц). В последнем случае в точке использования шаблона запроса обычно используется распаковка вида

```yql
$out1, $out2 = PROCESS $mySubquery($myParam1, $myParam2);
-- используем далее $out1 и $out2 как отдельные таблицы.
```

#### Примеры

```yql
DEFINE SUBQUERY $hello_world($name, $suffix?) AS
    $name = $name ?? ($suffix ?? "world");
    SELECT "Hello, " || $name || "!";
END DEFINE;

SELECT * FROM $hello_world(NULL); -- Hello, world!
SELECT * FROM $hello_world("John"); -- Hello, John!
SELECT * FROM $hello_world(NULL, "Earth"); -- Hello, Earth!
```

```yql
DEFINE SUBQUERY $dup($x) AS
   SELECT * FROM $x(1)  -- применяем переданный шаблон запроса с одним аргументом
   UNION ALL
   SELECT * FROM $x(2); -- ... и с другим аргументом
END DEFINE;

DEFINE SUBQUERY $sub($n) AS
   SELECT $n * 10;
END DEFINE;

SELECT * FROM $dup($sub); -- передаем шаблон запроса $sub как параметр
-- Результат:
-- 10
-- 20
```

```yql
/* Уберем используемые именованные выражения $a и $b в отдельную область видимости */
DEFINE SUBQUERY $clean() AS
   $a = 10;
   $b = $a * $a;
   SELECT $a AS a, $b AS b;
END DEFINE;

SELECT * FROM $clean(); -- a: 10, b: 100
```

```yql
USE my_cluster;

DEFINE SUBQUERY $input() as
    SELECT * FROM `home/yql/tutorial/users`;
END DEFINE;

DEFINE SUBQUERY $myProcess1($nestedQuery, $lambda) AS
    PROCESS $nestedQuery() -- использование скобок () тут обязательно
    USING $lambda(TableRow());
END DEFINE;

$myProcess2 = ($world, $nestedQuery, $lambda) -> {
    -- Если использовать ListFlatMap или YQL::OrderedFlatMap, то получится Ordered YT Map операция
    return YQL::FlatMap($nestedQuery($world), $lambda);
};

-- При таком использовании реализации $myProcess1 и $myProcess2 идентичны
SELECT * FROM $myProcess1($input, ($x) -> { RETURN AsList($x, $x) });
SELECT * FROM $myProcess2($input, ($x) -> { RETURN AsList($x, $x) });
```

```yql
USE my_cluster;

DEFINE SUBQUERY $runPartition($table) AS
    $paritionByAge = ($row) -> {
        $recordType = TypeOf($row);
        $varType = VariantType(TupleType($recordType, $recordType));
        RETURN If($row.age % 2 == 0,
            Variant($row, "0", $varType),
            Variant($row, "1", $varType),
        );
    };

    PROCESS $table USING $paritionByAge(TableRow());
END DEFINE;

-- Распаковка двух результатов
$i, $j = (PROCESS $runPartition("home/yql/tutorial/users"));

SELECT * FROM $i;

SELECT * FROM $j;
```

## Объединение шаблонов подзапросов SubqueryExtend, SubqueryUnionAll, SubqueryMerge, SubqueryUnionMerge {#subquery-extend} {#subquery-unionall} {#subquery-merge} {#subquery-unionmerge}

Эти функции объединяют результаты одного и более шаблонов подзапросов, переданных аргументами. Требуется совпадение количества параметров в этих шаблонах подзапросов.

* `SubqueryExtend` требует совпадение схем подзапросов;
* `SubqueryUnionAll` работает по тем же правилам, что и [ListUnionAll](../builtins/list.md#ListUnionAll);
* `SubqueryMerge` использует те же ограничения, что и `SubqueryExtend`, а также выдает сортированный результат в случае если все подзапросы одинаково отсортированны;
* `SubqueryUnionMerge` использует те же ограничения, что и `SubqueryUnionAll`, а также выдает сортированный результат в случае если все подзапросы одинаково отсортированны.

#### Примеры

```yql
DEFINE SUBQUERY $sub1() as
    SELECT 1 as x;
END DEFINE;

DEFINE SUBQUERY $sub2() as
    SELECT 2 as x;
END DEFINE;

$s = SubqueryExtend($sub1,$sub2);
PROCESS $s();
```

## Объединение шаблонов подзапросов после подстановки элемента списка SubqueryExtendFor, SubqueryUnionAllFor, SubqueryMergeFor, SubqueryUnionMergeFor {#subquery-extend-for} {#subquery-unionall-for} {#subquery-merge-for} {#subquery-unionmerge-for}

Эти функции принимают аргументы:

* Непустой список значений;
* Шаблон подзапроса, в котором должен быть ровно один параметр.

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

* `SubqueryExtendFor` требует совпадение схем подзапросов;
* `SubqueryUnionAllFor` работает по тем же правилам, что и [ListUnionAll](../builtins/list.md#ListUnionAll);
* `SubqueryMergeFor` использует те же ограничения, что и `SubqueryExtendFor`, а также выдает сортированный результат в случае если все подзапросы одинаково отсортированны;
* `SubqueryUnionMergeFor` использует те же ограничения, что и `SubqueryUnionAllFor`, а также выдает сортированный результат в случае если все подзапросы одинаково отсортированны.

#### Примеры

```yql
DEFINE SUBQUERY $sub($i) as
    SELECT $i as x;
END DEFINE;

$s = SubqueryExtendFor([1,2,3],$sub);
PROCESS $s();
```

## Добавление сортировки в шаблон подзапроса SubqueryOrderBy или указание о наличии таковой SubqueryAssumeOrderBy

Эти функции принимают аргументы:

* Шаблон подзапроса без параметров;
* Список пар: строка - имя колонки, булево значение - true для сортировки по возрастанию или false для сортировки по убыванию.

И выполняют построение нового шаблона запроса без параметров, в котором выполняется сортировка или добавляется указание о наличии сортировки к результату. Для использования полученного шаблона запроса необходимо использовать функцию `PROCESS`, так как при использовании `SELECT` сортировка будет проигнорирована.

#### Примеры

```yql
DEFINE SUBQUERY $sub() as
   SELECT * FROM (VALUES (1,'c'), (1,'a'), (3,'b')) AS a(x,y);
end define;

$sub2 = SubqueryOrderBy($sub, [('x',false), ('y',true)]);

PROCESS $sub2();
```