Конфигурация УПП. Нужно выбрать 4 последних заказа покупателей из регистра "продажи". Как это сделать оптимально одним запросом?
---
```1c
    ВЫБРАТЬ
    ЗаказПокупателя.Ссылка,
    ЗаказПокупателя.Дата,
    ЗаказПокупателя.Контрагент
    ПОМЕСТИТЬ т
    ИЗ
    Документ.ЗаказПокупателя КАК ЗаказПокупателя
    ;

    ////////////////////////////////////////////////////////////­////////////////////
    ВЫБРАТЬ
    т1.Ссылка,
    т1.Дата,
    т1.Контрагент КАК Контрагент,
    КОЛИЧЕСТВО(РАЗЛИЧНЫЕ т2.Ссылка) КАК Ссылка1
    ИЗ
    т КАК т1
    левое СОЕДИНЕНИЕ т КАК т2
    ПО т1.Контрагент = т2.Контрагент
    И т1.Дата < т2.Дата

    СГРУППИРОВАТЬ ПО
    т1.Ссылка,
    т1.Дата,
    т1.Контрагент

    имеющие КОЛИЧЕСТВО(РАЗЛИЧНЫЕ т2.Ссылка) <4

    УПОРЯДОЧИТЬ ПО
    Контрагент,
    Ссылка1
```
---
это нужно в чистом запросе, или в СКД? В СКД есть специальные настройки для этого.
---
В запросе надо. Но если знаешь в СКД такой вариант, то подскажи. Буду благодарен.
---
в СКД все просто. Для нужной группировки указать обратную сортировку и в настройках указать максимальное кол-во записей.
---
Упорядочить по убыванию даты, выбрать первые 4.
---
Гениально. А если мне надо не по одному контрагенту а по всем у которых были продажи?
---
По каждому контрагенту последние 4 записи?
---
Да
---
а вот так

```1c
    Запрос.Текст =
    "ВЫБРАТЬ
    Продажи.Контрагент КАК Контрагент,
    Продажи.ЗаказПокупателя
    Продажи.ЗаказПокупателя.Дата КАК Дата
    ИЗ
    РегистрНакопления.Продажи.Обороты(...)
    УПОРЯДОЧИТЬ ПО
    Контрагент,
    Дата Убыв
    ";
    Выборка = Запрос.Выполнить().Выбрать();
    Пока Выборка.СледующийПоЗначениюПоля("Контрагент") Цикл
    Инд = 1;
    //Получаем первый заказ в выборке
    ...
    Пока Выборка.Следующий() И Инд < 5 Цикл
    //Получаем следующие заказы (3) контрагента в выборке
    Инд = Инд + 1;
    КонецЦикла;
    КонецЦикла;
```

и фсё ))
---
Плохой вариант. База теробайтная. Заказов - туева хуча. И ты предлагаешь ВСЕ заказы за ВСЁ время выбирать а потом обрабатывать? Так не пишется грамотный код.
---
Почему все , а параметры в вирт таблице обороты
---
И не забывайте что витр. таблица "обороты" работает быстрей чем выборка по всему регистру
---
это с чего бы? Если выборка без ограничения по дате... в виртуальной таблице будет выборка из всей физической таблице, плюс рассчитанные данные.
---
с того-бы, что вирт. таблицы имеют свои записи (на то они и нужны), а не переборка всех записей как вам представляется, даже если не указаны параметры! надо почитать мат часть об устройстве таблиц в 1С...
---
новое слово в структуре хранения данных 1С. Жуть.
Один только вопрос, а нафига она называется "виртуальная"?
---
отвечаю, потому-что выбирает не всю таблицу, как она есть, а только часть записей (итоговые называются)! Ребята я даже спорить не буду ))
---
да, вижу, что каша в голове. Оборотные виртуальные таблицы не используют итоговые таблицы. Их даже нет для оборотных регистров, от слова "совсем". Они нужны только для вирт. "остатки". И то там не то что думаете.
 Даже не представляете себе структуру хранения и выборки данных.
---
Итоги для оборотных регистров - есть. И всегда были, включая 7.7
---
ок. Они как бы есть, но не итоговые таблицы, а обороты за месяц.
---
Которые хранятся в таблице итогов :)
---
таблица общих оборотов за месяц. Пусть это будет называться итоговой таблицей, но это другая таблица, отличная от таблицы итогов "остатки". Вот для нее она точно передает назначение - итоги.
Вот с этим: "потому-что выбирает не всю таблицу, как она есть, а только часть записей (итоговые называются)!" тоже согласишься?

ps. в классификации 1С эта таблица называется "таблица оборотов".
---
Да ну. Всегда это называлось таблицами движений и таблицами итогов (итоговых записей). "Таблицей оборотов" можно назвать и таблицу движений - там же тоже обороты. Что смысл итогов отличается для остатков и оборотов - с этим никто не спорит. Короче, замяли тему.
---
Как скажешь, наверное тебе видней
З.Ы. а за одно попробуй тогда сделать обороты через условие "ГДЕ" и через вирт. таблицу "Обороты", будет разница или нет...
---
это вообще тут каким боком?
---
и хамить не обязательно
---
Ну или напиши свой "грамотный" код и не задавай вопросов (или хотя-бы в заголовке укажи что база охренеть какая)
---
в скобках укажи период, который интересует. и все. Делов то
---
ну да, перебором результата то быстрее получится чем запросом
---
ну во первых я этого не говорил, во вторых это не простой перебор а "шаговый", а в третьих "ПродажиДокумент В (Выбрать первые 4 Прод.Документ из РегистрНакопления.Продажи как Прод Где Прод.Контрагент = ВТ.Контрагент УПОРЯДОЧИТЬ ПО Прод.Документ.Дата УБЫВ) " здесь два не оптимальных случая конструкция "В" и вложенный запрос! Так что "шаговый" перебор сработает намного быстрей
---
Какие-то выводы можно делать только анализируя конкретные планы выполнения. Далеко не всегда можно глядя на запрос понять, насколько оптимально или неоптимально он будет выполняться. Даже банальные советы типа "не использовать вложенные запросы" - это всего лишь способ гарантировать стабильность результата и застраховаться от "выбрыков" оптимизатора запросов. А не истина последней инстанции.
Есть такое понятие как "достаточная производительность".
И есть две крайности:
1) вообще не принимать во внимание вопрос производительности, пока он сам не заявит о себе в полный рост (и это вполне себе работающий подход для стартапов)
2) слепо следовать рекомендациям, не обращая внимание на другую чашу весов.
Истина, как всегда, посередине. Если в боевых условиях удается получить достаточную производительность не переусложняя код и имея при этом какой-то запас по масштабируемости - то это хороший вариант.
Это я к чему. Я не раз и не два применял "ужас" со вложенными коррелирующими запросами и никакого "ужаса" по производительности в боевых условиях на немаленьких объемах данных не получал. Зато такие запросы, как правило, гораздо более простые и короткие, по сравнению с альтернативами.
---
```1c
    ВЫБРАТЬ
    ЗаказПокупателя.Ссылка,
    ЗаказПокупателя.Дата,
    ЗаказПокупателя.Контрагент
    ПОМЕСТИТЬ т
    ИЗ
    Документ.ЗаказПокупателя КАК ЗаказПокупателя
    ;

    ////////////////////////////////////////////////////////////­////////////////////
    ВЫБРАТЬ
    т1.Ссылка,
    т1.Дата,
    т1.Контрагент КАК Контрагент,
    КОЛИЧЕСТВО(РАЗЛИЧНЫЕ т2.Ссылка) КАК Ссылка1
    ИЗ
    т КАК т1
    левое СОЕДИНЕНИЕ т КАК т2
    ПО т1.Контрагент = т2.Контрагент
    И т1.Дата < т2.Дата

    СГРУППИРОВАТЬ ПО
    т1.Ссылка,
    т1.Дата,
    т1.Контрагент

    имеющие КОЛИЧЕСТВО(РАЗЛИЧНЫЕ т2.Ссылка) <4

    УПОРЯДОЧИТЬ ПО
    Контрагент,
    Ссылка1
```
---
Непонятно зачем тут ВТ и вообще выборка из регистра, а не документов, но я тоже хотел подвести ТС к схеме с
КОЛИЧЕСТВО(РАЗЛИЧНЫЕ т2.Ссылка) КАК Ссылка1
имеющие КОЛИЧЕСТВО(РАЗЛИЧНЫЕ т2.Ссылка) <=4
Только он пока ещё не написал, что хочет именно этого.
---
Ну ВЫ, блин, даете. Выборка всей физ.таблицы во временную.
Тогда уж делали бы:

```1c
    ВЫБРАТЬ
    т1.Ссылка,
    т1.Дата,
    т1.Контрагент КАК Контрагент,
    КОЛИЧЕСТВО(РАЗЛИЧНЫЕ т2.Ссылка) КАК Ссылка1
    ИЗ
    Документ.ЗаказПокупателя КАК т1
    левое СОЕДИНЕНИЕ Документ.ЗаказПокупателя КАК т2
    ПО т1.Контрагент = т2.Контрагент
    И т1.Дата < т2.Дата
```

....
---
Абсолютно не оптимальный запрос в первом ВТ. Если у человека база с заказами в миллион документов?! Первая выборка что сделает?!? Поместит ВЕСЬ МИЛЛИОН ДОКУМЕНТОВ в ВТ.

Это лажа!!!

Зачем там вообще различные!? ссылка никогда в этом запросе не продублируется. фигня это а не запрос.
---
Чот сразу не увидел, что из регистра продажи.

Выборку врем. таблицы тогда нужно немного переписать.
---
```1c
    ВЫБРАТЬ Различные
    Продажи.Контрагент
    поместить ВТ
    из РегистрНакопления.Продажи.Оборот() как продажи
    ;
    Выбрать
    ВТ.Контрагент,
    ПродажиДокументы.Документ
    из
    РегистрНакопления.Продажи.Оборот() как Продажи
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.Продажи как ПродажиДокумент
    ПО ВТ.Контрагент = ПродажиДокумент.Контрагент и ПродажиДокумент В (Выбрать первые 4 Прод.Документ из РегистрНакопления.Продажи как Прод Где Прод.Контрагент = ВТ.Контрагент УПОРЯДОЧИТЬ ПО Прод.Документ.Дата УБЫВ)
```
---
```1c
    ВЫБРАТЬ Различные
    Продажи.Контрагент
    поместить ВТ
    из РегистрНакопления.Продажи.Оборот(&ДатаНачала,&ДатаОкончания) как продажи
    ;
    Выбрать
    ВТ.Контрагент,
    ПродажиДокументы.Документ
    из
    РегистрНакопления.Продажи.Оборот(&ДатаНачала,&ДатаОкончания) как Продажи
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.Продажи как ПродажиДокумент
    ПО ВТ.Контрагент = ПродажиДокумент.Контрагент и ПродажиДокумент В (Выбрать первые 4 Прод.Документ из РегистрНакопления.Продажи как Прод Где Прод.Контрагент = ВТ.Контрагент УПОРЯДОЧИТЬ ПО Прод.Документ.Дата УБЫВ)
```
---
А вот этот запрос, точно повесит все ресурсы, учитывая объем данных, по ходу не мне надо учится оптимальный код писать! Конструкции "ПродажиДокумент В (Выбрать первые 4 Прод.Документ из РегистрНакопления.Продажи как Прод Где Прод.Контрагент = ВТ.Контрагент УПОРЯДОЧИТЬ ПО Прод.Документ.Дата УБЫВ) " работают очень медленно!!!
---
Выбрать первые 4 Прод.Документ из
Не будет отрабатывать такой запрос. Он выберет первые 4 записи по регистру "продажи". Тем более запрос построен неверно, с ошибкой. Мы в "Выбрать" не передадим конрагента из связи как ты хочешь.
---
Вы попробуйте и удивитесь. Контрагента без проблем передадите
---
Откройте для себя мир коррелирующих подзапросов :)
---
а вы попробуйте сначала.
---
один миллион записей обрабатывает за полсекунды
---
Вы думаете 1000000 записей это 1Тб? ))
---
да почитайте вы рекомендации самой 1С по поводу построения запросов...
---
вместо советов что нибудь почитать, попробуйте выполнить запрос. Хоть 10 терабайт весит база, укажите нормальные параметры для выборки и будет Вам счастье
---
Мне то зачем пробовать, я напробовался, пущай пробует кто вопрос задавал, и сравнивает, что быстрей ))
---
ради интереса откройте любой запрос типовой конфигурации УТ 11.3 к примеру печати счет-фактуры.
---
Я бы попробовал бы СКД через 2 набора данных ,в первом выбираются контрагенты за нужный период и передаются во второй набор в качестве параметров вирт.таблици Обороты и как раз во втором можно использовать ВЫБРАТЬ ПЕРВЫЕ 4 - как-то так
---
можно сделать через 4 временные таблицы, с выборкой максимальных заказов (хотя я бы сделал как Jen1978)

```1c
    ВЫБРАТЬ
    ЗаказКлиента.Контрагент КАК Контрагент,
    МАКСИМУМ(ЗаказКлиента.Ссылка) КАК Заказ,
    МАКСИМУМ(ЗаказКлиента.Дата) КАК Дата
    ПОМЕСТИТЬ ВР1
    ИЗ
    Документ.ЗаказКлиента КАК ЗаказКлиента

    СГРУППИРОВАТЬ ПО
    ЗаказКлиента.Контрагент
    ;

    ////////////////////////////////////////////////////////////­////////////////////
    ВЫБРАТЬ
    ВР1.Контрагент КАК Контрагент,
    МАКСИМУМ(ЗаказКлиента.Ссылка) КАК Ссылка,
    МАКСИМУМ(ЗаказКлиента.Дата) КАК Дата
    ПОМЕСТИТЬ ВР2
    ИЗ
    ВР1 КАК ВР1
    ЛЕВОЕ СОЕДИНЕНИЕ Документ.ЗаказКлиента КАК ЗаказКлиента
    ПО ВР1.Контрагент = ЗаказКлиента.Контрагент
    И (ВР1.Дата > ЗаказКлиента.Дата)
    ГДЕ
    НЕ ЗаказКлиента.Ссылка ЕСТЬ NULL

    СГРУППИРОВАТЬ ПО
    ВР1.Контрагент
    ;

    ////////////////////////////////////////////////////////////­////////////////////
    ВЫБРАТЬ
    ВР2.Контрагент КАК Контрагент,
    МАКСИМУМ(ЗаказКлиента.Ссылка) КАК Ссылка,
    МАКСИМУМ(ЗаказКлиента.Дата) КАК Дата
    ПОМЕСТИТЬ ВР3
    ИЗ
    ВР2 КАК ВР2
    ЛЕВОЕ СОЕДИНЕНИЕ Документ.ЗаказКлиента КАК ЗаказКлиента
    ПО ВР2.Контрагент = ЗаказКлиента.Контрагент
    И ВР2.Дата > ЗаказКлиента.Дата
    ГДЕ
    НЕ ЗаказКлиента.Ссылка ЕСТЬ NULL

    СГРУППИРОВАТЬ ПО
    ВР2.Контрагент
    ;

    ////////////////////////////////////////////////////////////­////////////////////
    ВЫБРАТЬ
    ВР3.Контрагент КАК Контрагент,
    МАКСИМУМ(ЗаказКлиента.Ссылка) КАК Ссылка,
    МАКСИМУМ(ЗаказКлиента.Дата) КАК Дата
    ПОМЕСТИТЬ ВР4
    ИЗ
    ВР3 КАК ВР3
    ЛЕВОЕ СОЕДИНЕНИЕ Документ.ЗаказКлиента КАК ЗаказКлиента
    ПО ВР3.Контрагент = ЗаказКлиента.Контрагент
    И ВР3.Дата > ЗаказКлиента.Дата
    ГДЕ
    НЕ ЗаказКлиента.Ссылка ЕСТЬ NULL

    СГРУППИРОВАТЬ ПО
    ВР3.Контрагент
    ;

    ////////////////////////////////////////////////////////////­////////////////////
    ВЫБРАТЬ
    ВР1.Контрагент КАК Контрагент,
    ВР1.Заказ КАК Заказ
    ИЗ
    ВР1 КАК ВР1

    ОБЪЕДИНИТЬ ВСЕ

    ВЫБРАТЬ
    ВР2.Контрагент,
    ВР2.Ссылка
    ИЗ
    ВР2 КАК ВР2

    ОБЪЕДИНИТЬ ВСЕ

    ВЫБРАТЬ
    ВР3.Контрагент,
    ВР3.Ссылка
    ИЗ
    ВР3 КАК ВР3

    ОБЪЕДИНИТЬ ВСЕ

    ВЫБРАТЬ
    ВР4.Контрагент,
    ВР4.Ссылка
    ИЗ
    ВР4 КАК ВР4
```
---
Так тоже не пойдёт. Ты выбираешь Максимум(Ссылка) и Максимум(Дата). В итоге мы получим дату последнего документа и ссылку на какой-то другой документ. Максимум по ссылке работает так: платформа смотрит на ГУИД объекта и выбирает максимальный ГУИД по строковому значению ГУИДа.
(хотя я бы сделал как Jen1978) - он неправильно сделал, там ошибка в коде в последнем условии. Нельзя такое условие построить.
---
согласен, не проверил, вот так правильно

```1c
    ВЫБРАТЬ
    ЗаказКлиента.Контрагент КАК Контрагент,
    МАКСИМУМ(ЗаказКлиента.Ссылка) КАК Заказ
    ПОМЕСТИТЬ ВР1
    ИЗ
    Документ.ЗаказКлиента КАК ЗаказКлиента
    ГДЕ
    ЗаказКлиента.Контрагент = &Контрагент

    СГРУППИРОВАТЬ ПО
    ЗаказКлиента.Контрагент
    ;

    ////////////////////////////////////////////////////////////­////////////////////
    ВЫБРАТЬ
    ВР1.Контрагент КАК Контрагент,
    МАКСИМУМ(ЗаказКлиента.Ссылка) КАК Ссылка
    ПОМЕСТИТЬ ВР2
    ИЗ
    ВР1 КАК ВР1
    ЛЕВОЕ СОЕДИНЕНИЕ Документ.ЗаказКлиента КАК ЗаказКлиента
    ПО ВР1.Контрагент = ЗаказКлиента.Контрагент
    И ВР1.Заказ.Дата > ЗаказКлиента.Дата
    ГДЕ
    НЕ ЗаказКлиента.Ссылка ЕСТЬ NULL

    СГРУППИРОВАТЬ ПО
    ВР1.Контрагент
    ;

    ////////////////////////////////////////////////////////////­////////////////////
    ВЫБРАТЬ
    ВР2.Контрагент КАК Контрагент,
    МАКСИМУМ(ЗаказКлиента.Ссылка) КАК Ссылка
    ПОМЕСТИТЬ ВР3
    ИЗ
    ВР2 КАК ВР2
    ЛЕВОЕ СОЕДИНЕНИЕ Документ.ЗаказКлиента КАК ЗаказКлиента
    ПО ВР2.Контрагент = ЗаказКлиента.Контрагент
    И ВР2.Ссылка.Дата > ЗаказКлиента.Дата
    ГДЕ
    НЕ ЗаказКлиента.Ссылка ЕСТЬ NULL

    СГРУППИРОВАТЬ ПО
    ВР2.Контрагент
    ;

    ////////////////////////////////////////////////////////////­////////////////////
    ВЫБРАТЬ
    ВР3.Контрагент КАК Контрагент,
    МАКСИМУМ(ЗаказКлиента.Ссылка) КАК Ссылка
    ПОМЕСТИТЬ ВР4
    ИЗ
    ВР3 КАК ВР3
    ЛЕВОЕ СОЕДИНЕНИЕ Документ.ЗаказКлиента КАК ЗаказКлиента
    ПО ВР3.Контрагент = ЗаказКлиента.Контрагент
    И ВР3.Ссылка.Дата > ЗаказКлиента.Дата
    ГДЕ
    НЕ ЗаказКлиента.Ссылка ЕСТЬ NULL

    СГРУППИРОВАТЬ ПО
    ВР3.Контрагент
    ;

    ////////////////////////////////////////////////////////////­////////////////////
    ВЫБРАТЬ
    ВР1.Контрагент КАК Контрагент,
    ВР1.Заказ КАК Заказ
    ИЗ
    ВР1 КАК ВР1

    ОБЪЕДИНИТЬ ВСЕ

    ВЫБРАТЬ
    ВР2.Контрагент,
    ВР2.Ссылка
    ИЗ
    ВР2 КАК ВР2

    ОБЪЕДИНИТЬ ВСЕ

    ВЫБРАТЬ
    ВР3.Контрагент,
    ВР3.Ссылка
    ИЗ
    ВР3 КАК ВР3

    ОБЪЕДИНИТЬ ВСЕ

    ВЫБРАТЬ
    ВР4.Контрагент,
    ВР4.Ссылка
    ИЗ
    ВР4 КАК ВР4
```
---
Максимум(Ссылка) - это по моменту времени для документа, можешь проверить
---
Рука-лицо. Тот случай, когда метод научного тыка формирует опаснейшие заблуждения. Потому что часто результат будет похож на правду, но иногда будет "глючить".
---
Я проверил. Ты не прав. Проверь сам.
---
тогда можно выбрать 4 последние даты и соединить с таблицей заказы по дате и контрагенту, может конечно быть что даты до секунд совпадут, тогда заказов может быть больше 4, но выбранное решение вообще первые попавшиеся 4 берет, без упорядочивания
---
ну и конечно вместо таблицы "Документ.ЗаказКлиента", нужно подставить "РегистрНакопления.Продажи", а вместо поля "Дата" - "Период"
---
Автору советую подумать в сторону периода документов, чтобы значительно уменьшить выборку тем самым облегчит запрос.
Индекс поля Контрагент в документе как обязательный.
Ну и советую подумать если задача стоит тривиально 4 документа попробуйте использовать простых запроса ОБЪЕДЕНИТЬ