Есть ли разница между GROUP BY и DISTINCT?


310

Я узнал кое-что простое о SQL на днях:

SELECT c FROM myTbl GROUP BY C

Имеет тот же результат, что и:

SELECT DISTINCT C FROM myTbl

Что мне интересно, есть ли что-то другое в том, как механизм SQL обрабатывает команду, или это действительно одно и то же?

Я лично предпочитаю четкий синтаксис, но я уверен, что это больше по привычке, чем что-либо еще.

РЕДАКТИРОВАТЬ: Это не вопрос о агрегатах. Использование GROUP BYс агрегатными функциями понятно.


11
Это не вопрос агрегатов, это GROUP BY, функционирующий так же, как и отдельный, когда нет агрегатной функции
Brettski

2
Вы также можете сделать SELECT c FROM myTbl UNION SELECT c FROM myTblи получить тот же результат ... Но зачем все усложнять, когда SELECT DISTINCT так просто.
jarlh

«Логический порядок выполнения» GROUP BYнамного раньше, чем «SELECT» и DISTINCTследует за «select».
Used_By_Already

Одно очень незначительное отличие, о котором я не упомянул, состоит в том, что в DISTINCTрезультате фактически выбирается поле, т. Е. Значение будет отображаться в наборе результатов. GROUP BYможет эффективно удалять дубликаты без фактического выбора поля. В большинстве случаев это несколько неактуально, но может быть именно тем, что вы хотите в других. Если вы в конечном итоге используете GROUP BYвместо DISTINCT, пояснительный комментарий в коде, вероятно, оправдан.
Риного

Суть в том, что, поскольку удаление дубликатов происходит в разных точках плана выполнения, одно может быть более эффективным, чем другое, поскольку удаление дубликатов требует сортировки или, возможно, использования этого индекса над этим индексом. Таким образом, преимущество раннего удаления дубликатов может быть преимуществом или преимуществом раннего использования другого индекса и последующего употребления сортировки, когда осталось мало строк и сортировка незначительна.
Белавский

Ответы:


246

Ответ MusiGenesis является функционально правильным в отношении вашего вопроса, как указано; SQL Server достаточно умен, чтобы понять, что если вы используете «Группировать по» и не используете никаких агрегатных функций, то на самом деле вы имеете в виду «Отличительный» - и, следовательно, он генерирует план выполнения, как если бы вы просто использовали «Отличительный» «.

Тем не менее, я думаю, что важно отметить реакцию Хэнка , а также - кавалерийское обращение с «Group By» и «Distinct» может привести к пагубным ошибкам в будущем, если вы не будете осторожны. Не совсем правильно говорить, что это «не вопрос об агрегатах», потому что вы спрашиваете о функциональной разнице между двумя ключевыми словами SQL-запроса, одно из которых предназначено для использования с агрегатами, а другое - нет.

Иногда молоток может вбить винт, но если у вас под рукой есть отвертка, зачем?

(для целей этой аналогии Hammer : Screwdriver :: GroupBy : Distinctи screw => get list of unique values in a table column)


Я полностью согласен с тобой, Сколан. Я был весьма удивлен, когда наткнулся на эту функциональность. Это не то, что я планирую использовать, а то, как все было сделано в этом новом месте, где я работаю.
Бретцки

По крайней мере в Oracle 12, по-видимому, бывают случаи, когда DISTINCT, получение различных значений с помощью UNION и GROUP BY, работают по-разному. Сегодня у меня был случай, когда DISTINCT и UNION вызывают ошибку оракула, но GROUP BY работал; Я выбирал только 1 столбец из представления и не использовал агрегирование; Я все еще озадачен, почему это требовалось, но это подтверждает, что есть некоторая разница в исполнении. Как отмечают другие, он также позволяет вам столбцы GROUP BY, не входящие в select, хотя это редко требуется без агрегирования.
ZeroK

1
Когда дело доходит до SQL, у вас всегда есть и отвертка, и молоток. Зачем использовать молоток для ввинчивания винта?
jarlh

Просто чтобы прояснить вашу аналогию - ваш молоток == GroupBy и отвертка == в этом случае различаются?
HopeKing

Ух ты, у этого десятилетнего вопроса все еще есть ноги! «Отличительная» - это отвертка, если «список уникальных значений» - это винт. Я уточню ответ, чтобы сделать аналогию более понятной.
Сколан

136

GROUP BYпозволяет использовать агрегатные функции, как AVG, MAX, MIN, SUMи COUNT. С другой стороны DISTINCTпросто удаляет дубликаты.

Например, если у вас есть куча записей о покупках, и вы хотите знать, сколько было потрачено каждым отделом, вы можете сделать что-то вроде:

SELECT department, SUM(amount) FROM purchases GROUP BY department

Это даст вам одну строку для каждого отдела, содержащую название отдела и сумму всех amountзначений во всех строках для этого отдела.


2
Использование GROUP BY Я понимаю, вопрос основан на том факте, что он возвращает отдельный набор данных, когда отсутствует агрегатная функция.
Бретцки

2
Потому что GROUP BY неявно делает DISTINCT над значениями столбца, по которому вы группируете (извините за какофонию).
Джо Пинеда

Разве нельзя использовать DISTINCT+ агрегатные функции? вот так:select distinct department, SUM(amount) from ...
Шафизаде

@ Sajad, вы можете сделать это, да, но вы все равно должны иметь GROUP BY, поэтому DISTINCT ничего не делает для вас.
ZeroK

44

Разницы нет (в SQL Server, по крайней мере). Оба запроса используют один и тот же план выполнения.

http://sqlmag.com/database-performance-tuning/distinct-vs-group

Может быть , есть разница, если есть суб-запросы участия:

http://blog.sqlauthority.com/2007/03/29/sql-server-difference-between-distinct-and-group-by-distinct-vs-group-by/

Нет никакой разницы (в стиле Oracle):

http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:32961403234212


40

В чем отличие с точки зрения простого удаления дубликатов

Помимо того факта, что, в отличие от этого DISTINCT, GROUP BYдопускается агрегирование данных по группам (что было упомянуто во многих других ответах), самым важным отличием, на мой взгляд, является тот факт, что две операции «происходят» на двух очень разных этапах в логическом порядке. операций, которые выполняются в SELECTзаявлении .

Вот самые важные операции:

  • FROM( в том числе JOIN, APPLYи т.д.)
  • WHERE
  • GROUP BY (можно удалить дубликаты)
  • Скопления
  • HAVING
  • Оконные функции
  • SELECT
  • DISTINCT (можно удалить дубликаты)
  • UNION, INTERSECT, EXCEPT (Можно удалить дубликаты)
  • ORDER BY
  • OFFSET
  • LIMIT

Как видите, логический порядок каждой операции влияет на то, что можно сделать с ней, и как она влияет на последующие операции. В частности, тот факт , что GROUP BYоперация «происходит до» на SELECTоперации (проекция) означает , что:

  1. Это не зависит от прогноза (что может быть преимуществом)
  2. Он не может использовать какие-либо значения из проекции (что может быть недостатком)

1. Это не зависит от проекции

Пример, в котором полезно не зависеть от проекции, - это если вы хотите рассчитать оконные функции по различным значениям:

SELECT rating, row_number() OVER (ORDER BY rating) AS rn
FROM film
GROUP BY rating

При запуске с базой данных Sakila это дает:

rating   rn
-----------
G        1
NC-17    2
PG       3
PG-13    4
R        5

Этого не может быть достигнуто с DISTINCTлегкостью:

SELECT DISTINCT rating, row_number() OVER (ORDER BY rating) AS rn
FROM film

Этот запрос «неправильный» и дает что-то вроде:

rating   rn
------------
G        1
G        2
G        3
...
G        178
NC-17    179
NC-17    180
...

Это не то, что мы хотели. DISTINCTОперация «не происходит после того, как » проекция, поэтому мы больше не можем удалить DISTINCTрейтинги , потому что функция окна уже рассчитаны и прогнозируемые. Чтобы использовать DISTINCT, нам нужно было бы вложить эту часть запроса:

SELECT rating, row_number() OVER (ORDER BY rating) AS rn
FROM (
  SELECT DISTINCT rating FROM film
) f

Примечание: в данном конкретном случае мы могли бы также использоватьDENSE_RANK()

SELECT DISTINCT rating, dense_rank() OVER (ORDER BY rating) AS rn
FROM film

2. Он не может использовать какие-либо значения из проекции

Одним из недостатков SQL является его многословие. По той же причине, что мы видели раньше (а именно, логический порядок операций), мы не можем «легко» группировать то, что мы проецируем.

Это неверный SQL:

SELECT first_name || ' ' || last_name AS name
FROM customer
GROUP BY name

Это верно (повторяет выражение)

SELECT first_name || ' ' || last_name AS name
FROM customer
GROUP BY first_name || ' ' || last_name

Это также верно (вложенное выражение)

SELECT name
FROM (
  SELECT first_name || ' ' || last_name AS name
  FROM customer
) c
GROUP BY name

Я написал об этой теме более подробно в блоге


Я был искренне удивлен, увидев, что порядок исполнения не обсуждался сразу по этому вопросу. Спасибо, очень хорошо объяснил тоже. Что касается вашего пункта 2. некоторые (один?) Базы данных разрешают использовать псевдонимы выбора по всему запросу (я знаю, что это Teradata, но это исключение).
Used_By_Already

@Used_By_Already: Конечно, некоторые базы данных делают это. Многие базы данных позволяют использовать эти псевдонимы только в отдельных частях (например, нет, WHEREно возможно GROUP BY). В любом случае, я думаю, что это плохая идея, и я предлагаю никогда не использовать эту функцию в целях переносимости и обслуживания. «Внезапно» это больше не будет работать, например, при наложении псевдонимов на агрегатную функцию или оконную функцию.
Лукас Эдер

never using that feature for portability and maintenance reasons!! согласился на 100% ... и теперь я тоже получаю удовольствие от вашего блога, отличная работа. Приветствия.
Used_By_Already

32

Используйте, DISTINCTесли вы просто хотите удалить дубликаты. Используйте , GROUPY BYесли вы хотите применить агрегатные операторы ( MAX, SUM, GROUP_CONCAT..., или HAVINGоговорка).


19

Я ожидаю, что есть вероятность тонких различий в их исполнении. Я проверил планы выполнения для двух функционально эквивалентных запросов по этим направлениям в Oracle 10g:

core> select sta from zip group by sta;

---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |    58 |   174 |    44  (19)| 00:00:01 |
|   1 |  HASH GROUP BY     |      |    58 |   174 |    44  (19)| 00:00:01 |
|   2 |   TABLE ACCESS FULL| ZIP  | 42303 |   123K|    38   (6)| 00:00:01 |
---------------------------------------------------------------------------

core> select distinct sta from zip;

---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |    58 |   174 |    44  (19)| 00:00:01 |
|   1 |  HASH UNIQUE       |      |    58 |   174 |    44  (19)| 00:00:01 |
|   2 |   TABLE ACCESS FULL| ZIP  | 42303 |   123K|    38   (6)| 00:00:01 |
---------------------------------------------------------------------------

Средняя операция немного отличается: «HASH GROUP BY» и «HASH UNIQUE», но предполагаемые затраты и т. Д. Идентичны. Затем я выполнил их с включенной трассировкой, и фактическое количество операций было одинаковым для обоих (за исключением того, что второй не должен был выполнять какие-либо физические чтения из-за кэширования).

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

Я думаю, что вы должны предпочесть синтаксис DISTINCT для этой цели. Это не просто привычка, это более четко указывает на цель запроса.


14

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

Например, это не то же самое, что:

SELECT C FROM myTbl GROUP BY C, D

14

Я прочитал все приведенные выше комментарии, но не увидел, чтобы кто-то указывал на основное различие между Group By и Distinct, кроме бита агрегации.

Distinct возвращает все строки, а затем дедуплицирует их, тогда как Group By дедуплицирует строки, когда они читаются алгоритмом одна за другой.

Это означает, что они могут давать разные результаты!

Например, приведенные ниже коды дают разные результаты:

SELECT distinct ROW_NUMBER() OVER (ORDER BY Name), Name FROM NamesTable

 SELECT ROW_NUMBER() OVER (ORDER BY Name), Name FROM NamesTable
GROUP BY Name

Если в таблице 10 имен, из которых 1 является дубликатом другого, то первый запрос возвращает 10 строк, тогда как второй запрос возвращает 9 строк.

Причина в том, что я сказал выше, чтобы они могли вести себя по-другому!


11
Это связано с тем, что во Nameвтором запросе вы группируете только одно, а distinctключевое слово применяется как к столбцам, так Nameи к вашему ROW_NUMBER()столбцу в selectпредложении первого запроса. Если бы вы также сгруппировали по первому столбцу во втором запросе, запросы вернули бы те же результаты.

Это исход из order of executionиз пунктов SQL , который представляет собой (в общем смысле) FROM and ON (joins), WHERE, GROUP BY, HAVING, SELECT, DISTINCT, ORDER BY, LIMIT / OFFSET / TOPпоэтому второй запрос имена уменьшаются в количестве по группе и позже row_number () применяется в результате в одном ряду за уникальное имя. В первом запросе row_number () применяется до того, как применяется отличное, и из-за природы функции row_number () каждая строка получает уникальное целое число, таким образом, каждая строка возвращается, даже если есть повторяющиеся значения имени.
Used_By_Already

12

Если вы используете DISTINCT с несколькими столбцами, результирующий набор не будет сгруппирован, как это будет с GROUP BY, и вы не сможете использовать агрегатные функции с DISTINCT.


11

Они имеют различную семантику, даже если у них есть эквивалентные результаты для ваших конкретных данных.


6

GROUP BY имеет очень специфическое значение, отличное (хе) от функции DISTINCT.

GROUP BY приводит к тому, что результаты запроса группируются с использованием выбранного выражения, затем могут применяться агрегатные функции, которые будут действовать на каждую группу, а не на весь набор результатов.

Вот пример, который может помочь:

Учитывая таблицу, которая выглядит так:

name
------
barry
dave
bill
dave
dave
barry
john

Этот запрос:

SELECT name, count(*) AS count FROM table GROUP BY name;

Будет производить вывод, как это:

name    count
-------------
barry   2
dave    3
bill    1
john    1

Что, очевидно, сильно отличается от использования DISTINCT. Если вы хотите сгруппировать свои результаты, используйте GROUP BY, если вы просто хотите получить уникальный список определенного столбца, используйте DISTINCT. Это даст вашей базе данных возможность оптимизировать запрос для ваших нужд.


6

Пожалуйста, не используйте GROUP BY, когда вы имеете в виду DISTINCT, даже если они работают одинаково. Я предполагаю, что вы пытаетесь сэкономить миллисекунды от запросов, и я должен отметить, что время разработки на порядок дороже, чем время компьютера.


5

Если вы используете GROUP BY без какой-либо агрегатной функции, то внутренне она будет обрабатываться как DISTINCT, поэтому в этом случае нет разницы между GROUP BY и DISTINCT.

Но когда вам предоставляется предложение DISTINCT, лучше использовать его для поиска ваших уникальных записей, поскольку целью GROUP BY является достижение агрегации.


4

group by используется в агрегатных операциях - например, когда вы хотите получить количество Bs с разбивкой по столбцу C

select C, count(B) from myTbl group by C

это звучит по-разному - вы получаете уникальные строки.

В SQL Server 2005 похоже, что оптимизатор запросов может оптимизировать разницу в упрощенных примерах, которые я запускал. Не знаю, если вы можете рассчитывать на это во всех ситуациях, хотя.


3

В этом конкретном запросе нет никакой разницы. Но, конечно, если вы добавите какие-либо статистические столбцы, вам придется использовать группирование по.


3

В перспективе Teradata :

С точки зрения набора результатов не имеет значения, используете ли вы DISTINCT или GROUP BY в Teradata. Набор ответов будет таким же.

С точки зрения производительности, это не то же самое.

Чтобы понять, что влияет на производительность, вам необходимо знать, что происходит с Teradata при выполнении оператора с помощью DISTINCT или GROUP BY.

В случае DISTINCT строки перераспределяются немедленно без какой-либо предварительной агрегации, в то время как в случае GROUP BY на первом этапе выполняется предварительная агрегация, и только после этого уникальные значения перераспределяются по AMP.

Не думайте, что GROUP BY всегда лучше с точки зрения производительности. Если у вас много разных значений, шаг предварительной агрегации GROUP BY не очень эффективен. Teradata должна отсортировать данные, чтобы удалить дубликаты. В этом случае может быть лучше сначала перераспределить, то есть использовать инструкцию DISTINCT. Только если имеется много повторяющихся значений, оператор GROUP BY, вероятно, является лучшим выбором, поскольку только после выполнения шага дедупликации после перераспределения.

Короче говоря, DISTINCT против GROUP BY в Teradata означает:

GROUP BY -> для многих дубликатов DISTINCT -> нет или только несколько дубликатов. Время от времени при использовании DISTINCT вам не хватает места в буфере на AMP. Причина в том, что перераспределение происходит немедленно, а перекос может привести к тому, что AMP не хватит места.

Если это произойдет, у вас, вероятно, больше шансов с GROUP BY, так как дубликаты уже удалены на первом шаге и меньше данных перемещается по AMP.


Что такое Teradata?
Бретцки

Teradata - это система управления реляционными базами данных (RDBMS), способная поддерживать множество одновременно работающих пользователей с различных клиентских платформ. Teradata совместима со стандартом ANSI и полностью основана на параллельной архитектуре.
Рам Гадиярам

2

С точки зрения «языка SQL» эти две конструкции эквивалентны, и то, что вы выбираете, является одним из тех вариантов «образа жизни», которые мы все должны сделать. Я думаю, что есть хороший пример того, что DISTINCT является более явным (и, следовательно, более внимательным к человеку, который унаследует ваш код и т. Д.), Но это не означает, что конструкция GROUP BY является недопустимым выбором.

Я думаю, что «GROUP BY для агрегатов» - неправильный акцент. Люди должны знать, что функция set (MAX, MIN, COUNT и т. Д.) Может быть опущена, чтобы они могли понять намерения кодера, когда оно есть.

Идеальный оптимизатор распознает эквивалентные конструкции SQL и всегда соответственно выберет идеальный план. Для выбора реального движка SQL вы должны протестировать :)

PS обратите внимание, что положение ключевого слова DISTINCT в предложении select может давать разные результаты, например, контраст:

SELECT COUNT(DISTINCT C) FROM myTbl;

SELECT DISTINCT COUNT(C) FROM myTbl;

1

Вы замечаете это только потому, что выбираете один столбец.

Попробуйте выбрать два поля и посмотрите, что произойдет.

Group By предназначена для использования следующим образом:

SELECT name, SUM(transaction) FROM myTbl GROUP BY name

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


Это не вопрос совокупностей. В вашем примере, ВЫБЕРИТЕ c, d ОТ mytbl GROUP BY C, D; фактически вернет тот же набор данных, что и SELECT DISTINCT C, D FROM mytbl; Это
основа

1

Я знаю, что это старый пост. Но бывает, что у меня был запрос, который использовал group by, чтобы просто возвращать различные значения при использовании этого запроса в отчетах toad и oracle, все работало нормально, я имею в виду хорошее время отклика. Когда мы перешли с Oracle 9i на 11g, время отклика в Toad было превосходным, но в отчете на завершение отчета ушло около 35 минут, а при использовании предыдущей версии - около 5 минут.

Решением было изменить группу и использовать DISTINCT, и теперь отчет выполняется примерно за 30 секунд.

Я надеюсь, что это полезно для кого-то с такой же ситуацией.


1

С точки зрения использования, GROUP BY используется для группировки строк, которые вы хотите вычислить. DISTINCT не будет делать никаких расчетов. Это не покажет повторяющихся строк.

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

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


0

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

то есть:

select distinct a, b, c from table;

такой же как:

select a, b, c from table group by a, b, c

Согласовано, но будет ли это так же, как выберите c, b, a из группы таблиц a, b, c
Dheer

Да, было бы то же самое
Caius Jard

0

Функциональная эффективность совершенно иная. Если вы хотите выбрать только «возвращаемое значение», за исключением дубликата, лучше использовать отличный, чем группировать по. Поскольку «группировать по» включают (сортировка + удаление), «отличные» включают (удаление)



0

Иногда они могут дать вам одинаковые результаты, но они предназначены для использования в другом смысле / случае. Основное отличие заключается в синтаксисе.

Обратите внимание на пример ниже. DISTINCTиспользуется для фильтрации дублированного набора значений. (6, cs, 9.1) и (1, cs, 5.5) - два разных набора. Так DISTINCTчто будет отображать обе строки, в то время GROUP BY Branchкак будет отображать только один набор.

 SELECT * FROM student; 
+------+--------+------+
| Id   | Branch | CGPA |
+------+--------+------+
|    3 | civil  |  7.2 |
|    2 | mech   |  6.3 |
|    6 | cs     |  9.1 |
|    4 | eee    |  8.2 |
|    1 | cs     |  5.5 |
+------+--------+------+
5 rows in set (0.001 sec)

SELECT DISTINCT * FROM student; 
+------+--------+------+
| Id   | Branch | CGPA |
+------+--------+------+
|    3 | civil  |  7.2 |
|    2 | mech   |  6.3 |
|    6 | cs     |  9.1 |
|    4 | eee    |  8.2 |
|    1 | cs     |  5.5 |
+------+--------+------+
5 rows in set (0.001 sec)

SELECT * FROM student GROUP BY Branch;
+------+--------+------+
| Id   | Branch | CGPA |
+------+--------+------+
|    3 | civil  |  7.2 |
|    6 | cs     |  9.1 |
|    4 | eee    |  8.2 |
|    2 | mech   |  6.3 |
+------+--------+------+
4 rows in set (0.001 sec)

Иногда результаты, которые могут быть достигнуты с помощью GROUP BYпредложения, не могут быть достигнуты DISTINCTбез использования какого-либо дополнительного условия или условия. Например, в вышеуказанном случае.

Чтобы получить такой же результат, как DISTINCTвы должны передать все имена столбцов в GROUP BYпредложении, как показано ниже. Итак, посмотрите на синтаксическую разницу. Вы должны знать все имена столбцов, чтобы использовать GROUP BYпредложение в этом случае.

SELECT * FROM student GROUP BY Id, Branch, CGPA;
+------+--------+------+
| Id   | Branch | CGPA |
+------+--------+------+
|    1 | cs     |  5.5 |
|    2 | mech   |  6.3 |
|    3 | civil  |  7.2 |
|    4 | eee    |  8.2 |
|    6 | cs     |  9.1 |
+------+--------+------+

Также я заметил, GROUP BYотображает результаты в порядке возрастания по умолчанию, который DISTINCTне делает. Но я не уверен в этом. Это может отличаться от поставщика.

Источник: https://dbjpanda.me/dbms/languages/sql/sql-syntax-with-examples#group-by


0

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

В случае «GROUP BY» мы можем применить агрегацию функции , такие как AVG, MAX, MIN, SUMи COUNTна определенном столбце и принести имя столбца и агрегация функции результат на той же колонке.

Пример :

select  specialColumn,sum(specialColumn) from yourTableName group by specialColumn;

-1

Нет значительного различия между выражением group by и выражением отдельно, за исключением использования агрегатных функций. И то, и другое можно использовать для различения значений, но если с точки зрения производительности, группировать лучше. Когда используется отдельное ключевое слово, внутренне используется операция сортировки, которую можно просмотреть в плане выполнения.

Попробуйте простой пример

Объявить таблицу @tmpresult (Id tinyint)

Вставить в @tmpresult Выбрать 5 Объединить все Выбрать 2 Объединить все Выбрать 3 Объединить все Выбрать 4

Выберите отличный идентификатор из @tmpresult


отличные и сгруппированные по обоим воле
vignesh
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.