Имя: Пароль:
1C
1С v8
Как вывести миллион строк из таблицы 1С в файл (и не свихнуться)?
0 ironicman
 
30.07.20
16:21
Теоретический вопрос, но без шуток, по мотивам настоящей задачи.
Один мой друг (допустим из бухгалтерии) захотел выгрузить допустим миллион строк (допустим проводок) из таблицы 1С в файл, потому что ну вот надо.

Как быстрее всего вывести этот миллион строк?
1. Обойти выборку и вывести в табличный документ - не подходит потому что очень долго (очень долгие часы или даже дни...) и сильно ест память
2. Обойти выборку и вывести в csv/текст строки с разделителями через ЗаписьТекста или ЗаписьXML.ЗаписатьБезОбработки - не подходит потому что очень долго (долгие часы или дни)
3. Обойти выборку и вывести в csv/текст строки с разделителями через ЗаписьТекста или ЗаписьXML.ЗаписатьБезОбработки в в фоне в нескольких воркерах - не очень, потому что просто долго (часы..)

Дополнительные вводные:
Запрос написан достаточно оптимально и выполняется быстро, в запросе фигурируют только примитивные типы данных - большинство из них строки, но есть и цифры. В выборку попадает порядка 50 разных полей. Профайлер говорит, что бОльшая часть времени уходит на получение значений из выборки через Выборка[Имя_нужного_поля]

Хотелось бы услышать мнение опытных джентельменов.

З.Ы.
Настоящему джентельмену не запрещено использовать любые, пусть даже не 1с-ные методы выгрузки. Задачу конечно, переформулировать нельзя - ибо джентельмены так с дамами (допустим из бухгалтерии) не поступают
1 H A D G E H O G s
 
30.07.20
16:23
ДЖентельмены не верят.
2 Ёпрст
 
30.07.20
16:24
да пиши через Scripting.FileSystemObject делов то
3 SleepyHead
 
гуру
30.07.20
16:25
(0) А есть видео, где этот друг показывает, как он потом пользуется этим миллионом строк?
4 mikecool
 
30.07.20
16:25
(0) "тебя посодют, а ты не воруй" (с)
5 Ёпрст
 
30.07.20
16:26
6 rowvg
 
naïve
30.07.20
16:26
(0) Подскажу если скажешь что потом с этим файлом твой друг будет делать
7 Hmster
 
30.07.20
16:30
долго и много это как понимать?
8 NcSteel
 
30.07.20
16:32
(3) Ну вот у меня баже больше, эти данные нужна для машин ленинг
9 Garykom
 
гуру
30.07.20
16:32
(0)
1. Миллион строк это фигня
2. Выводить надо порциями
3. Через COM ВК используя https://habr.com/ru/post/216381/
10 SleepyHead
 
гуру
30.07.20
16:33
(8) Пивовар. Судя по тексту, уже продегустировал партию?
11 NcSteel
 
30.07.20
16:34
(10) Не знаю что с ним делать) Весь подвал забит, начинаю переходить в сыровара)
12 ironicman
 
30.07.20
17:02
(2) Спасибо, попробую. Но думаю не сильно поможет. Сейчас судя по анализу производительности дольше всего работает обход полей выборки.

(6) Хз, если честно, но думаю как минимум хранит. Можно подумать, ты никогда не делал бессмысленной работы))

(7) "Долго" это значит "Хочется ещё быстрее, но не знаю каким образом", "Много" это "допустим миллион".

(9) Вот это уже интересно, спасибо!

Господа, есть ещё идеи?
Я думаю трейсануть профайлером что за запрос выполнялся сделать селект в файл средствами SQL. Но там не так всё красиво как мне бы хотелось из-за получения представлений для полей составного типа.
13 ironicman
 
30.07.20
17:03
(8) Ну, не томи, как выгружал?
14 Конструктор1С
 
30.07.20
17:08
(0) не пойму, тебе нужно во что бы то ни стало выгрузить в какой угодно текстовый формат и обязательно одним файлом?
15 ironicman
 
30.07.20
17:09
(14) Угу
16 Конструктор1С
 
30.07.20
17:09
и да, что-то ты делаешь не так, в XML и поболее данных свободно выгружается
17 Конструктор1С
 
30.07.20
17:13
(15) если будешь грузить порциями, то уже будет намного проще, и можно будет выгрузки разбить на паралельные потоки. Поищи какую-нибудь внешнюю приблуду, которая соединяет текстовые файлы в один. Выгрузи кусками в CSV, например, потом соедини всё в один файл
18 Конструктор1С
 
30.07.20
17:14
Но всё-таки, миллион строк это ни о чём. Попробуй поделать замеры и поискать узкие места в твоей выгрузке
19 Кирпич
 
30.07.20
17:16
Запрос кривой наверное. ЗаписьXML.ЗаписатьБезОбработки работает быстро.
Как вариант - запрос в таблицу значений. Таблицу значений сериализовать в XML или JSON
Правда чо будет делать с  этим xml друг из бухгалтерии...
20 dezss
 
30.07.20
17:16
(0) слушай...а может сразу в запросе получать представления, а не при выводе в файл?
21 МихаилМ
 
30.07.20
17:18
(0)была недавно похожая тема. ищите по слову "конкотенация"
22 NcSteel
 
30.07.20
17:19
(13) Обычно, через Запись текста, подготовка данных дольше занимает чем сама запись. Весь процесс занимает минут 30-40
23 dezss
 
30.07.20
17:20
(20) + глючит...
вижу, что в запросе только примитивные типы.
Ну тогда странно. А что замер производительности говорит?
24 NcSteel
 
30.07.20
17:22
(23) Про замер в нулевом посте тоже написано;)
25 Креатив
 
30.07.20
17:39
(0)А если выполнить запрос в консоли и вывести список?
26 ironicman
 
30.07.20
17:40
Если поменять пыщь-строку в цикле на вжух-строку (чисто для самопроверки) то вывод ускоряется кратно.
Хз как тут код оформлять, но вот:
//... хожу по выборке
    Для каждого Колонка Из ИменаКолонокВЗапросе Цикл
        //... тут что-то невесомое
        //ЗначениеПоляВыборки = Выборка[Колонка] // пыщь-строка, долгая
        ЗначениеПоляВыборки = "Вжух" // вжух-строка, быстрая ну каэшна)
        //... тут вывод ЗначениеПоляВыборки файл
    КонецЦикл"
//...
27 ironicman
 
30.07.20
17:43
(25) Боюсь, это тупиковый сценарий, потому что он сводится к п.1 из (0), нет?

Всем спасибо, попробую приготовить завтра какой-нибудь минимальный воспроизводимый пример, может в ходе его подготовки что-то выяснится
28 H A D G E H O G s
 
30.07.20
17:52
Почему нельзя верить джентельменам
http://prntscr.com/tr8ud3
http://prntscr.com/tr8ut3
http://prntscr.com/tr8v2x

И все равно у меня вопросы к производительности этого решения...
29 H A D G E H O G s
 
30.07.20
18:05
Если ЗаписьДанных заменить на ЗаписьТекста то все становиться гораздо лучше
http://prntscr.com/tr9498
30 Злопчинский
 
30.07.20
18:50
(17) " Поищи какую-нибудь внешнюю приблуду, которая соединяет текстовые файлы в один. Выгрузи кусками в CSV, например, потом соедини всё в один файл"
- тоже мне...
copy /b a1.txt+a2.txt+a3.txt >a.txt
31 Сияющий в темноте
 
30.07.20
18:57
ну,насколько я помню,у ексель конеснон чтсло строк но я выгружал на несколько листов блоками.
32 Ёпрст
 
30.07.20
18:58
(29) а через ЗаписьXML.ЗаписатьБезОбработки ? И на какой платформе? На старых, записьтекса не очень шустро было
33 VladZ
 
30.07.20
19:05
(0) Не нужно тебе это.
34 H A D G E H O G s
 
30.07.20
19:17
(32) 8.3.15
35 ILM
 
гуру
30.07.20
19:55
Выводишь строки многопоточно в 10 файлов, потом командой системы собираешь их в один. Должно отработать минут за 10. Я так данные по городу выгружал 850000 лицевых счетов с данными.
36 polosov
 
30.07.20
21:32
(35) Зачем КомандаСистемы?
Нативная ОбъединитьФайлы есть же
37 Asmody
 
30.07.20
22:17
(0) Странные у твоего друга желания. У меня есть друг,  который на таких желаниях специализируется, - он психиатр.
38 acht
 
30.07.20
22:24
И сейчас окажется, что это все происходит в толстом клиенте файловой базы.
39 Sysanin_1ц
 
31.07.20
00:35
(0) На самом деле такие запросы довольно часто приходят от всяких разных аудиторов. Мы у себя ежегодно выгружаем данные по проводкам для аудита. Получается в среднем 800 тыс строк. Для выгрузки используем метод выгрузить непосредственно после выполнения запроса к регистру бухучета. Методы выгрузить загрузить в 1с оптимизированы. После этого оперируем с ТЗ. Обход ТЗ на хорошем компе проходит быстро, так как сама ТЗ в оперативной памяти. Результат пишем либо в текстовой файл с csv разделителями или в dbf. И потом уже этот файл открывается в экселе. На все про все уходит пару часов
40 ironicman
 
31.07.20
00:39
(29) (39) Откровенно говоря, не стал даже пробовать использовать ТаблицаЗначений из суеверных побуждений. В типовых её не встретить, на сертификационных экзаменах за неё банят, хранится на сервере в сеансовых данных в файле...короче сразу отмёл и зря. Вот и думай теперь...
Однако, в попытке приблизить мой вариант к (29), чтобы сравнить, я заменил выборку на таблицу значений и кратно сэкономил время.
Большое спасибо!

В итоге всё сошлось к такому варианту: чтобы выгрузить годзилла-таблицу из 1С нужно разбить её на несколько порций, каждая порция данных запросом выгружается в ТЗ, из которой данные выводятся в csv с использованием ЗаписьТекста, полученные кусочки склеиваю так (ОбъединитьФайлы не пробовал, попробую):
for %f in (C:\что-то\что-что\*.csv) type "%f" >> C:\что-то\что-то\result.csv

Занимает, на всё про всё, около 25 минут.
41 ironicman
 
31.07.20
00:41
Халва Мисте!
42 Ёпрст
 
31.07.20
00:44
(40) 486 пня что ле у тебя ? Откуда там 25 минут то ?
43 ironicman
 
31.07.20
00:52
(42) Теперь основном жду на запросе к таблице РегистрБухгалтерииДвиженияССубконто и на РезультатЗапроса.Выгрузить(). Там по-твоему есть что оптимизировать?
44 ironicman
 
31.07.20
00:58
(42) (43) Кстати, наверное есть. В принципе можно подобрать порции данных так, чтобы оптимизировать выигрыш в ожидании запроса к РегистрБухгалтерииДвиженияССубконто по отношению к проигрышу на всех других частях пайплайна.
45 Ёпрст
 
31.07.20
01:11
(43) есть.. РегистрБухгалтерииДвиженияССубконто  - одна из самых тормозных табличек в базе
46 ironicman
 
31.07.20
01:13
(45) Не томи, чертяка) Есть другой способ получить ровно эти же колонки быстрее (включая типы субконто)?
47 lodger
 
31.07.20
01:14
(39) по теме dbf стало интересно
какой из способов может работать "на другой скорости"?
объект xbase
com object ado db
или прописать в конфигурации ВнешнийИсточникДанных
или получится одно и то же?
48 Eeeehhhh
 
31.07.20
09:00
Вариант DBF хорош, если вписывается ограничения DBF.
Создать таблицу на SQL, а затем
SQLCMD -S server_name -E -Q "SELECT column1,column2,column3 FROM work_base.dbo.tmp_table" -s "," -o "d:\data_file.csv"
49 Garykom
 
гуру
31.07.20
09:05
А шо еще никто не придумал прямых запросов для 8-ки?
Ну типа пишем запрос на 1С а получаем прямой sql и автоматом вьюху
50 Злопчинский
 
31.07.20
09:06
(40) заюзать type даже с переопределением потока вывода - это сильно...

copy f1+f2 frez должно быть ощутимо побыстрее
51 Eeeehhhh
 
31.07.20
09:08
(49) инструменты разработчика могут это сделать, как вытащить фактический запрос, так и трансформировать в SQL формат.
52 rsv
 
31.07.20
13:05
(0)  если база на скуле  откройте доя себя мастер импорта экспорта MS SQL
53 sikuda
 
31.07.20
13:37
(50) ТС гонит. Простая ЗаписьТекста в выборке ЗаписьСтроку идеально подходит...
Другое дело если все это на сервере, а потом тащить на клиента...
54 ironicman
 
31.07.20
13:47
(53) Да разрулили уже, припозднился ты)
Если бы была файловая я бы так и написал. Я ужал в зип и утащил на клиента - вроде норм, друг доволен)
55 Конструктор1С
 
01.08.20
09:06
(49) а что оно даёт? И про какие вьюхи ты говоришь?
56 Garykom
 
гуру
01.08.20
13:54
57 Cyberhawk
 
01.08.20
15:48
"Профайлер говорит, что бОльшая часть времени уходит на получение значений из выборки через Выборка[Имя_нужного_поля]" // Какой профайлер?
58 ДенисЧ
 
01.08.20
16:23
(57) А что - их у 1с много? О_о
59 H A D G E H O G s
 
01.08.20
19:10
(55) Это магия черного ящика.
На самом деле и в других ЯП все плохо.
Конечно, скуль все сделает четко, но на то он скуль.

Собственно, писать кусками через буферизированный hFile через FileWrite - такое себе (через это пишут, к примеру, все vcl компоненты от Delphi, например TFileStream, TStringStream и, я подозреваю, вся остальная пиздобратия от C++, java и питонов). Он эти куски пихает в буфер и реллокэйтит его. Поэтому нужно писать в небуферизированный hFile готовый буфер, тогда будет скоро.

procedure Test();
var
  Значение: string;
  ЗначениеБайтами: TBytes;
  Размер, НакопленныйРазмер: Cardinal;
  ИмяКолонки: string;

  Буфер: TMemoryStream;
  Счетчик: integer;
  Разделитель: String;
  Старт, Стоп, ВремяВыполнения: Cardinal;
  count, ЗаписаноБайт: Cardinal;
  КоличествоСтрокЗапроса: Cardinal;
  hFile: THandle;
begin
  Разделитель := #13#10;
  Старт := Gettickcount;
  ADOConnection1.Open('sa', '1');
  ADOQuery1.SQL.Text :=
    'SELECT TOP 400000 _Description as d1, _Description as d2, _Description as d3, _Description as d4, _Description as d5, _Description as d6, _Description as d7, _Description as d8, _Description as d9'
    + ', _Description as d10, _Description as d11, _Description as d12, _Description as d13, _Description as d14, _Description as d15, _Description as d16'
    + '  FROM dbo._Reference51';

  hFile := CreateFile(pchar('e:\tmp1\data.txt'), GENERIC_READ or GENERIC_WRITE,
    0, nil, TRUNCATE_EXISTING, FILE_FLAG_WRITE_THROUGH, 0);
  Буфер := TMemoryStream.Create;
  ADOQuery1.Open;
  ProgressBar1.Max := 400000;
  ЗаписаноБайт := 0;
  КоличествоСтрокЗапроса := 0;
  Буфер.SetSize(1224 * 1024 * 1024); // 1224 мегабайта буфера
  НакопленныйРазмер := 0;
  while not ADOQuery1.Recordset.EOF do
  begin
    for Счетчик := 0 to ADOQuery1.FieldList.count - 1 do
    begin
      ИмяКолонки := ADOQuery1.FieldList.Fields[Счетчик].FullName;
      Значение := ADOQuery1.Recordset.Fields[ИмяКолонки].Value;
      ЗначениеБайтами := TEncoding.UTF8.GetBytes(Значение);
      Размер := length(ЗначениеБайтами);
      НакопленныйРазмер := НакопленныйРазмер + Размер;
      Буфер.WriteData(ЗначениеБайтами, Размер);
    end;
    Буфер.WriteData(Разделитель[1], 2);
    НакопленныйРазмер := НакопленныйРазмер + 2;
    ADOQuery1.Recordset.MoveNext;
    inc(КоличествоСтрокЗапроса);
    if КоличествоСтрокЗапроса mod 10000=0 then
    begin
      ProgressBar1.Position := КоличествоСтрокЗапроса;
      Application.ProcessMessages;
    end;
    if НакопленныйРазмер >= 1024 * 1024 * 1024 then // 1024 мегабайта буфера заполнено, сбросим на диск
    begin
      FileWrite(hFile, Буфер.Memory^, НакопленныйРазмер);
      FlushFileBuffers(hFile);
      ЗаписаноБайт := ЗаписаноБайт + НакопленныйРазмер;
      НакопленныйРазмер := 0;
      Буфер.Seek(0, soFromBeginning);
    end;
  end;
  FileWrite(hFile, Буфер.Memory^, НакопленныйРазмер);
  ProgressBar1.Position := КоличествоСтрокЗапроса;
  Application.ProcessMessages;
  FlushFileBuffers(hFile);
  ЗаписаноБайт := ЗаписаноБайт + НакопленныйРазмер;
  НакопленныйРазмер := 0;
  FlushFileBuffers(hFile);
  FileClose(hFile);
  Стоп := Gettickcount;
  ВремяВыполнения := round((Стоп - Старт) / 1000);
  ShowMessage('Выполнено за ' + FloatToStr(ВремяВыполнения / 60) + 'мин.' +
    ' Записано байт:' + inttostr(ЗаписаноБайт) + '. Записей:' +
    inttostr(КоличествоСтрокЗапроса));
end;

Тут я пишу тупо в выделенный буфер, чтобы избежать реллокации и потом без буферизации в файл.
Табличка та же, что и в примере с 1С, но записей 400к, так как на болшем количестве ADO валиться с ошибкой выделения памяти, разбираться в которой мне лень.
Результаты:
http://prntscr.com/tsc1m6
60 H A D G E H O G s
 
01.08.20
19:15
(59) 0.45 минут за 400к экстраполируем в 1000k и получаем 1,125 мин в других ЯП против 3,6 мин в 1С.

Что скажет Гарри, ну там, фьюхи, вывод порциями, запись через ВК, натурные эксперименты?
61 Garykom
 
гуру
01.08.20
19:24
62 wt
 
01.08.20
19:28
(9) ТС легенду плохую представил.
У меня вот какой случай был.
Несколько складов на предприятии. Несколько млн единиц хранения. Аудиторы привязались, после того как автоматизировал, а типа где у вас карточки учета ТМЦ? А кладовщики уже про бумажки и забыли. Паника. А там аудиторы хотели посмотреть движение по позициям с приличными суммами и по интересующим вышестоящей компании фирмам (ТС вот тебе обоснование, нафига все это надо.) Пришлось молниеносно выводить в файл за последние три года карточки учёта ТМЦ и движение МЦ. Я вывел, получилось дофига файлов, в каждом по 500 карточек со всех складов( я озаботился об аудиторах, чтоб Москве не сломали). Но ГБ решила схитрить и сказала чтоб  делал одним файлом. Сделал. Записал на флешку 128Гб. ГБ отдала. Я потом поинтересовался , ну как, открыли, посмотрели? ГБ была очень довольна, сказала, типа просили? Мы вам дали и показали, а вы уж там с собственником разбирайтесь. Аудиторы больше не донимали с карточками.Правда, потребовали,   Об мы каждый квартал сбрасывали в файл. Но это уже другая история.
63 H A D G E H O G s
 
01.08.20
19:34
(61) где деньги, Лебовски?
64 Garykom
 
гуру
01.08.20
19:36
(63) До примера В до читай и сам попробуй.

Ибо проводить тесты разных способов на разном железе и разном софте и разных базах/данных изврат.
65 H A D G E H O G s
 
01.08.20
21:22
(64) Экспорт в файл csv через мастер экспорта выполнил запись 1кк строк за 54 секунды. Не так уж и быстрее, чем 1.12 мин.
66 vde69
 
01.08.20
21:39
(62) у меня есть похожая история.

У нас за месяц выходило примерно 2 ящика сф, примерно 10к страниц.

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

Дали нам команду напечатать.
Ну во первых пришлось по кускам...
Во вторых печатали в параллель на 3х принтерах. Когда неожиданно закончилась бумага пришёл шеф и увидев 3 коробки напечатан ной карточки заорал

Дебилы, им всего 5 строчек нужно было...

Короче бухам вылетело..
67 Ёпрст
 
01.08.20
21:52
Эээх.. а во времена бумажных декларациях на рохлях возили коробки с бумажками, стояли в очереди на заветный штампик на своём экземпляре..
Уже не помню, сколько коробок бумаги одна декларация занимала. Печатали её кусками на всех принтерах.
68 Ёпрст
 
01.08.20
21:52
еще и прошивать надо было и скреплять, п...ц бардак был
69 b_ru
 
01.08.20
22:45
Для й = 1 по КоличествоКолонокВЗапросе Цикл
    ЗначениеПоляВыборки = Выборка[й];
КонецЦикл;


Плюс в запросе получай не ссылки на объекты, а их наименования.
70 Fragster
 
гуру
01.08.20
22:49
(0) хз, что там у тебя, но у меня 4кк строк (после получения данных) что в xml, что в json выгружались минуты. Если это долго, то на этом наши полномочия как бы все.
результирующий файл был под гигабайт, если что.
71 wt
 
01.08.20
23:50
(66) аналогично.  Только мы для аудиторов открывали актовый зал. Туда всю сетевую инфраструктуру на 30 рабочих мест десяток принтеров. Копию баз, ибо нефик мешать работе. Типа печатайте сами, если файл смотреть не хотите. На самом деле, там только одно название»аудиторы». Нанимают тучу бап, заставляют их собирать тофу.  Яйцеголовых пара, тройка. Если ГБ стоящий, то какие ему удобно недостатки зафиксирует. А если ГБ лопух, извините...
И такая история раз в полгода, на 1,5 месяца.
72 wt
 
01.08.20
23:57
+(66) да, я ещё подсуетился и бюджет на принтеры и расходные материалы увеличил. Так отдельной строкой и запустил. Концерн утерся.
73 wt
 
02.08.20
00:36
(67) ещё одна байка.
Сменилось руководство. Фиником поставили какого своего бывшего военного. А у того строгая субординация. И нач. ЦФО не дали права подписи. Все испереживались,типа как же теперь жить? И вот наступает пора сдачи кварт отчётности. А там не помню что за бумажки, то ли книга покупок, то ли сч-факт, в общем на каждом листочке должна стоять первая подпись. И вот торжественной вереницей начЦФО со своими приближенными прёт финику коробок, наверное 10, сброшурованных листов А4. Вышла, фразу бросила, ну теперь пусть руководит!
Через некоторое время, слышу, требует кого нибудь, чтоб слюнили и листы переворачивали. Через несколько дней говорили, когда закончил, что злой был как черт.
74 v77
 
02.08.20
00:40
(59) Чо TBufferedFileStream в Delphi забанили чтоли? Он бы за пару секунд миллион записей на диск скинул бы. Проблема только в том, как за пару секунд эти записи из ADO прочитать.
75 wt
 
02.08.20
00:53
(74) там вся беда в форматах представления этих данных. Они регламентированы ПБУ. И бабушки другого не понимают.
76 Cyberhawk
 
02.08.20
08:12
(58) Впервые слышу "у 1С"
77 vde69
 
02.08.20
08:23
(73) для этого есть факс мили, только её надо зарегить... А так сажает девочку и та шлепает а он смотрит и сигары курит..
78 Конструктор1С
 
02.08.20
08:52
(73) по-моему какая-то тупость. Всё-таки подпись означает, что человек с документом ознакомлен, подтверждает правильность, берет ответственность. А при таком рутинно-механическом прдписывании он мог расписаться хоть под признанием в убийстве Кеннеди, хоть на рулоне обоев. Если возникают такие ситуации, то надо копать в сторону правильности делегирования полномочий
79 ДенисЧ
 
02.08.20
08:52
(76) У 1с нет профайлера? О_о
2 + 2 = 3.9999999999999999999999999999999...