La utilidad BCP (Programa de copia masiva) en SQL Server permite a los administradores de bases de datos importar datos a una tabla y exportar datos de una tabla a un archivo plano. La utilidad BCP también admite varias funciones que facilitan el proceso de exportación e importación de datos masivos.
Ahora comencemos con un escenario empresarial.
Escenario empresarial
Digamos que necesitamos compartir un informe mensual en el formato específico con un cliente en una ubicación compartida segura como SFTS, es decir, al comienzo de cada mes, debemos enviar el archivo al cliente del mes anterior. En este escenario, intentaremos crear el procedimiento almacenado para generar datos y exportar esos datos al archivo plano (.txt o .csv).
¿Cómo importar y exportar los datos SQL?
Hay varias formas de hacerlo:
- Usando SSMS, ejecute la consulta en la ventana Consulta y exporte o el asistente de importación y exportación de SQL Server.
- Uso de SSIS:creación de un paquete mediante SSDT.
- Uso de SSRS.
- Usando C#:cree una consola o gane la aplicación para exportar.
- Utilidad BCP.
- etc.
¿Qué es la utilidad BCP?
La utilidad BCP (programa de copia masiva) es una utilidad de línea de comandos para copiar datos entre una instancia de MS SQL Server y un archivo de datos en un formato especificado por el usuario. Podemos exportar e importar grandes cantidades de datos dentro y fuera de las bases de datos de SQL Server de forma rápida y sencilla.
La utilidad BCP realiza las siguientes tareas:
- Exportación masiva de datos desde una tabla de SQL Server a un archivo de datos.
- Exportación masiva de datos desde una consulta/procedimiento almacenado.
- Importación masiva de datos desde un archivo de datos a una tabla de SQL Server.
- Generación de los archivos de formato.
Puede encontrar más detalles sobre la utilidad BCP aquí.
Entorno utilizado
- Edición para desarrolladores de SQL Server 2017
- Servidor SQL 2017 Estudio de gestión
- Base de datos de ejemplo de Wide World Importers v1.0
- Utilidad BCP
Cómo exportar datos a un archivo plano
Cree un procedimiento almacenado para generar los datos del informe mensual.
Primero, cree los objetos dependientes para el procedimiento almacenado de exportación.
Así que tenemos que crear las siguientes tablas:
- La tabla Orders_Monthly_Temp_Table tabla:esta tabla temporal se usa para almacenar los datos de los pedidos mensuales en un formato específico para exportarlo a un archivo de texto, es decir, en nuestro caso, concatenar todas las columnas en una fila con el delimitador "|".
- La Export_Config tabla:esta tabla se utiliza para almacenar configuraciones de exportación, es decir, ruta de carpeta compartida, tipo de archivo sin formato, delimitador.
Cree un script para Orders_Monthly_Temp_Table

CREATE TABLE [dbo].[Orders_Monthly_Temp_Table](
[Row] [varchar](200) NOT NULL
) ON [PRIMARY] Cree un script para Export_Config

CREATE TABLE [dbo].[Export_Config](
[Exp_Id] [int] IDENTITY(1,1) NOT NULL,
[ShareFolder] [varchar](200) NOT NULL,
[FileType] [varchar](5) NOT NULL,
[Delimiter] [char](1) NOT NULL,
CONSTRAINT [PK_Export_Config] PRIMARY KEY CLUSTERED
(
[Exp_Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [USERDATA]
) ON [USERDATA]
GO Insertar datos en Export_Config

SET IDENTITY_INSERT [dbo].[Export_Config] ON GO INSERT [dbo].[Export_Config] ([Exp_Id], [ShareFolder], [FileType], [Delimiter]) VALUES (1, N'\\AASHREEPC\FileServer\OrdersMonthly', N'.txt', N'|') GO SET IDENTITY_INSERT [dbo].[Export_Config] OFF GO
Creación de procedimientos almacenados y parámetros
- Aquí los parámetros de año y mes son opcionales.
- Si no se especifica un mes, se toma el mes anterior y si el mes es 12, tenemos que tomar el año anterior, porque si estamos generando el informe en enero de 2019 para diciembre de 2018.
- Si no se especifica un año, se toma el año actual y la ruta de la carpeta es obligatoria.
CREATE PROCEDURE [dbo].[Orders_Monthly_Report]
@Month INT = NULL
,@Year INT = NULL
,@FolderPath VARCHAR(200)
AS
BEGIN
SET NOCOUNT ON;
BEGIN TRY Validación de parámetros
--#region Parametes validation
IF NULLIF(@Month, '') IS NULL
BEGIN
SELECT @Month = DATEPART(mm, DATEADD(month, - 1, GETDATE()))
IF (@Month = 12) –
BEGIN
SELECT @Year = DATEPART(Year, GETDATE()) - 1
END
END
IF NULLIF(@Year, '') IS NULL
BEGIN
SELECT @Year = DATEPART(Year, GETDATE())
END
IF NULLIF(@FolderPath, '') IS NULL
BEGIN
--SELECT @FolderPath = '\\AASHREEPC\FileServer'
SELECT 'ERROR FolderPath must be specified.'
RETURN;
END
--#endregion Parameters validation Obtener la configuración de la tabla de exportación
DECLARE @ExportPath VARCHAR(200)
,@Delimiter CHAR(1)
,@FileType VARCHAR(5)
SELECT @ExportPath = TRIM(ShareFolder)
,@FileType = TRIM(FileType)
,@Delimiter = TRIM(Delimiter)
FROM dbo.Export_Config Obtener la fecha de inicio y la fecha de finalización del mes
DECLARE @MonthStartDate DATETIME = DATEADD(month, @Month - 1, DATEADD(year, @Year - 1900, 0))
,@MonthEndDate DATETIME = DATEADD(day, - 1, DATEADD(month, @Month, DATEADD(year, @Year - 1900, 0)))
Check and Create the temporary table for report data/result
IF NOT EXISTS (
SELECT *
FROM sys.objects
WHERE object_id = OBJECT_ID(N'[dbo].[Orders_Monthly_Temp_Table]')
AND type IN (N'U')
)
BEGIN
CREATE TABLE [dbo].Orders_Monthly_Temp_Table ([Row] [varchar](200) NOT NULL) ON [PRIMARY]
END Inserte los datos en la tabla temporal en un formato específico, es decir, en este caso "| – símbolo de tubo separado”
TRUNCATE TABLE Orders_Monthly_Temp_Table
INSERT INTO Orders_Monthly_Temp_Table
SELECT CAST([OrderID] AS VARCHAR(10)) + ' | ' + CAST(c.[CustomerName] AS VARCHAR(50)) + ' | ' + CAST(p.[FullName] AS VARCHAR(50)) + ' | ' + ISNULL(CAST([PickedByPersonID] AS VARCHAR(4)), '') + ' | ' + CAST(p.[FullName] AS VARCHAR(20)) + ' | ' + ISNULL(CAST([BackorderOrderID] AS VARCHAR(4)), '') + ' | ' + CAST([OrderDate] AS VARCHAR(20)) + ' | ' + CAST([ExpectedDeliveryDate] AS VARCHAR(20)) + ' | ' + CAST([CustomerPurchaseOrderNumber] AS VARCHAR(10)) + ' | ' + CAST([IsUndersupplyBackordered] AS VARCHAR(4)) + ' | ' + ISNULL(CAST([Comments] AS VARCHAR(50)), '') + ' | ' + ISNULL(CAST([DeliveryInstructions] AS VARCHAR(50)), '') + ' | ' + ISNULL(CAST([InternalComments] AS VARCHAR(50)), '') + ' | ' + CAST([PickingCompletedWhen] AS VARCHAR(20)) + ' | ' + CAST(o.[LastEditedBy] AS VARCHAR(4)) + ' | ' + CAST([LastEditedWhen] AS VARCHAR(20)) AS Row
FROM [WideWorldImporters].[Sales].[Orders] o
INNER JOIN [Sales].[Customers] c ON o.[CustomerID] = c.[CustomerID]
INNER JOIN [Application].[People] p ON o.[SalespersonPersonID] = p.[PersonID]
WHERE OrderDate BETWEEN @MonthStartDate
AND @MonthEndDate Código para exportar los datos a un archivo plano
Cree la carpeta si no existe Usando SQL xp_create_subdir
DECLARE @sql VARCHAR(8000)
,@FilePath VARCHAR(200)
,@Query VARCHAR(100)
DECLARE @file_results TABLE (
file_exists INT
,file_is_a_directory INT
,parent_directory_exists INT
)
SET @FolderPath = @FolderPath + '\' + CAST(@Year AS VARCHAR(10)) + '\' + CAST(@Month AS VARCHAR(10)) + '\'
INSERT INTO @file_results
EXEC MASTER.dbo.xp_fileexist @FolderPath
IF NOT EXISTS (
SELECT 1
FROM @file_results
WHERE file_is_a_directory = 1
)
EXEC MASTER.dbo.xp_create_subdir @FolderPath Crear el archivo en la carpeta compartida
SET @FilePath = '"' + @FolderPath + '' + 'Orders_Monthly' + '_' + (
SELECT Format(GETDATE(), N'yyyyMMddHHmmss')
) + '.txt"'
SET @Query = '"SELECT * from ' + (
SELECT DB_NAME()
) + '.dbo.Orders_Monthly_Temp_Table"'
DECLARE @exe_path10 VARCHAR(200) = ' cd C:\Program Files\Microsoft SQL Server\Client SDK\ODBC\130 & '
SELECT @sql = @exe_path10 + ' bcp.exe ' + @Query + ' queryout ' + @FilePath + ' -T -c -q -t0x7c -r\n ' --+ @@servername
EXEC master..xp_cmdshell @sql
END TRY
BEGIN CATCH
SELECT ERROR_NUMBER() AS ErrorNumber
,ERROR_STATE() AS ErrorState
,ERROR_SEVERITY() AS ErrorSeverity
,ERROR_PROCEDURE() AS ErrorProcedure
,ERROR_LINE() AS ErrorLine
,ERROR_MESSAGE() AS ErrorMessage;
END CATCH
SET NOCOUNT OFF;
END
Cambie el contexto de su directorio a la carpeta donde se encuentra la utilidad BPC
[identificación de la tabla=58 /]
Ejecución del procedimiento
DECLARE @return_value int
EXEC @return_value = [dbo].[Exp_Orders_Monthly_Report]
@Month = NULL,
@Year = NULL,
@FolderPath = NULL
SELECT 'Return Value' = @return_value
GO Salida

Carpeta de destino

Archivo plano real (.txt/.cvs)

La carpeta compartida debe tener permisos para la cuenta virtual "SERVICIO NT\MSSQLSERVER"
Haga clic con el botón derecho en el archivo o la carpeta en la que desea establecer permisos → Haga clic en Propiedades → Haga clic en la pestaña Seguridad. → Haga clic en Editar → Haga clic en Agregar → Escriba NT SERVICE\MSSQLSERVER en el cuadro de nombre del objeto. (no haga clic en "Comprobar nombres"; si hace clic en Comprobar nombres, puede suceder que obtenga un error 'No se puede encontrar un objeto llamado "NT SERVICE\MSSQLSERVER".) → Haga clic en Aceptar → elija la cuenta MSSQLSERVER → Agregar permisos ( Control total) que se necesitan para la cuenta MSSQLSERVER:

Habilitar el servidor SQL 'xp_cmdshell'
EXEC sp_configure 'show advanced options', 1 GO RECONFIGURE GO EXEC sp_configure 'xp_cmdshell', 1 GO RECONFIGURE GO
Cómo importar datos desde un archivo plano
En este ejemplo, estamos utilizando Bulk Insert para importar datos del archivo. También podemos usar Openrowset, etc.
Cree un procedimiento almacenado para importar los datos desde un archivo plano en la carpeta compartida.
Primero, cree los objetos dependientes para el procedimiento almacenado de importación.
Así que tenemos que crear las siguientes tablas
- Los pedidos_mensuales tabla:esta tabla se utiliza para almacenar los datos de pedidos mensuales del archivo plano.
- La Configuración_Importación tabla: esta tabla se utiliza para almacenar configuraciones de importación, es decir, ruta de carpeta compartida, tipo de archivo plano, delimitador.

CREATE TABLE [dbo].[Orders_Monthly](
[OrderID] [int] NOT NULL,
[CustomerName] [varchar](50) NOT NULL,
[SalespersonPersonName] [varchar](50) NOT NULL,
[PickedByPersonName] [varchar](50) NULL,
[ContactPersonName] [varchar](50) NOT NULL,
[BackorderOrderID] [varchar](4) NULL,
[OrderDate] [date] NOT NULL,
[ExpectedDeliveryDate] [date] NOT NULL,
[CustomerPurchaseOrderNumber] [nvarchar](20) NULL,
[IsUndersupplyBackordered] [bit] NOT NULL,
[Comments] [nvarchar](max) NULL,
[DeliveryInstructions] [nvarchar](max) NULL,
[InternalComments] [nvarchar](max) NULL,
[PickingCompletedWhen] [datetime2](7) NULL,
[LastEditedBy] [int] NOT NULL,
[LastEditedWhen] [datetime2](7) NOT NULL,
CONSTRAINT [PK_Orders_Monthly] PRIMARY KEY CLUSTERED
(
[OrderID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [USERDATA]
) ON [USERDATA] TEXTIMAGE_ON [USERDATA]
GO

CREATE TABLE [dbo].[Import_Config](
[Exp_Id] [int] IDENTITY(1,1) NOT NULL,
[ShareFolder] [nchar](200) NOT NULL,
[FileType] [varchar](5) NOT NULL,
[Delimiter] [char](1) NOT NULL
) ON [USERDATA]
GO Insertar datos en Import_Config

SET IDENTITY_INSERT [dbo].[Import_Config] ON GO INSERT [dbo].[Import_Config] ([Exp_Id], [ShareFolder], [FileType], [Delimiter]) VALUES (1, N'\\AASHREEPC\FileServer\OrdersMonthly', N'.txt', N'|') GO SET IDENTITY_INSERT [dbo].[Import_Config] OFF GO
Creación de procedimientos almacenados y parámetros
Igual que en el procedimiento almacenado de exportación.
CREATE PROCEDURE [dbo].[Imp_Orders_Monthly_Report] @Month INT = NULL
,@Year INT = NULL
,@FolderPath VARCHAR(200) = NULL
AS
BEGIN
SET NOCOUNT ON;
BEGIN TRY
Get the configuration from the import table
DECLARE @ImportPath VARCHAR(200)
,@Delimiter CHAR(1)
,@FileType VARCHAR(5)
,@FilePath VARCHAR(200)
SELECT @ImportPath = TRIM(ShareFolder)
,@FileType = TRIM(FileType)
,@Delimiter = TRIM(Delimiter)
FROM dbo.Import_Config Validación de parámetros
Igual que en el procedimiento almacenado de exportación.
SET @FolderPath = @ImportPath + '\' + CAST(@Year AS VARCHAR(10)) + '\' + CAST(@Month AS VARCHAR(10)) + '\'
END
ELSE
BEGIN
--SELECT @FolderPath = '\\AASHREEPC\FileServer\OrdersMonthly'
SELECT 'ERROR FolderPath must be specified.'
RETURN;
END
END
--#endregion Parametes validation Comprueba si el archivo existe o no
CREATE TABLE #File (
FileName SYSNAME
,Depth TINYINT
,IsFile TINYINT
);
INSERT INTO #File (
FileName
,Depth
,IsFile
)
EXEC xp_DirTree @FolderPath
,1
,1
SELECT TOP 1 @FilePath = @FolderPath + '\' + FileName
FROM #File
ORDER BY FileName DESC;
IF NULLIF((SELECT TOP 1 FileName FROM #File ORDER BY FileName DESC), '') IS NULL
BEGIN
SELECT 'ERROR import File does not exists'
RETURN;
END
DROP TABLE #File
Import the data from the shared folder using Bulk Insert
DECLARE @SQL_BULK VARCHAR(MAX)
DecLare @Errorlog varchar (Max) = @FolderPath + '\Error.log'
SET @SQL_BULK = 'BULK
INSERT [Orders_Monthly]
FROM ''' + @FilePath + '''
WITH
(
DATAFILETYPE = ''char''
,BATCHSIZE = 50000
,CODEPAGE = ''RAW''
,FIRSTROW = 1
,FIELDTERMINATOR = '''example@sqldat.com+'''
,ROWTERMINATOR = ''\n''
,KEEPNULLS
,ERRORFILE = '''+ @Errorlog + '''
,MAXERRORS = 20000
,TABLOCK
)'
EXEC (@SQL_BULK)
END TRY
BEGIN CATCH
SELECT ERROR_NUMBER() AS ErrorNumber
,ERROR_STATE() AS ErrorState
,ERROR_SEVERITY() AS ErrorSeverity
,ERROR_PROCEDURE() AS ErrorProcedure
,ERROR_LINE() AS ErrorLine
,ERROR_MESSAGE() AS ErrorMessage;
END CATCH
SET NOCOUNT OFF;
END Ejecución del procedimiento

DECLARE @return_value int
EXEC @return_value = [dbo].[Imp_Orders_Monthly_Report]
@Month = NULL,
@Year = NULL,
@FolderPath = NULL
SELECT 'Return Value' = @return_value
GO Salida

Verificación


Automatización del proceso:
Para ejecutar el proceso de exportación e importación automáticamente en un tiempo programado. digamos que necesitamos ejecutar la exportación el primer día del mes a las 12:00 am del mes para el informe del último mes y ejecutar la importación más tarde. Entonces necesitamos crear el trabajo SQL para eso.
Pasos para crear el trabajo SQL para exportar e importar.
- Abrir MS SQL Server Management Studio →
- y debe tener el "Agente SQL Server" →
- Expanda el "Agente SQL Server" en el Explorador de objetos. →
- Haga clic derecho en TRABAJO y seleccione "Nuevo trabajo..." →
- Puede ver la ventana "Nuevo trabajo" e ingresar el nombre ="Pedidos_Exportación_mensual" &Descripción

Luego vaya a la pestaña Pasos → Haga clic en el botón Nuevo en la parte inferior → se abre una nueva ventana de Pasos de trabajo → Ingrese el Nombre ="ejecutar [Exp_Orders_Monthly_Report] SP" y Tipo ="Transact-SQL Script (T-SQL)" → Pegue el siguiente script en el área de texto Comando y haga clic en Aceptar.
USE [WideWorldImporters]
GO
DECLARE @return_value int+
EXEC @return_value = [dbo].[Exp_Orders_Monthly_Report]
@Month = NULL,
@Year = NULL,
@FolderPath = NULL
SELECT 'Return Value' = @return_value
GO

Luego, vaya a la pestaña Programar → Haga clic en el botón Nuevo en la parte inferior → se abre una nueva ventana Programar trabajo. Ingrese el Nombre ="Programación mensual del pedido" e ingrese los siguientes detalles y haga clic en Aceptar → De nuevo, haga clic en Aceptar en la ventana Nuevo trabajo.
El trabajo se crearía con éxito.


Probar el trabajo de SQL:
Elimine todos los archivos de la carpeta compartida para realizar pruebas.

Para ejecutar el trabajo manualmente para la prueba:haga clic con el botón derecho en el trabajo recién creado → haga clic en "Iniciar trabajo en el paso..." y podemos ver el trabajo en ejecución


Podemos ver que el archivo se crea en la carpeta compartida.

Nota:siga los pasos anteriores para crear el trabajo SQL (Orders_Monthly_Import) para Importar también.
Espero que ahora tenga una mejor comprensión de cómo usar la utilidad BCP.
Herramienta útil:
dbForge Data Pump:un complemento de SSMS para llenar bases de datos SQL con datos de fuentes externas y migrar datos entre sistemas.