gifts2017

Как удалить из выборки дубли по двум полям.

Опубликовал Петр Ивакин (Petr54-ru) в раздел Программирование - Практика программирования

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

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

Контекст задачи: писал обработку для УТ 11.1, которая заполняла вспомогательный периодический независимый регистр сведений по данным из проведенных документов ПоступлениеТоваровУслуг. Измерение регистра – Номенклатура, ресурс – цена. Специфика бизнеса такова, что поставщики в одном приходном документе ставили одну и ту же номенклатуру по разным ценам. Требовалось выбрать из пакета записей по полям «Дата» и «Номенклатура» запись с максимальной ценой. Иначе регистр отказывался воспринимать данные. Заодно нужно было исключить услуги.

Цель работы - быстро однократно получить нужные для задач управленческого учета цифры из "запущеной" и базы. 

Код процедуры исполняющейся на сервере.

Процедура НаСервереЗаполняемРегистрЦеныЗакупа(НашаДата)
// Очищаем регистр.
НаборЗаписей = РегистрыСведений.ЦеныЗакупаАнадо.СоздатьНаборЗаписей();
НаборЗаписей.Записать();

// Заполняем регистр данными

НаборЗаписей = РегистрыСведений.ЦеныЗакупаАнадо.СоздатьНаборЗаписей();
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ РАЗЛИЧНЫЕ
| ПоступлениеТоваровУслугТовары.Ссылка.Дата КАК Дата,
| ПоступлениеТоваровУслугТовары.Номенклатура КАК Номенклатура,
| ПоступлениеТоваровУслугТовары.Цена КАК Цена
|ИЗ
| Документ.ПоступлениеТоваровУслуг.Товары КАК ПоступлениеТоваровУслугТовары
|ГДЕ
| ПоступлениеТоваровУслугТовары.Ссылка.Проведен = &Проведен

| И ПоступлениеТоваровУслугТовары.Ссылка.Дата <= &Дата

| И ПоступлениеТоваровУслугТовары.Ссылка.Дата | И ПоступлениеТоваровУслугТовары.Номенклатура.ТипНоменклатуры <> &ТипНоменклатуры
|
|УПОРЯДОЧИТЬ ПО
| Дата,
| Номенклатура,
| Цена УБЫВ";
Запрос.УстановитьПараметр("Проведен", Истина);
Запрос.УстановитьПараметр("Дата", НашаДата);
Запрос.УстановитьПараметр("ТипНоменклатуры", Перечисления.ТипыНоменклатуры.Услуга);

Результат = Запрос.Выполнить().Выбрать();
// Присваиваем начальные значания проверочным переменным с которыми будем работать в цикле
ДатаДляПроверки = Дата(1,1,1);
НоменклатураДляПроверки = Справочники.Номенклатура.ПустаяСсылка();

Пока Результат.Следующий() Цикл
       Если (Результат.Номенклатура = НоменклатураДляПроверки) И (Результат.Дата = ДатаДляПроверки) Тогда
             Продолжить;
      Иначе
             НоменклатураДляПроверки = Результат.Номенклатура;
             ДатаДляПроверки = Результат.Дата;

             НовЗапись = НаборЗаписей.Добавить();

             НовЗапись.Период = Результат.Дата;
             НовЗапись.Номенклатура = Результат.Номенклатура;
             НовЗапись.Цена = Результат.Цена;
       КонецЕсли;

КонецЦикла;
НаборЗаписей.Записать(Истина);

КонецПроцедуры

Код сам по себе достаточно простой. В запросе я произвожу упорядочивание выборки -  по дате, затем по номенклатуре и по цене. Цена - по убыванию. Дальше используя вспомогательные переменные в теле цикла, произвожу отбрасывание дублей. Все работает, код читаем. 

См. также

Подписаться Добавить вознаграждение

Комментарии

1. sanek sanek_gk (sanek_gk) 16.11.13 10:22
Автор честно скажу сам не люблю когда кто то умничает но прими мои комментарии не лично к себе а коду.
Не спорю "код читаем", но "не понимаем".
1. У меня сомнения что код вообще выполнится ибо вряд ли у поля ПоступлениеТоваровУслугТовары.Ссылка.Дата тип Булево
2. При желании в выборку не попадают дубли, для этого используются разные способы (к примеру сгруппировать)
3. Стоило бы понимать почему регистр отказывается делать записи с одинаковой номенклатурой
никаких сомнений не остается после такого кода база действительно станет ' "запущеной" и базы'
4. Есть предложение именовать регистры словами и выражениями отражающими суть данного регистра, потому "ЦеныЗакупаАнадо" - вызывает сомнения действительно А НАДО ли ? И последнее, надеюсь никому этот код не пригодится разве что для наглядного пособия как не нужно делать.
вот запрос который сделает сам то что ты писал в коде тебе останется только передать в него:
1) две Даты(Дата1 - дата начала и ДАта2 дата окончания)
2) Записать результаты в регистр уже без всяких условий и проверок
|ВЫБРАТЬ
| ПоступлениеТоваровУслугТовары.Ссылка.Дата КАК Дата,
| ПоступлениеТоваровУслугТовары.Номенклатура КАК Номенклатура,
| МАКСИМУМ(ПоступлениеТоваровУслугТовары.Цена) КАК Цена
|ИЗ
| Документ.ПоступлениеТоваровУслуг.Товары КАК ПоступлениеТоваровУслугТовары
|ГДЕ
| ПоступлениеТоваровУслугТовары.Ссылка.Проведен = ИСТИНА
| И ПоступлениеТоваровУслугТовары.Ссылка.Дата МЕЖДУ &Дата1 И &Дата2
| И ПоступлениеТоваровУслугТовары.Номенклатура.ТипНоменклатуры <> ЗНАЧЕНИЕ(Перечисление.ТипыНоменклатуры.Услуга)
| И ПоступлениеТоваровУслугТовары.Номенклатура <> ЗНАЧЕНИЕ(Справочник.Номенклатура.пустаяссылка)
|
|СГРУППИРОВАТЬ ПО
| ПоступлениеТоваровУслугТовары.Номенклатура,
| ПоступлениеТоваровУслугТовары.Ссылка.Дата
|
|УПОРЯДОЧИТЬ ПО
| Дата,
| Номенклатура
ну и немного по логике работы : может в регистр стоит добавить также поставщика ? а то выяснится что минимальная цена закупки на такую то дату равна "X", а какого поставщика цена останется неизвестным... И мне так кажется что УТ 11 умеет сама делать эти штуки и настройка называется регистрировать цены поставщиков.....
Pervuy; anchovy; Ponommax; Serj1C; +4 Ответить 6
3. anado (Samojlov_Denis) 18.11.13 06:59
(1) sanek_gk,
3. Стоило бы понимать почему регистр отказывается делать записи с одинаковой номенклатурой никаких сомнений не остается после такого кода база действительно станет ' "запущеной" и базы'
- насколько я понял это проблема платформы, но с вами согласен не мешало бы разобраться и попинать 1С
4. anado (Samojlov_Denis) 18.11.13 07:35
(1) sanek_gk,
4. Есть предложение именовать регистры словами и выражениями отражающими суть данного регистра, потому "ЦеныЗакупаАнадо" - вызывает сомнения действительно А НАДО ли ? И последнее, надеюсь никому этот код не пригодится разве что для наглядного пособия как не нужно делать.
вот запрос который сделает сам то что ты писал в коде тебе останется только передать в него:
1) две Даты(Дата1 - дата начала и ДАта2 дата окончания)


Ваш пример не учитывает того, что Дата1=ДАта2, значит в регистр уйдет все тоже самое что и уходит в типовой, но тогда при регистрации цены поставщика (вы об этом тоже писали)- цена не привяжется, так как в регистре будет несколько цен и 1С тупо не поймет какую брать. Это кстати один из косяков почему неправильно считается себестоимость - так что вопрос "а надо ли это?" очень актуален.

P.S. АНАДО это название организации. По вопросам каламбура - смысл вложенный в названия фирмы: "А Надо!"
5. Тимофей Шантин (ShantinTD) 18.11.13 09:34
(0) anado,
Как удалить из выборки дубли по двум полям.
Тут, видимо, полагался вопрос. Тогда вопрос встречный: из какой выборки? Выборка из результата запроса? Тогда просто запрос нужно составить так, чтобы не осталось дублей, то есть "ЗАПРОСОМ УДАЛИТЬ ДУБЛИ!". Если выборка это СправочникВыборка или ДокументВыборка (или прочее), то это отдельная история.

(1) sanek_gk,
1. У меня сомнения что код вообще выполнится ибо вряд ли у поля ПоступлениеТоваровУслугТовары.Ссылка.Дата тип Булево
человек писал статью в пятницу вечером. Вместе с тем, что в запрос передается лишний параметр - понятно, что кусок строки из запроса просто отрезан.
Тем не менее, согласен, что приведенный код для данной задачи совершенно неприемлем. Конечно, нужно использовать правильно написанный запрос. И обойтись без цикла, например, что-то типа
НаборЗаписей.Загрузить(РезультатЗапроса.Выгрузить());


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

Да стоило бы понимать вообще что ты делаешь. И что уже сделали до тебя (и для тебя в том числе). Тогда вместо изобретания велосипеда можно будет на нем сразу кататься.

"код читаем", но "не понимаем"
а что именно не понятно?

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

(3) anado,
насколько я понял это проблема платформы
это не "проблема" платформы, а суть понятия "измерение" для регистра сведений (да и не только сведений).

не мешало бы разобраться и попинать 1С
В каком смысле "попинать"? В смысле разобраться как работает 1С? Или в смысле научить фирму 1С, что в регистр можно складывать неоднозначные записи? 0_0
6. anado (Samojlov_Denis) 18.11.13 12:34
(5) ShantinTD,
В каком смысле "попинать"? В смысле разобраться как работает 1С? Или в смысле научить фирму 1С, что в регистр можно складывать неоднозначные записи? 0_0


В том смысле что 1с умеет складывать в регистр "неоднозначные" записи, а потом сама же не может их использовать. То есть напрашиваются решения: или 1С не должна складывать "неоднозначные" записи в регистр; или уметь использовать "неоднозначные" записи. Иначе, действительно не нужно было бы изобретать велосипед.

P.S. Кстати Автор статьи и кода не я - мы просто вместе над одной проблемой работаем.
7. Тимофей Шантин (ShantinTD) 18.11.13 16:49
(6) anado, конкретно к Вашему посту я ответил на "насколько я понял".

Про неоднозначные записи заинтриговал. Можно увидеть пример, как сложить в регистр неоднозначные записи? Ну и как их потом пытаться использовать тоже? Без примера - "не верю!" ((с) К.С. Станиславский)
8. Дмитрий Глеков (glek) 18.11.13 17:30
(1) sanek_gk, Дополню: в запросе надо брать не Ссылка.Дата, а началопериода(ссылка.дата, <периодичность регистра>
9. Петр Ивакин (Petr54-ru) 18.11.13 21:49
Спасибо всем за комментарии в статье. Были запарки дома и на работе, прошу простить за запоздалый ответ.

(1) sanek_gk, вы написали замечательный запрос, однако мне нужны были несколько иные данные. Как верно тут написал ShantinTD -
в данном коде есть прием, который как раз таки может пригодиться. Не часто, но может.
(На всякий случай поясню, что прием этот - сравнение с текущей номенклатурой на каждом шаге, и действие только в случае отличия от нужной.)


Еще раз и более подробно, я при помощи обработки заливаю данные во вспомогательный регистр сведений - независимый, с периодом в одну секунду. Дальше я могу оттуда дергать куда угодно нужные мне данные, используя - СрезПоследних() и СрезПервых(). На то и регистры, чтобы оттуда брать данные.


(5) ShantinTD, ага, вы совершенно верно поняли про текущую номенклатуру на каждом шаге, и с вашей идеей о том что циклы вредят производительности и что все нужно получать одним запросом нельзя не согласиться. Вы даже начали писать сам запрос, хотелось бы увидеть такой запрос, который бы решал эту задачу. Я это в разумные сроки придумать не смог.

Да, параметр, который передается в процедуру он не лишний. Мне нужно было чтобы в регистр не улетели не проверенные данные. Например по 1 ноября, в базе все данные проверены и соответствуют, а дальше может быть и лажа. Так вот, чтобы лажа не летела в регистр, этот параметр Дата и ограничивает дату документов по времени.

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

Что касается родного регистра ЦеныНоменклатуры. У этого регистра штатно установлена периодичность в пределах дня. Я изменял период на в пределах секунды на УТ 11.0, все нормально было, сейчас УТ 11.1, есть вероятность что изменения периодичности может сломать логику программы. Тут так просто нельзя, надо тестировать. Как я уже писал, в приходной накладной от поставщиков есть одни и те же товары по разным ценам. Эта специфика бизнеса не укладывается в логику УТ 11. Так бывает.
Прикрепленные файлы:
10. Тимофей Шантин (ShantinTD) 19.11.13 08:41
(9) Petr54-ru,
Да, параметр, который передается в процедуру он не лишний.
глядя на запрос из (0) так не скажешь. В запрос нужно вернуть выпавший кусок текста (собственно параметр в тексте запроса).

Вы даже начали писать сам запрос
да его передо мной уже написал sanek_gk в (1).
хотелось бы увидеть такой запрос, который бы решал эту задачу
тогда не совсем понимаю, чем приведенный в (1) запрос не попадает под Вашу задачу.
Что касается родного регистра ЦеныНоменклатуры
Вам предложили воспользоваться регистром ЦеныНоменклатурыПоставщиков, а у него периодичность в пределах секунды.
с вашей идеей о том что циклы вредят производительности и что все нужно получать одним запросом нельзя не согласиться
из результата запроса можно получить выборку, а можно - выгрузку. Выгрузка может быть таблицей или деревом. И набор записей регистра - тоже таблица. Улавливаете? Зачем переписывать от руки, если можно сделать ксерокопию?
Что касается родного регистра ЦеныНоменклатуры. У этого регистра штатно установлена периодичность в пределах дня.
У меня под руками УТ11.1.2.16 Базовая (не думаю, что в этом вопросе это критично), у регистра ЦеныНоменклатуры периодичность в пределах секунды.
если перед программистом встала задача, которую он с налету решить не смог
согласен, бывают ситуации, когда грамотное решение требует слишком много времени, тщательной проработки или еще чего-то, то делаешь "аварийное" решение, потом переделываешь нормально. И нужно разделять "проблемы" на технические (не можешь написать хороший запрос и выгрузить его результат в набор записей - пишешь запрос попроще и заталкиваешь в регистр поштучно) и концептуальные ("что и куда я буду записывать?"). В решении технических проблем, конечно, гугл поможет, а вот концептуальных - далеко не всегда. Но и тут не нужно торопиться изобретать велосипед: есть Библиотека Стандартных Подсистем, есть типовые конфигурации, есть рекомендации, есть глобальные концепции. Так что вполне может найтись почти подходящий велосипед, к которому придется прикрутить свои колеса.
11. sanek sanek_gk (sanek_gk) 19.11.13 10:07
(4) anado, в запросе выражение "между" означает проверку на вхождение и равенство границам интервала. Т.Е. дата1 и дата2 могут запросто быть равны друг другу, и записи всё равно в запросе будут отобраны верно. По поводу того почему не пишутся одинаковые записи Период,Номенклатура - дело в том что в 1с Измерения не могут дублироваться в регистре сведений, так как записью считается именно пересечение указанных Измерений (ну и + значение Периода к измерениям - раз уж регистр периодический) платформа и ругается на неоднозначные записи, что в принципе логично и правильно. Ничего в запросе исправлять под описанную задачу не надо, так как он точно соответствует описанию задачи и приведенному примером коду. Единственное наводит на мысли, а правильно ли вообще в пределах секунды фиксировать записи, по мне так я бы предложил фиксировать одну запись в день (в настройках регистра "В пределах дня") потому что максимум в пределах секунды это не максимум в пределах дня + записей будет на порядок меньше(примерно во столько раз сколько документов поступления в день). В запросе я не задавал никаких новых правил алгоритма,я просто избавился от необходимости проверки на дубли потому что их там не будет. По поводу себестоимости могу сказать следующее : там все считает нормально так как определителем партии практически всегда выступает документ из чего следует что себестоимость единицы Номенклатура1 = (Сумма Сумм этих номенклатур)/(Сумма количества этих номенклатур).
12. Тимофей Шантин (ShantinTD) 19.11.13 15:25
(11) sanek_gk,
Ничего в запросе исправлять под описанную задачу не надо, так как он точно соответствует описанию задачи и приведенному примером коду.
это если задача описана правильно.
дело в том что в 1с Измерения не могут дублироваться в регистре сведений, так как записью считается именно пересечение указанных Измерений (ну и + значение Периода к измерениям - раз уж регистр периодический) платформа и ругается на неоднозначные записи
да это и не только в 1С. Просто называться оно может по другому (например - ключевое поле). Тут хочется или увидеть как anado складывает (успешно складывает) в регистр неоднозначные записи, или увидеть понимание, что это не 1С нужно попинать, а подучить матчасть.
13. Петр Ивакин (Petr54-ru) 20.11.13 07:11
(10) ShantinTD,
В запрос нужно вернуть выпавший кусок текста (собственно параметр в тексте запроса).

Вернул, спасибо за замечание не заметил.
Строчка выпала, когда я делал "подсветку синтаксиса" кода
14. Aleksandr Filonov (AleksSF) 20.11.13 10:53
Обсуждение идет уже 5 день, а в статье текст запроса кривой.
| И ПоступлениеТоваровУслугТовары.Ссылка.Дата | И ПоступлениеТоваровУслугТовары.Номенклатура.ТипНоменклатуры <> &ТипНоменклатуры

Как понять этот кусок, как неуважение к читателям. Типа мое дело прокукарекать, а там хоть и не рассветай.

Теперь о самой статье. Не совсем понятна решаемая задача, а от поставленной задачи зависит и алгоритм решения. Само решение тоже не понятно. Каждый раз перезаполнять весь регистр ???
А если в базе документы за последние 5 лет и в день приходов не один десяток.
Сюда принято выкладывать решения, которые обладают какой-либо универсальностью. А данное решение очень узкое и совсем не оптимальное.
По этому поводу хорошо сказал М.М. Жванецкий: "Писать как и писать нужно когда уже не можешь.
15. Петр Ивакин (Petr54-ru) 20.11.13 10:58
(14) AleksSF,
Не совсем понятна решаемая задача, а от поставленной задачи зависит и алгоритм решения. Само решение тоже не понятно. Каждый раз перезаполнять весь регистр ???


Прошу прощенья, в статье было написано
Цель работы - быстро однократно получить нужные для задач управленческого учета цифры из "запущеной" базы.

Что конкретно не понятно?
16. Aleksandr Filonov (AleksSF) 20.11.13 11:14
(15) Petr54-ru,
В том то и дело, что однократно, а здесь место для решений которые обладают хоть какой-то универсальностью.
У на у всех есть не одна сотня куском кода для решения частных задач. Если мы все это будем сюда выкладывать, то Инфостарт за полгода вырастет как на дрожжах. И пользователям чтобы найти нужную информацию придется просмотреть не одну сотню нашего флуда на тему, а кто лучше посчитал факториал.
17. Петр Ивакин (Petr54-ru) 20.11.13 12:01
(16) AleksSF,
В том то и дело, что однократно, а здесь место для решений которые обладают хоть какой-то универсальностью.
У на у всех есть не одна сотня куском кода для решения частных задач. Если мы все это будем


Есть философский вопрос - A что делать с "убитой" базой? Быстро вытащить оттуда нужные для управленческого учета данные и стартануть в новой базе, тщательно контролируя и организуя работу пользователей или убивать время на восстановление работоспособности старой "убитой" базы? В ряде случаев первый вариант предпочтительней.

С другой стороны, я искал в гугле реализацию исключения дублей по двум полям, что и написано в заголовке статьи, не нашел ничего. Теперь это в сети есть.
18. Марина Чирина (chmv) 28.11.13 09:16
19. Igor Krishen (IgKR) 10.12.13 00:52
Привет! А попробуй так?

ТекстЗапроса = "ВЫБРАТЬ
| ПоступлениеТоваровУслугТовары.Номенклатура,
| ПоступлениеТоваровУслугТовары.Ссылка,
| ПоступлениеТоваровУслугТовары.Цена
|ПОМЕСТИТЬ втДоки
|ИЗ
| Документ.ПоступлениеТоваровУслуг.Товары КАК ПоступлениеТоваровУслугТовары
|ГДЕ
| ПоступлениеТоваровУслугТовары.Ссылка.Проведен
| И ПоступлениеТоваровУслугТовары.Номенклатура.ВидНоменклатуры.ТипНоменклатуры = ЗНАЧЕНИЕ(Перечисление.ТипыНоменклатуры.Услуга)
| И ПоступлениеТоваровУслугТовары.Ссылка.Дата <= &НашаДата
|
|СГРУППИРОВАТЬ ПО
| ПоступлениеТоваровУслугТовары.Номенклатура,
| ПоступлениеТоваровУслугТовары.Ссылка,
| ПоступлениеТоваровУслугТовары.Цена
|;
|
|ВЫБРАТЬ
| втДокНом.Номенклатура,
| втДокНом.Ссылка
|ПОМЕСТИТЬ втДокНом
|ИЗ
| втДоки КАК втДокНом
|
|СГРУППИРОВАТЬ ПО
| втДокНом.Номенклатура,
| втДокНом.Ссылка
|;
|
|ВЫБРАТЬ
| втДокНом.Номенклатура,
| втДокНом.Ссылка,
| втДоки.Цена,
| втДокНом.Ссылка.Дата КАК Период
|ИЗ
| втДокНом КАК втДокНом
| ЛЕВОЕ СОЕДИНЕНИЕ втДоки КАК втДоки
| ПО втДокНом.Номенклатура = втДоки.Номенклатура
| И втДокНом.Ссылка = втДоки.Ссылка
| И (втДоки.Цена В
| (ВЫБРАТЬ ПЕРВЫЕ 1
| втДоки2.Цена
| ИЗ
| втДоки КАК втДоки2
| ГДЕ
| втДоки2.Номенклатура = втДокНом.Номенклатура
| И втДоки2.Ссылка = втДокНом.Ссылка
| УПОРЯДОЧИТЬ ПО
| втДоки2.Цена УБЫВ))";

Запрос = Новый Запрос;
Запрос.Текст = ТекстЗапроса;
Запрос.УстановитьПараметр("НашаДата", НашаДата);

НаборЗаписей = РегистрыСведений.ЦеныЗакупаАнадо.СоздатьНаборЗаписей();
НаборЗаписей.Загрузить(Запрос.Выполнить().Выгрузить());
НаборЗаписей.Записать();
Прикрепленные файлы:
20. Денис Луцик (NAKIS) 18.12.13 11:53
Спасибо, помоголо очень)
22. Роман Ложкин (webester) 07.03.14 19:18
Я понимаю, что это было квартал назад, но все равно спасибо, вам! Читая как вы на полном серьезе пишете:
С другой стороны, я искал в гугле реализацию исключения дублей по двум полям, что и написано в заголовке статьи, не нашел ничего. Теперь это в сети есть.

очень сильно поднимается самооценка! Еще раз спасибо! Вы лучшие, продолжайте радовать своими "креативами"
23. Vlad (KillHunter) 09.03.14 00:19
Неплохая версия запроса, а если попробовать ЧЕРЕЗ "Объединить" две таблицы, чтобы свернуть одинаковые поля!
Для написания сообщения необходимо авторизоваться
Прикрепить файл
Дополнительные параметры ответа