Форум программистов, компьютерный форум, киберфорум
Microsoft SQL Server
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
1304 / 358 / 97
Регистрация: 14.10.2022
Сообщений: 1,087

Как создать fmt файл на основе вызова хранимой процедуры?

18.09.2025, 14:56. Показов 924. Ответов 13
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Коллеги, приветствую!

Есть проблема. В одной их подшефных БД имеется прядка 200 хранимых процедур.
Все они вызываются однотипно, и организовать выгрузку данных нет проблем.
Генерирую по названию API такие команды:

Code
1
bcp "exec [DB1].[report].[API_InformFirst] @department_id = N'30',@begin_date = N'20250101', @end_date = N'20250831'" queryout D:\out\30_API_InformFirst.txt  -S . -T -d DB1 -c -C 1251 -t\0\t -r\0\n
И скармливаю их cmd.

Проблемы начинаются потом.
Дело в том, что эти API возвращают чуть-чуть разный результат, в смысле состава данных (да, примерно одинаковый, но нет, слегка разный).
Мне нужно сгенерировать fmt файлы для всех этих 200 хранимок.

И нет, sys.dm_exec_describe_first_result_set - не поможет, потому что в некоторых, чуть менее чем всех, хранимках используются временные таблицы.

Какие есть соображения?
0
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
18.09.2025, 14:56
Ответы с готовыми решениями:

Как создать отчет (форму) в Access на основе хранимой процедуры с параметрами
Как создать отчет (форму) в Access на основе хранимой процедуры с параметрами. Без параметров -...

Вставка тела хранимой процедуры вместо вызова по имени
Всем добрый вечер. Подскажите, в БД есть хранимая процедура. В хранимой процедуре есть...

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

13
 Аватар для abit
868 / 528 / 148
Регистрация: 03.02.2013
Сообщений: 1,845
18.09.2025, 15:30
через промежуточную таблицу пробовал?

SQL
1
2
3
4
5
6
7
8
9
10
11
DECLARE @SQL NVARCHAR(MAX) = '
    SELECT * 
    INTO #TempResult 
    FROM OPENROWSET(
        ''SQLNCLI'', 
        ''Server=.;Trusted_Connection=yes;'',
        ''EXEC [DB1].[report].[API_InformFirst] @department_id = N''''30'''', @begin_date = N''''20250101'''', @end_date = N''''20250831''''''
    )
    SELECT * FROM #TempResult WHERE 1 = 0'
 
EXEC sp_describe_first_result_set @SQL
0
1304 / 358 / 97
Регистрация: 14.10.2022
Сообщений: 1,087
18.09.2025, 15:59  [ТС]
Цитата Сообщение от abit Посмотреть сообщение
через промежуточную таблицу пробовал?
Нет. Та же ошибка, что и напрямую.
The metadata could not be determined because statement 'insert #tDS_C (DS)
Т.е. внутри хранимки есть вставка во временную таблицу.
0
668 / 291 / 120
Регистрация: 12.04.2022
Сообщений: 1,000
19.09.2025, 08:05
Цитата Сообщение от uaggster Посмотреть сообщение
Т.е. внутри хранимки есть вставка во временную таблицу.
Создать глобальную временную таблицу через create, должно работать.

BCP Global temporary table data - Unable to create format file and export data
0
1304 / 358 / 97
Регистрация: 14.10.2022
Сообщений: 1,087
19.09.2025, 12:16  [ТС]
Цитата Сообщение от PaulWist Посмотреть сообщение
Создать глобальную временную таблицу через create, должно работать.
Нет. Не об этом речь.
Задача в том, что у меня есть под 1000 (уже, как выяснилось), ХП, которые вызываются однотипно, но возвращают немного разные по форме resultset-ы.
Мне нужно выгружать результаты всех csv, и потом - загрузить на других серверах.
(Для этого нужен SSIS, но что есть, то есть).
Выгрузить очень просто. bcp, и никаких проблем.
А вот чтобы ЗАгрузить - нужна информация о составе выгруженного.
Собственно, я думал, что всё можно решить через sys.dm_exec_describe_first_result_set, а фот фиг.

Пока попросил Дипсик написать мне на пауэршелл скрипт, который вызывает ХП по списку, подключившись через клиента, а потом описывает NET-овский resultset.
Типы там, получаются NET-овские, но хотя бы так.
0
74 / 18 / 6
Регистрация: 18.01.2025
Сообщений: 83
19.09.2025, 12:46
Можно еще попробовать вот этот инструмент задействовать наверное: SQL Server Integration Services (SSIS). Или его и так планируется использовать?
0
 Аватар для Andrey-MSK
3329 / 2216 / 387
Регистрация: 14.08.2018
Сообщений: 7,460
Записей в блоге: 4
19.09.2025, 15:32
Цитата Сообщение от uaggster Посмотреть сообщение
а потом описывает NET-овский resultset
И NET есть SqlBulkCopy(), очень удобная штука. Из DataTable грузит на сервер всё что нужно. DataTable можно формировать перед загрузкой на основе входных данных. Вот пример как у меня сделано
C#
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
public async Task<List<DrawSpecification>> UploadSpecficationToDBAsync(int systemID, List<ExcelSpecification> specification)
{
    // Преобразуем коллекцию данных в DataTable
    DataTable dataTable = specification.ToDataTable();
    List<DrawSpecification> drawSpecification = new List<DrawSpecification>();
    // Запрос результата после загрузки
    string sqlText =
        """
        SELECT
            ID_DrawSpec, ID_DrawSys, SpecPos, KKS, Oboznachenie, Naimenovanie, Kolvo, KolvoIzg,
            EdIzm, Material, VesEd, ID_NaryadZakaz, ID_NaryadZakazIzg, ID_CPSDetail
        FROM
            dbo.tblDrawSpec
        WHERE
            ID_DrawSys = @idDrawSys
        ORDER BY
            SpecPos
        """;
 
    try
    {
        using (SqlConnection sqlConnection = new SqlConnection(_connectionString))
        {
            // Открываем соединение
            await sqlConnection.OpenAsync();
 
            // Запускаем транзакцию
            using (SqlTransaction sqlTransaction = sqlConnection.BeginTransaction())
            {
                // Настраиваем загрузку
                using (SqlBulkCopy sqlBulkCopy = new SqlBulkCopy(sqlConnection,
                       SqlBulkCopyOptions.KeepNulls | SqlBulkCopyOptions.FireTriggers | SqlBulkCopyOptions.CheckConstraints,
                       sqlTransaction))
                {
                    // Делаем маппинг на существующую таблицу
                    sqlBulkCopy.DestinationTableName = "dbo.tblDrawSpec";
 
                    sqlBulkCopy.ColumnMappings.Add("DrawSystemID", "ID_DrawSys");
                    sqlBulkCopy.ColumnMappings.Add("DSPositionNum", "SpecPos");
                    sqlBulkCopy.ColumnMappings.Add("DSKKS", "KKS");
                    sqlBulkCopy.ColumnMappings.Add("DSDesignation", "Oboznachenie");
                    sqlBulkCopy.ColumnMappings.Add("DSName", "Naimenovanie");
                    sqlBulkCopy.ColumnMappings.Add("DSQuantity", "Kolvo");
                    sqlBulkCopy.ColumnMappings.Add("DSQuantityAssembly", "KolvoIzg");
                    sqlBulkCopy.ColumnMappings.Add("DSMeasure", "EdIzm");
                    sqlBulkCopy.ColumnMappings.Add("DSMaterial", "Material");
                    sqlBulkCopy.ColumnMappings.Add("DSUnitWeight", "VesEd");
 
                    using (SqlCommand sqlCommand = new SqlCommand(sqlText, sqlConnection, sqlTransaction))
                    {
                        // Параметр запроса
                        SqlParameter pSystemID = new SqlParameter
                        {
                            ParameterName = "@idDrawSys",
                            Value = systemID,
                            SqlDbType = SqlDbType.Int,
                            Direction = ParameterDirection.Input
                        };
                        sqlCommand.Parameters.Add(pSystemID);
 
                        try
                        {
                            // Заливаем данные на сервер
                            await sqlBulkCopy.WriteToServerAsync(dataTable);
                            // Читаем результат
                            using (SqlDataReader sqlDataReader = await sqlCommand.ExecuteReaderAsync())
                            {
                                while (await sqlDataReader.ReadAsync())
                                {
                                    drawSpecification.Add(new DrawSpecification
                                    {
                                        DrawSpecID = (int)sqlDataReader["ID_DrawSpec"],
                                        DrawSystemID = (int)sqlDataReader["ID_DrawSys"],
                                        DSPositionNum = (short)sqlDataReader["SpecPos"],
                                        DSKKS = sqlDataReader["KKS"]
                                            == DBNull.Value ? null : (string)sqlDataReader["KKS"],
                                        DSDesignation = sqlDataReader["Oboznachenie"]
                                            == DBNull.Value ? null : (string)sqlDataReader["Oboznachenie"],
                                        DSName = sqlDataReader["Naimenovanie"]
                                            == DBNull.Value ? null : (string)sqlDataReader["Naimenovanie"],
                                        DSQuantity = sqlDataReader["Kolvo"]
                                            == DBNull.Value ? null : (double?)sqlDataReader["Kolvo"],
                                        DSQuantityAssembly = sqlDataReader["KolvoIzg"]
                                            == DBNull.Value ? null : (double?)sqlDataReader["KolvoIzg"],
                                        DSMeasure = sqlDataReader["EdIzm"]
                                            == DBNull.Value ? null : (string)sqlDataReader["EdIzm"],
                                        DSMaterial = sqlDataReader["Material"]
                                            == DBNull.Value ? null : (string)sqlDataReader["Material"],
                                        DSUnitWeight = sqlDataReader["VesEd"]
                                            == DBNull.Value ? null : (double?)sqlDataReader["VesEd"],
                                        DSWorkOrder = sqlDataReader["ID_NaryadZakaz"]
                                            == DBNull.Value ? null : (int?)sqlDataReader["ID_NaryadZakaz"],
                                        DSWorkOrderAssembly = sqlDataReader["ID_NaryadZakazIzg"]
                                            == DBNull.Value ? null : (int?)sqlDataReader["ID_NaryadZakazIzg"],
                                        CPSDetailID = sqlDataReader["ID_CPSDetail"]
                                            == DBNull.Value ? null : (int?)sqlDataReader["ID_CPSDetail"]
                                    });
                                }
                            }
                            // Закрываем транзакцию
                            sqlTransaction.Commit();
                            return drawSpecification;
                        }
                        catch (SqlException sqlEx)
                        {
                            sqlTransaction.Rollback();
                            throw new ApplicationException(string.Format("T-SQL #{0} - {1}", sqlEx.Number, sqlEx.Message), sqlEx);
                        }
                        catch (Exception ex)
                        {
                            sqlTransaction.Rollback();
                            throw new ApplicationException(ex.Message, ex);
                        }
                    }
                }
            }
        }
    }
    catch (SqlException sqlEx)
    {
        throw new ApplicationException(string.Format("T-SQL #{0} - {1}", sqlEx.Number, sqlEx.Message), sqlEx);
    }
    catch (Exception ex)
    {
        throw new ApplicationException(ex.Message, ex);
    }
}
Метод расширения коллекции для преобразования в DataTable\
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
internal static DataTable ToDataTable<T>(this IList<T> data)
{
    PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(T));
    DataTable table = new DataTable();
 
    foreach (PropertyDescriptor prop in properties)
        table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
 
    foreach (T item in data)
    {
        DataRow row = table.NewRow();
 
        foreach (PropertyDescriptor prop in properties)
            row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
 
        table.Rows.Add(row);
    }
 
    return table;
}
1
3614 / 2135 / 756
Регистрация: 02.06.2013
Сообщений: 5,169
19.09.2025, 20:53
bcp умеет создавать fmt-файл
https://learn.microsoft.com/ru... bs=non-xml
1
1304 / 358 / 97
Регистрация: 14.10.2022
Сообщений: 1,087
20.09.2025, 23:22  [ТС]
Неа. В данном случае - не умеет.
По всей видимости, проблему создают те же временные таблицы внутри ХП.
Вот что получаю:

Code
1
2
3
4
5
6
7
C:\Windows\system32>bcp "exec [DB2].[report].[API_InformFirst] @department_id = N'30',@begin_date = N'20250101', @end_date = N'20250831'" queryout nul -S . -T -d DB2 -C 1251 -t\0\t -r\0\n -f "d:\out\API_InformFirst.fmt"
 
Starting copy...
SQLState = S1000, NativeError = 0
Error = [Microsoft][ODBC Driver 17 for SQL Server]Format file could not be opened. Invalid name specified or access denied.
SQLState = S1000, NativeError = 0
Error = [Microsoft][ODBC Driver 17 for SQL Server]Unable to resolve column level collations
Собственно, иначе б не спрашивал.
0
3614 / 2135 / 756
Регистрация: 02.06.2013
Сообщений: 5,169
21.09.2025, 08:32
Цитата Сообщение от uaggster Посмотреть сообщение
В данном случае - не умеет.
Ну тогда радикальный способ - сделать клон ХП с заменой временных таблиц на табличные переменные. Regex.Replcace вполне справится. И клон использовать для формирования файла формата
0
1304 / 358 / 97
Регистрация: 14.10.2022
Сообщений: 1,087
21.09.2025, 09:17  [ТС]
Ненене!
Во первых, это, что называется, "не в моей власти".
Во вторых, это убьет производительность напрочь. Там во времяшки иногда миллионы записей сохраняются, сначала по select into, а потом по ним индексы строятся.

Или ты имеешь ввиду, временно создать клон ХП, запустить bcp на создание fmt, а потом прибить?
Ну... это очень вычурно...
Надо подумать как :-)))
0
3614 / 2135 / 756
Регистрация: 02.06.2013
Сообщений: 5,169
21.09.2025, 11:45
Цитата Сообщение от uaggster Посмотреть сообщение
Или ты имеешь ввиду, временно создать клон ХП, запустить bcp на создание fmt, а потом прибить?
Именно так.
И не более вычурно чем
Цитата Сообщение от uaggster Посмотреть сообщение
Пока попросил Дипсик написать мне на пауэршелл скрипт, который вызывает ХП по списку, подключившись через клиента, а потом описывает NET-овский resultset.
Типы там, получаются NET-овские, но хотя бы так.
0
1304 / 358 / 97
Регистрация: 14.10.2022
Сообщений: 1,087
23.09.2025, 09:02  [ТС]
invm, неа. не выйдет так.
Там постоянно select into используется.
а into в переменную не сделать. Нужно ее сначала создать, с необходимым набором полей.
... и мы опять возвращаемся к получению набора полей.
0
3614 / 2135 / 756
Регистрация: 02.06.2013
Сообщений: 5,169
23.09.2025, 12:08
Цитата Сообщение от uaggster Посмотреть сообщение
Там постоянно select into используется.
Ну можно на постоянную таблицу подменять. С уникальным именем
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
23.09.2025, 12:08
Помогаю со студенческими работами здесь

Создание хранимой процедуры, которая создает хранимые процедуры
Написать хранимую процедуру SQL , которая будет создавать хранимые процедуры(любые) для БД.

Создание и вызов хранимой процедуры
Привет всем. ест таблица SALES, Мне надо создать процедур и сохранит их в базе при SELECT, INSERT...

Как сделать меню вызова различных хранимых процедур?
Уже 3-ю неделю мучаюсь не могу вызвать хранимую процедуру из хранимой процедуры. Как возможно...

Вызов хранимой процедуры
Народ. У меня возникла проблема, может кто порекомендует что-нибудь. Мне надо вызывать одну и...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
14
Ответ Создать тему
Новые блоги и статьи
Установка Emscripten SDK (emsdk) и CMake на Windows для сборки C и C++ приложений в WebAssembly (Wasm)
8Observer8 30.01.2026
Чтобы скачать Emscripten SDK (emsdk) необходимо сначало скачать и уставить Git: Install for Windows. Следуйте стандартной процедуре установки Git через установщик. Система контроля версиями Git. . .
Подключение Box2D v3 к SDL3 для Android: физика и отрисовка коллайдеров
8Observer8 29.01.2026
Содержание блога Box2D - это библиотека для 2D физики для анимаций и игр. С её помощью можно определять были ли коллизии между конкретными объектами. Версия v3 была полностью переписана на Си, в. . .
Инструменты COM: Сохранение данный из VARIANT в файл и загрузка из файла в VARIANT
bedvit 28.01.2026
Сохранение базовых типов COM и массивов (одномерных или двухмерных) любой вложенности (деревья) в файл, с возможностью выбора алгоритмов сжатия и шифрования. Часть библиотеки BedvitCOM Использованы. . .
Загрузка PNG с альфа-каналом на SDL3 для Android: с помощью SDL_LoadPNG (без SDL3_image)
8Observer8 28.01.2026
Содержание блога SDL3 имеет собственные средства для загрузки и отображения PNG-файлов с альфа-каналом и базовой работы с ними. В этой инструкции используется функция SDL_LoadPNG(), которая. . .
Загрузка PNG с альфа-каналом на SDL3 для Android: с помощью SDL3_image
8Observer8 27.01.2026
Содержание блога SDL3_image - это библиотека для загрузки и работы с изображениями. Эта пошаговая инструкция покажет, как загрузить и вывести на экран смартфона картинку с альфа-каналом, то есть с. . .
Влияние грибов на сукцессию
anaschu 26.01.2026
Бифуркационные изменения массы гриба происходят тогда, когда мы уменьшаем массу компоста в 10 раз, а скорость прироста биомассы уменьшаем в три раза. Скорость прироста биомассы может уменьшаться за. . .
Воспроизведение звукового файла с помощью SDL3_mixer при касании экрана Android
8Observer8 26.01.2026
Содержание блога SDL3_mixer - это библиотека я для воспроизведения аудио. В отличие от инструкции по добавлению текста код по проигрыванию звука уже содержится в шаблоне примера. Нужно только. . .
Установка Android SDK, NDK, JDK, CMake и т.д.
8Observer8 25.01.2026
Содержание блога Перейдите по ссылке: https:/ / developer. android. com/ studio и в самом низу страницы кликните по архиву "commandlinetools-win-xxxxxx_latest. zip" Извлеките архив и вы увидите. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru